297 lines
8.8 KiB
Go
297 lines
8.8 KiB
Go
package customtemplates
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
portainer "github.com/portainer/portainer/api"
|
|
"github.com/portainer/portainer/api/dataservices"
|
|
"github.com/portainer/portainer/api/http/security"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCustomTemplateDelete_NotFound(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, _, _ := newTestHandler(t)
|
|
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/99", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "99"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.NotNil(t, herr)
|
|
require.Equal(t, http.StatusNotFound, herr.StatusCode)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_Forbidden(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
err := tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// User 2 did not create this template and is not an admin
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 2}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.NotNil(t, herr)
|
|
require.Equal(t, http.StatusForbidden, herr.StatusCode)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_CreatorDeniedWhenAdminOnly(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
err := tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 2,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = tx.ResourceControl().Create(&portainer.ResourceControl{
|
|
ID: 1,
|
|
ResourceID: "1",
|
|
Type: portainer.CustomTemplateResourceControl,
|
|
AdministratorsOnly: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// User 2 created the template but an admin later changed it to admins-only
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 2}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.NotNil(t, herr)
|
|
require.Equal(t, http.StatusForbidden, herr.StatusCode)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_CreatorDeniedWithoutResourceControl(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
return tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 2,
|
|
})
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// User 2 created this template but there is no resource control
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 2}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.NotNil(t, herr)
|
|
require.Equal(t, http.StatusForbidden, herr.StatusCode)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_Success(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
err := tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 2,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = tx.ResourceControl().Create(&portainer.ResourceControl{
|
|
ID: 1,
|
|
ResourceID: "1",
|
|
Type: portainer.CustomTemplateResourceControl,
|
|
UserAccesses: []portainer.UserResourceAccess{{UserID: 2}},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 2}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.Nil(t, herr)
|
|
require.Equal(t, http.StatusNoContent, rr.Code)
|
|
|
|
err = store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
_, err := tx.CustomTemplate().Read(1)
|
|
require.True(t, tx.IsErrObjectNotFound(err))
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_AdminCanDeleteAdminOnly(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
err := tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 2,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = tx.ResourceControl().Create(&portainer.ResourceControl{
|
|
ID: 1,
|
|
ResourceID: "1",
|
|
Type: portainer.CustomTemplateResourceControl,
|
|
AdministratorsOnly: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.Nil(t, herr)
|
|
require.Equal(t, http.StatusNoContent, rr.Code)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_PublicTemplateAllowsAnyUser(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
err := tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = tx.ResourceControl().Create(&portainer.ResourceControl{
|
|
ID: 1,
|
|
ResourceID: "1",
|
|
Type: portainer.CustomTemplateResourceControl,
|
|
Public: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// User 2 is not the creator but the template is public
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 2}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.Nil(t, herr)
|
|
require.Equal(t, http.StatusNoContent, rr.Code)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_NonCreatorForbiddenWithPrivateRC(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
err := tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = tx.ResourceControl().Create(&portainer.ResourceControl{
|
|
ID: 1,
|
|
ResourceID: "1",
|
|
Type: portainer.CustomTemplateResourceControl,
|
|
UserAccesses: []portainer.UserResourceAccess{{UserID: 1}},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// User 2 is not the creator and the template has a private resource control
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 2}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.NotNil(t, herr)
|
|
require.Equal(t, http.StatusForbidden, herr.StatusCode)
|
|
}
|
|
|
|
func TestCustomTemplateDelete_CreatorDeniedWithoutAccess(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler, store, _ := newTestHandler(t)
|
|
|
|
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
err := tx.CustomTemplate().Create(&portainer.CustomTemplate{
|
|
ID: 1,
|
|
CreatedByUserID: 2,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// RC exists but only grants access to user 3, not the creator (user 2)
|
|
err = tx.ResourceControl().Create(&portainer.ResourceControl{
|
|
ID: 1,
|
|
ResourceID: "1",
|
|
Type: portainer.CustomTemplateResourceControl,
|
|
UserAccesses: []portainer.UserResourceAccess{{UserID: 3}},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
r := httptest.NewRequest(http.MethodDelete, "/custom_templates/1", nil)
|
|
r = mux.SetURLVars(r, map[string]string{"id": "1"})
|
|
r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 2}))
|
|
rr := httptest.NewRecorder()
|
|
|
|
herr := handler.customTemplateDelete(rr, r)
|
|
require.NotNil(t, herr)
|
|
require.Equal(t, http.StatusForbidden, herr.StatusCode)
|
|
}
|