snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
67
server/tests/system/actionlog_test.go
Normal file
67
server/tests/system/actionlog_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearActionLog() {
|
||||
h.noError(store.TruncateActionlogs(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) repoMakeActionLog() *actionlog.Action {
|
||||
var res = &actionlog.Action{
|
||||
ID: id.Next(),
|
||||
Timestamp: time.Now(),
|
||||
ActorID: id.Next(),
|
||||
Resource: types.ComponentRbacResource(),
|
||||
Action: "lookup",
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateActionlog(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func TestActionLogList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearActionLog()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "action-log.read")
|
||||
|
||||
h.repoMakeActionLog()
|
||||
|
||||
h.apiInit().
|
||||
Get("/actionlog/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestActionLogListForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearActionLog()
|
||||
|
||||
h.repoMakeActionLog()
|
||||
|
||||
h.apiInit().
|
||||
Get("/actionlog/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("access_denied")).
|
||||
End()
|
||||
}
|
||||
678
server/tests/system/apigw_test.go
Normal file
678
server/tests/system/apigw_test.go
Normal file
@@ -0,0 +1,678 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) createRouteWithFilter(s string, fkind string) (*types.ApigwRoute, *types.ApigwFilter) {
|
||||
return h.createRouteAndFilterWithEnabled(s, fkind, true, true)
|
||||
}
|
||||
|
||||
func (h helper) createRouteWithFilterEnabled(s string, fkind string, enable bool) (*types.ApigwRoute, *types.ApigwFilter) {
|
||||
return h.createRouteAndFilterWithEnabled(s, fkind, true, enable)
|
||||
}
|
||||
|
||||
func (h helper) createRouteAndFilterWithEnabled(s string, fkind string, rEnable, fEnable bool) (*types.ApigwRoute, *types.ApigwFilter) {
|
||||
r := h.createRoute(&types.ApigwRoute{Endpoint: "/" + s, Method: "GET", Enabled: rEnable})
|
||||
f := h.createFilters(&types.ApigwFilter{Kind: fkind, Enabled: fEnable}, r.ID)
|
||||
|
||||
return r, f
|
||||
}
|
||||
|
||||
func (h helper) createRoute(route *types.ApigwRoute) *types.ApigwRoute {
|
||||
if route.ID == 0 {
|
||||
route.ID = id.Next()
|
||||
}
|
||||
|
||||
if route.CreatedAt.IsZero() {
|
||||
route.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
h.a.NoError(service.DefaultStore.CreateApigwRoute(context.Background(), route))
|
||||
return route
|
||||
}
|
||||
|
||||
func (h helper) createFilters(f *types.ApigwFilter, routeID uint64) *types.ApigwFilter {
|
||||
f.Route = routeID
|
||||
|
||||
if f.ID == 0 {
|
||||
f.ID = id.Next()
|
||||
}
|
||||
|
||||
if f.CreatedAt.IsZero() {
|
||||
f.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
h.a.NoError(service.DefaultStore.CreateApigwFilter(context.Background(), f))
|
||||
return f
|
||||
}
|
||||
|
||||
func (h helper) clearRoutes() {
|
||||
h.noError(store.TruncateApigwFilters(context.Background(), service.DefaultStore))
|
||||
h.noError(store.TruncateApigwRoutes(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func TestApigwRouteRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/route/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.endpoint`, "/test")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteRead_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/route/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteSearch(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
h.createRouteWithFilter("test1", "")
|
||||
h.createRouteWithFilter("test2", "")
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "apigw-routes.search")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/route/")).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 2)).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].endpoint`, "/test1")).
|
||||
Assert(jsonpath.Equal(`$.response.set[1].endpoint`, "/test2")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteSearch_includeDisabled(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
h.createRouteWithFilter("test1", "")
|
||||
h.createRouteWithFilter("test2", "")
|
||||
h.createRouteAndFilterWithEnabled("test3", "", false, true)
|
||||
h.createRouteAndFilterWithEnabled("test4", "", false, false)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "apigw-routes.search")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/route/")).
|
||||
Query("disabled", "1").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 4)).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].endpoint`, "/test1")).
|
||||
Assert(jsonpath.Equal(`$.response.set[1].endpoint`, "/test2")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteSearch_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
h.createRouteWithFilter("test1", "")
|
||||
h.createRouteWithFilter("test2", "")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/route/")).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToSearch")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteSearch_forbiddenSpecific(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test1", "")
|
||||
h.createRouteWithFilter("test2", "")
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "apigw-routes.search")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(0), "read")
|
||||
helpers.DenyMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/route/")).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 1)).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].endpoint`, "/test2")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "apigw-route.create")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/route")).
|
||||
Header("Accept", "application/json").
|
||||
FormData("endpoint", "/test").
|
||||
FormData("method", "GET").
|
||||
FormData("enabled", "false").
|
||||
FormData("group", "g1").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.routeID`)).
|
||||
Assert(jsonpath.Equal(`$.response.endpoint`, "/test")).
|
||||
Assert(jsonpath.Equal(`$.response.enabled`, false)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteCreate_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/route")).
|
||||
Header("Accept", "application/json").
|
||||
FormData("endpoint", "/test").
|
||||
FormData("method", "GET").
|
||||
FormData("enabled", "false").
|
||||
FormData("group", "g1").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToCreate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/apigw/route/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("endpoint", "/test-edited").
|
||||
FormData("enabled", "false").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.routeID`)).
|
||||
Assert(jsonpath.Equal(`$.response.endpoint`, "/test-edited")).
|
||||
Assert(jsonpath.Equal(`$.response.enabled`, false)).
|
||||
Assert(jsonpath.NotPresent(`$.response.deletedBy`)).
|
||||
Assert(jsonpath.NotPresent(`$.response.deletedAt`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteUpdateDeleted(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
now := time.Now()
|
||||
r := &types.ApigwRoute{Endpoint: "/deleted", Enabled: true, DeletedAt: &now, DeletedBy: 1}
|
||||
|
||||
h.createRoute(r)
|
||||
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/apigw/route/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("endpoint", "/deleted-and-updated").
|
||||
FormData("enabled", "false").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.routeID`)).
|
||||
Assert(jsonpath.Equal(`$.response.endpoint`, "/deleted-and-updated")).
|
||||
Assert(jsonpath.Present(`$.response.deletedBy`)).
|
||||
Assert(jsonpath.Present(`$.response.deletedAt`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteUpdate_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/apigw/route/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("endpoint", "/test-edited").
|
||||
FormData("enabled", "false").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/apigw/route/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteDelete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/apigw/route/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToDelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteUnDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/route/%d/undelete", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwRouteUnDelete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test", "")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/route/%d/undelete", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToUndelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
// Filters
|
||||
|
||||
func TestApigwFilterRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilter("test", "test")
|
||||
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.filterID`, strconv.FormatUint(f.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterRead_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
_, f := h.createRouteWithFilter("test", "test")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterSearch(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilter("test1", "")
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "apigw-routes.search")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/filter/")).
|
||||
Query("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 1)).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].filterID`, strconv.FormatUint(f.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterSearch_includeDisabled(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilterEnabled("test1", "", false)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "apigw-routes.search")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/filter/")).
|
||||
Query("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
Query("disabled", "1").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 1)).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].filterID`, strconv.FormatUint(f.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterSearch_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test1", "")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/apigw/filter/")).
|
||||
Query("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/apigw/filter")).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filterID`)).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterCreate_enabled(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/apigw/filter")).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
FormData("enabled", strconv.FormatBool(true)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filterID`)).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.enabled`, true)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterCreate_disabled(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/apigw/filter")).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
FormData("enabled", strconv.FormatBool(false)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filterID`)).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
Assert(jsonpath.NotPresent(`$.response.enabled`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterCreate_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, _ := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/apigw/filter")).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.filterID`, strconv.FormatUint(f.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterUpdate_enabled(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilterEnabled("test1", "", false)
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
FormData("enabled", strconv.FormatBool(true)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.filterID`, strconv.FormatUint(f.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.enabled`, true)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterUpdate_disabled(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilterEnabled("test1", "", true)
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "update")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
FormData("enabled", strconv.FormatBool(false)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.filterID`, strconv.FormatUint(f.ID, 10))).
|
||||
Assert(jsonpath.Equal(`$.response.routeID`, strconv.FormatUint(r.ID, 10))).
|
||||
Assert(jsonpath.NotPresent(`$.response.enabled`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterUpdate_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("routeID", strconv.FormatUint(r.ID, 10)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterDelete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
_, f := h.createRouteWithFilter("test1", "")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/apigw/filter/%d", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToDelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterUnDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/filter/%d/undelete", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApigwFilterUnDelete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoutes()
|
||||
|
||||
r, f := h.createRouteWithFilter("test1", "")
|
||||
helpers.AllowMe(h, types.ApigwRouteRbacResource(r.ID), "read")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/apigw/filter/%d/undelete", f.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("apigw-route.errors.notAllowedToUndelete")).
|
||||
End()
|
||||
}
|
||||
669
server/tests/system/application_test.go
Normal file
669
server/tests/system/application_test.go
Normal file
@@ -0,0 +1,669 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ft "github.com/cortezaproject/corteza/server/pkg/flag/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
labelTypes "github.com/cortezaproject/corteza/server/pkg/label/types"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (h helper) clearApplications() {
|
||||
h.noError(store.TruncateApplications(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) repoMakeApplication(ss ...string) *types.Application {
|
||||
var res = &types.Application{
|
||||
ID: id.Next(),
|
||||
CreatedAt: time.Now(),
|
||||
Unify: &types.ApplicationUnify{},
|
||||
}
|
||||
|
||||
if len(ss) > 0 {
|
||||
res.Name = ss[0]
|
||||
} else {
|
||||
res.Name = "n_" + rs()
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateApplication(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) repoFlagApplication(appID, owner uint64, flag string, active bool) *ft.Flag {
|
||||
res := &ft.Flag{
|
||||
Kind: "system:application",
|
||||
ResourceID: appID,
|
||||
OwnedBy: owner,
|
||||
Name: flag,
|
||||
Active: active,
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateFlag(context.Background(), service.DefaultStore, res))
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupApplicationByID(ID uint64) *types.Application {
|
||||
res, err := store.LookupApplicationByID(context.Background(), service.DefaultStore, ID)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) searchApplicationFlags(ID, owner uint64, flag string) ft.FlagSet {
|
||||
res, _, err := store.SearchFlags(context.Background(), service.DefaultStore, ft.FlagFilter{
|
||||
Kind: "system:application",
|
||||
ResourceID: []uint64{ID},
|
||||
OwnedBy: []uint64{owner},
|
||||
Name: []string{flag},
|
||||
})
|
||||
h.noError(err)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupApplicationByName(name string) *types.Application {
|
||||
res, _, err := store.SearchApplications(context.Background(), service.DefaultStore, types.ApplicationFilter{
|
||||
Name: name,
|
||||
})
|
||||
h.noError(err)
|
||||
if len(res) == 0 {
|
||||
return nil
|
||||
}
|
||||
return res[0]
|
||||
}
|
||||
|
||||
func TestApplicationRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
app := h.repoMakeApplication()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "applications.search")
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/application/%d", app.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.name`, app.Name)).
|
||||
Assert(jsonpath.Equal(`$.response.applicationID`, fmt.Sprintf("%d", app.ID))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "applications.search")
|
||||
|
||||
h.repoMakeApplication(h.randEmail())
|
||||
h.repoMakeApplication(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Get("/application/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationList_filterForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
// @todo this can be a problematic test because it leaves
|
||||
// behind applications that are not denied this context
|
||||
// db purge might be needed
|
||||
|
||||
h.repoMakeApplication("application")
|
||||
f := h.repoMakeApplication()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "applications.search")
|
||||
helpers.DenyMe(h, f.RbacResource(), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/application/").
|
||||
Query("name", f.Name).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.NotPresent(fmt.Sprintf(`$.response.set[? @.name=="%s"]`, f.Name))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationCreateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
h.apiInit().
|
||||
Post("/application/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("name", rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("application.errors.notAllowedToCreate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/application/").
|
||||
FormData("name", rs()).
|
||||
FormData("handle", "handle_"+rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationCreate_weight(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.create")
|
||||
name := "name_weight_create_" + rs()
|
||||
|
||||
h.apiInit().
|
||||
Post("/application/").
|
||||
FormData("name", name).
|
||||
FormData("weight", "10").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res := h.lookupApplicationByName(name)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(10, res.Weight)
|
||||
}
|
||||
|
||||
func TestApplicationUpdateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
u := h.repoMakeApplication()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/application/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("email", h.randEmail()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("application.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
res := h.repoMakeApplication()
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "update")
|
||||
|
||||
newName := "updated-" + rs()
|
||||
newHandle := "updated-" + rs()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/application/%d", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("name", newName).
|
||||
FormData("handle", newHandle).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupApplicationByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(newName, res.Name)
|
||||
}
|
||||
|
||||
func TestApplicationUpdate_weight(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
res := h.repoMakeApplication()
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "update")
|
||||
|
||||
newName := "updated-" + rs()
|
||||
newHandle := "updated-" + rs()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/application/%d", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("name", newName).
|
||||
FormData("handle", newHandle).
|
||||
FormData("weight", "20").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupApplicationByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(20, res.Weight)
|
||||
}
|
||||
|
||||
func TestApplicationReorder_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "update")
|
||||
a := h.repoMakeApplication()
|
||||
b := h.repoMakeApplication()
|
||||
c := h.repoMakeApplication()
|
||||
helpers.DenyMe(h, b.RbacResource(), "update")
|
||||
|
||||
h.apiInit().
|
||||
Post("/application/reorder").
|
||||
Header("Accept", "application/json").
|
||||
JSON(fmt.Sprintf(`{ "applicationIDs": ["%d", "%d", "%d"] }`, b.ID, a.ID, c.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("application.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationReorder(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "update")
|
||||
a := h.repoMakeApplication()
|
||||
b := h.repoMakeApplication()
|
||||
c := h.repoMakeApplication()
|
||||
|
||||
h.apiInit().
|
||||
Post("/application/reorder").
|
||||
Header("Accept", "application/json").
|
||||
JSON(fmt.Sprintf(`{ "applicationIDs": ["%d", "%d", "%d"] }`, b.ID, a.ID, c.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
check := func(app uint64, w int) {
|
||||
res := h.lookupApplicationByID(app)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(w, res.Weight)
|
||||
}
|
||||
|
||||
check(b.ID, 1)
|
||||
check(a.ID, 2)
|
||||
check(c.ID, 3)
|
||||
}
|
||||
|
||||
func TestApplicationDeleteForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
u := h.repoMakeApplication()
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/application/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("application.errors.notAllowedToDelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestApplicationDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "delete")
|
||||
|
||||
res := h.repoMakeApplication()
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/application/%d", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupApplicationByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.NotNil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestApplicationUndelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "delete")
|
||||
|
||||
res := h.repoMakeApplication()
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/application/%d/undelete", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupApplicationByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Nil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestApplicationLabels(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearApplications()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "applications.search")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.create")
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "read", "update", "delete")
|
||||
|
||||
var (
|
||||
ID uint64
|
||||
)
|
||||
|
||||
t.Run("create", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
payload = &types.Application{}
|
||||
)
|
||||
|
||||
helpers.SetLabelsViaAPI(h.apiInit(), t,
|
||||
"/application/",
|
||||
types.Application{Labels: map[string]labelTypes.LabelValue{"foo": {Val: "bar"}, "bar": {Val: "42"}}},
|
||||
payload,
|
||||
)
|
||||
req.NotZero(payload.ID)
|
||||
|
||||
h.a.Equal(payload.Labels["foo"].Val, "bar",
|
||||
"labels must contain foo with value bar")
|
||||
h.a.Equal(payload.Labels["bar"].Val, "42",
|
||||
"labels must contain bar with value 42")
|
||||
req.Equal(helpers.LabelsToStrings(payload.Labels), helpers.LabelsToStrings(helpers.LoadLabelsFromStore(t, service.DefaultStore, payload.LabelResourceKind(), payload.ID)),
|
||||
"response must match stored labels")
|
||||
|
||||
ID = payload.ID
|
||||
})
|
||||
|
||||
t.Run("update", func(t *testing.T) {
|
||||
if ID == 0 {
|
||||
t.Skip("label/create test not ran")
|
||||
}
|
||||
|
||||
var (
|
||||
req = require.New(t)
|
||||
payload = &types.Application{}
|
||||
)
|
||||
|
||||
helpers.SetLabelsViaAPI(h.apiInit(), t,
|
||||
fmt.Sprintf("PUT /application/%d", ID),
|
||||
&types.Application{Labels: map[string]labelTypes.LabelValue{"foo": {Val: "baz"}, "baz": {Val: "123"}}},
|
||||
payload,
|
||||
)
|
||||
req.NotZero(payload.ID)
|
||||
//req.Nil(payload.UpdatedAt, "updatedAt must not change after changing labels")
|
||||
|
||||
req.Equal(payload.Labels["foo"].Val, "baz",
|
||||
"labels must contain foo with value baz")
|
||||
req.NotContains(payload.Labels, "bar",
|
||||
"labels must not contain bar")
|
||||
req.Equal(payload.Labels["baz"].Val, "123",
|
||||
"labels must contain baz with value 123")
|
||||
req.Equal(helpers.LabelsToStrings(payload.Labels), helpers.LabelsToStrings(helpers.LoadLabelsFromStore(t, service.DefaultStore, payload.LabelResourceKind(), payload.ID)),
|
||||
"response must match stored labels")
|
||||
})
|
||||
|
||||
t.Run("search", func(t *testing.T) {
|
||||
if ID == 0 {
|
||||
t.Skip("label/create test not ran")
|
||||
}
|
||||
|
||||
var (
|
||||
req = require.New(t)
|
||||
set = types.ApplicationSet{}
|
||||
)
|
||||
|
||||
helpers.SearchWithLabelsViaAPI(h.apiInit(), t, "/application/", &set, url.Values{"labels": []string{"baz=123"}})
|
||||
req.NotEmpty(set)
|
||||
req.NotNil(set.FindByID(ID))
|
||||
req.NotNil(set.FindByID(ID).Labels)
|
||||
})
|
||||
}
|
||||
|
||||
func TestApplicationFlags(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearApplications()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "applications.search")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.create")
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "read")
|
||||
|
||||
t.Run("create", func(t *testing.T) {
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.flag.global")
|
||||
res := h.repoMakeApplication()
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, 0)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
ff := h.searchApplicationFlags(res.ID, 0, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
})
|
||||
|
||||
t.Run("create; not allowed", func(t *testing.T) {
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "application.flag.global")
|
||||
res := h.repoMakeApplication()
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, 0)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("application.errors.notAllowedToManageFlagGlobal")).
|
||||
End()
|
||||
|
||||
})
|
||||
|
||||
t.Run("create own", func(t *testing.T) {
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.flag.self")
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, "testFlag", true)
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, h.cUser.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
ff := h.searchApplicationFlags(res.ID, h.cUser.ID, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
})
|
||||
|
||||
t.Run("create own; not allowed", func(t *testing.T) {
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "application.flag.self")
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, "testFlag", true)
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, h.cUser.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("application.errors.notAllowedToManageFlag")).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("read application", func(t *testing.T) {
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, "testFlag", true)
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/application/%d", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.flags`)).
|
||||
Assert(jsonpath.Len(`$.response.flags`, 1)).
|
||||
Assert(jsonpath.Equal(`$.response.flags[0]`, "testFlag")).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("list applications", func(t *testing.T) {
|
||||
h.clearApplications()
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, "testFlag", true)
|
||||
|
||||
h.apiInit().
|
||||
Get("/application/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 1)).
|
||||
Assert(jsonpath.Len(`$.response.set[0].flags`, 1)).
|
||||
Assert(jsonpath.Equal(`$.response.set[0].flags[0]`, "testFlag")).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("read application; with own flag", func(t *testing.T) {
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, "testFlag", true)
|
||||
h.repoFlagApplication(res.ID, h.cUser.ID, "testFlagOwn", true)
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/application/%d", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.flags`)).
|
||||
Assert(jsonpath.Len(`$.response.flags`, 2)).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("read application; overwrite global", func(t *testing.T) {
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, "testFlag", true)
|
||||
h.repoFlagApplication(res.ID, h.cUser.ID, "testFlag", false)
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/application/%d", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.NotPresent(`$.response.flags`)).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("filter by flags", func(t *testing.T) {
|
||||
flag := rs()
|
||||
h.repoMakeApplication()
|
||||
h.repoMakeApplication()
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, flag, true)
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/application/")).
|
||||
QueryCollection(url.Values{"flags": []string{flag}}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("filter by flags; self inactive", func(t *testing.T) {
|
||||
flag := rs()
|
||||
h.repoMakeApplication()
|
||||
h.repoMakeApplication()
|
||||
res := h.repoMakeApplication()
|
||||
h.repoFlagApplication(res.ID, 0, flag, true)
|
||||
h.repoFlagApplication(res.ID, h.cUser.ID, flag, false)
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/application/")).
|
||||
QueryCollection(url.Values{"flags": []string{flag}}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 0)).
|
||||
End()
|
||||
})
|
||||
}
|
||||
|
||||
func TestApplicationFlags_Flow1(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearApplications()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.create")
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "read")
|
||||
|
||||
t.Run("create", func(t *testing.T) {
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "application.flag.global", "application.flag.self")
|
||||
res := h.repoMakeApplication()
|
||||
a := h.apiInit()
|
||||
|
||||
a.Post(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, h.cUser.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
a.Post(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, 0)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
a.Delete(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, h.cUser.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
ff := h.searchApplicationFlags(res.ID, 0, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
h.a.True(ff[0].Active)
|
||||
|
||||
ff = h.searchApplicationFlags(res.ID, h.cUser.ID, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
h.a.False(ff[0].Active)
|
||||
|
||||
a.Delete(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, h.cUser.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
ff = h.searchApplicationFlags(res.ID, 0, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
h.a.True(ff[0].Active)
|
||||
|
||||
ff = h.searchApplicationFlags(res.ID, h.cUser.ID, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
h.a.False(ff[0].Active)
|
||||
|
||||
a.Post(fmt.Sprintf("/application/%d/flag/%d/testFlag", res.ID, h.cUser.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
ff = h.searchApplicationFlags(res.ID, 0, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
h.a.True(ff[0].Active)
|
||||
|
||||
ff = h.searchApplicationFlags(res.ID, h.cUser.ID, "testFlag")
|
||||
h.a.NotNil(ff)
|
||||
h.a.Len(ff, 1)
|
||||
h.a.True(ff[0].Active)
|
||||
})
|
||||
}
|
||||
80
server/tests/system/attachment_test.go
Normal file
80
server/tests/system/attachment_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
)
|
||||
|
||||
func (h helper) clearAttachments() {
|
||||
h.noError(store.TruncateAttachments(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) repoMakeAttachment(ss ...string) *types.Attachment {
|
||||
var res = &types.Attachment{
|
||||
ID: id.Next(),
|
||||
CreatedAt: time.Now(),
|
||||
Kind: "json",
|
||||
}
|
||||
|
||||
if len(ss) > 0 {
|
||||
res.Name = ss[0]
|
||||
} else {
|
||||
res.Name = "n_" + rs()
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateAttachment(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupAttachmentByID(ID uint64) *types.Attachment {
|
||||
res, err := store.LookupAttachmentByID(context.Background(), service.DefaultStore, ID)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestAttachmentRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAttachments()
|
||||
|
||||
a := h.repoMakeAttachment()
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/attachment/%s/%d", a.Kind, a.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.name`, a.Name)).
|
||||
Assert(jsonpath.Equal(`$.response.attachmentID`, fmt.Sprintf("%d", a.ID))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestAttachmentDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAttachments()
|
||||
|
||||
a := h.repoMakeAttachment()
|
||||
helpers.AllowMe(h, types.ApplicationRbacResource(0), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/attachment/%s/%d", a.Kind, a.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
a = h.lookupAttachmentByID(a.ID)
|
||||
h.a.NotNil(a)
|
||||
h.a.NotNil(a.DeletedAt)
|
||||
}
|
||||
174
server/tests/system/auth_client_test.go
Normal file
174
server/tests/system/auth_client_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearAuthClients() {
|
||||
h.noError(store.TruncateAuthClients(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) repoMakeAuthClient(ss ...string) *types.AuthClient {
|
||||
res := &types.AuthClient{
|
||||
ID: id.Next(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if len(ss) > 0 {
|
||||
res.Handle = ss[0]
|
||||
} else {
|
||||
res.Handle = "n_" + rs()
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateAuthClient(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupAuthClientByID(id uint64) *types.AuthClient {
|
||||
res, err := store.LookupAuthClientByID(context.Background(), service.DefaultStore, id)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupAuthClientByHandle(handle string) *types.AuthClient {
|
||||
res, err := store.LookupAuthClientByHandle(context.Background(), service.DefaultStore, handle)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestAuthClientList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAuthClients()
|
||||
|
||||
h.repoMakeAuthClient()
|
||||
h.repoMakeAuthClient()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "auth-clients.search")
|
||||
helpers.AllowMe(h, types.AuthClientRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/auth/clients/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestAuthClientRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAuthClients()
|
||||
|
||||
client := h.repoMakeAuthClient()
|
||||
|
||||
helpers.AllowMe(h, types.AuthClientRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/auth/clients/%d", client.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestAuthClientCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAuthClients()
|
||||
|
||||
handle := rs()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "auth-clients.search")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "auth-client.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/auth/clients/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("meta", fmt.Sprintf(`{"name": "%s"}`, rs())).
|
||||
FormData("handle", handle).
|
||||
FormData("scope", "profile api").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res := h.lookupAuthClientByHandle(handle)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(handle, res.Handle)
|
||||
}
|
||||
|
||||
func TestAuthClientUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAuthClients()
|
||||
|
||||
client := h.repoMakeAuthClient()
|
||||
client.Meta = &types.AuthClientMeta{Name: rs()}
|
||||
client.Handle = rs()
|
||||
|
||||
helpers.AllowMe(h, types.AuthClientRbacResource(0), "update")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/auth/clients/%d", client.ID)).
|
||||
Header("Accept", "application/json").
|
||||
JSON(helpers.JSON(client)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res := h.lookupAuthClientByHandle(client.Handle)
|
||||
h.a.NotNil(res)
|
||||
}
|
||||
|
||||
func TestAuthClientDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAuthClients()
|
||||
|
||||
client := h.repoMakeAuthClient()
|
||||
|
||||
helpers.AllowMe(h, types.AuthClientRbacResource(0), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/auth/clients/%d", client.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res := h.lookupAuthClientByID(client.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.NotNil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestAuthClientUnDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearAuthClients()
|
||||
|
||||
client := h.repoMakeAuthClient()
|
||||
|
||||
helpers.AllowMe(h, types.AuthClientRbacResource(0), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/auth/clients/%d/undelete", client.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res := h.lookupAuthClientByID(client.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Nil(res.DeletedAt)
|
||||
}
|
||||
183
server/tests/system/auth_external_saml_success_test.go
Normal file
183
server/tests/system/auth_external_saml_success_test.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/auth/handlers"
|
||||
"github.com/cortezaproject/corteza/server/auth/saml"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
s "github.com/crewjam/saml"
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/steinfletcher/apitest"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func loadSAMLService() (srvc *saml.SamlSPService, err error) {
|
||||
var (
|
||||
links = handlers.GetLinks()
|
||||
keyPair tls.Certificate
|
||||
)
|
||||
|
||||
if keyPair, err = tls.X509KeyPair(readStaticFile("static/spCert.cert"), readStaticFile("static/spCert.key")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idpUrl, err := url.Parse("")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// idp metadata needs to be loaded before
|
||||
// the internal samlsp package
|
||||
md, err := samlsp.ParseMetadata(readStaticFile("static/idp_metadata.xml"))
|
||||
ru, err := url.Parse("http://localhost:8084")
|
||||
|
||||
rootURL := &url.URL{
|
||||
Scheme: ru.Scheme,
|
||||
User: ru.User,
|
||||
Host: ru.Host,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
srvc, err = saml.NewSamlSPService(zap.NewNop(), saml.SamlSPArgs{
|
||||
Enabled: true,
|
||||
|
||||
AcsURL: links.SamlCallback,
|
||||
MetaURL: links.SamlMetadata,
|
||||
SloURL: links.SamlLogout,
|
||||
|
||||
IdpURL: *idpUrl,
|
||||
Host: *rootURL,
|
||||
|
||||
Certificate: keyPair.Leaf,
|
||||
PrivateKey: keyPair.PrivateKey.(*rsa.PrivateKey),
|
||||
|
||||
IdpMeta: md,
|
||||
})
|
||||
|
||||
srvc.Handler().ServiceProvider.AllowIDPInitiated = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestAuthExternalSAMLSuccess(t *testing.T) {
|
||||
var (
|
||||
h = newHelper(t)
|
||||
|
||||
cookieSessionIDPtoSP = apitest.
|
||||
NewCookie("saml_tCu5PV6EgxcvUAa9e57uJ2g-bTkqnNkyyHHaOu15yEfZjgWKt02AtXGe").
|
||||
Value(strings.TrimSpace(string(readStaticFile("static/idp_to_sp.cookie"))))
|
||||
|
||||
cookieTokenIDPtoSPAfterLogin = apitest.
|
||||
NewCookie("token").
|
||||
Value(strings.TrimSpace(string(readStaticFile("static/idp_to_sp_token.cookie"))))
|
||||
|
||||
may21 = func() time.Time {
|
||||
tm, _ := time.Parse("2006-01-2 15:04:05", "2021-05-17 09:17:10")
|
||||
return tm
|
||||
}
|
||||
)
|
||||
|
||||
s.MaxClockSkew = time.Hour
|
||||
s.MaxIssueDelay = time.Hour
|
||||
|
||||
jwt.TimeFunc = may21
|
||||
s.TimeNow = may21
|
||||
|
||||
// first step, there is no session cookie, redirect to idp
|
||||
// in this case, host from parsed metadata
|
||||
t.Log("start login process")
|
||||
|
||||
h.apiInit().
|
||||
Get(handlers.GetLinks().SamlInit).
|
||||
Expect(t).
|
||||
Assert(func(res *http.Response, req *http.Request) error {
|
||||
loc, _ := res.Location()
|
||||
|
||||
h.assertBody("SSO Error: saml: session not present", res.Body)
|
||||
h.a.NotEmpty(loc.Query().Get("RelayState"))
|
||||
h.a.NotEmpty(loc.Query().Get("SAMLRequest"))
|
||||
h.a.Equal(http.StatusFound, res.StatusCode)
|
||||
return nil
|
||||
}).
|
||||
End()
|
||||
|
||||
cookies := []*apitest.Cookie{}
|
||||
|
||||
// coming back from idp, posting to sp-related endpoint
|
||||
// mocking session cookie and saml response
|
||||
// if everything is ok, redirect back to SAML init
|
||||
t.Log("post from idp to sp")
|
||||
|
||||
h.apiInit().
|
||||
Post(handlers.GetLinks().SamlCallback).
|
||||
Header("Content-Type", "application/x-www-form-urlencoded").
|
||||
FormData("RelayState", "tCu5PV6EgxcvUAa9e57uJ2g-bTkqnNkyyHHaOu15yEfZjgWKt02AtXGe").
|
||||
Cookies(cookieSessionIDPtoSP).
|
||||
FormData("SAMLResponse", string(readStaticFile("static/idp_to_sp.post"))).
|
||||
Expect(t).
|
||||
Assert(func(res *http.Response, req *http.Request) error {
|
||||
loc, _ := res.Location()
|
||||
|
||||
h.a.NotNil(getSessionCookie("token", res.Cookies()...))
|
||||
h.a.Equal(http.StatusFound, res.StatusCode)
|
||||
h.a.Equal(handlers.GetLinks().SamlInit, loc.String())
|
||||
|
||||
cookies = append(cookies, cookieSessionIDPtoSP)
|
||||
return nil
|
||||
}).
|
||||
End()
|
||||
|
||||
// idp sends a token session cookie also
|
||||
cookies = append(cookies, cookieTokenIDPtoSPAfterLogin)
|
||||
|
||||
// once everything is set and the external authentication via
|
||||
// internal Corteza services is done, redirect to default path (profile)
|
||||
t.Log("redirect to profile after session is created")
|
||||
|
||||
h.apiInit().
|
||||
Get(handlers.GetLinks().SamlInit).
|
||||
Cookies(cookies...).
|
||||
Expect(t).
|
||||
Assert(func(res *http.Response, req *http.Request) error {
|
||||
loc, _ := res.Location()
|
||||
|
||||
ss, _, err := service.DefaultStore.SearchAuthSessions(context.Background(), types.AuthSessionFilter{})
|
||||
h.a.NoError(err)
|
||||
h.a.Len(ss, 1)
|
||||
|
||||
h.a.NotNil(getSessionCookie("session", res.Cookies()...))
|
||||
h.a.Equal(http.StatusSeeOther, res.StatusCode)
|
||||
h.a.Equal(handlers.GetLinks().Profile, loc.String())
|
||||
return nil
|
||||
}).
|
||||
End()
|
||||
}
|
||||
|
||||
func getSessionCookie(name string, cc ...*http.Cookie) (found *http.Cookie) {
|
||||
for _, c := range cc {
|
||||
if c.Name == name {
|
||||
found = c
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
56
server/tests/system/auth_test.go
Normal file
56
server/tests/system/auth_test.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestAuthImpersonate(t *testing.T) {
|
||||
|
||||
var (
|
||||
h = newHelper(t)
|
||||
ctx = context.Background()
|
||||
user = h.createUserWithEmail(h.randEmail())
|
||||
input = &struct {
|
||||
UserID uint64 `json:",string"`
|
||||
}{
|
||||
UserID: user.ID,
|
||||
}
|
||||
)
|
||||
|
||||
helpers.AllowMe(h, types.UserRbacResource(user.ID), "impersonate")
|
||||
|
||||
var res struct {
|
||||
Response struct {
|
||||
JWT string `json:"jwt"`
|
||||
} `json:"response"`
|
||||
}
|
||||
h.apiInit().
|
||||
Post("/auth/impersonate").
|
||||
Header("Accept", "application/json").
|
||||
JSON(helpers.JSON(input)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End().
|
||||
JSON(&res)
|
||||
|
||||
// make sure response has JWT token
|
||||
signedToken := res.Response.JWT
|
||||
h.a.NotEmpty(signedToken)
|
||||
|
||||
token, err := jwt.Parse([]byte(signedToken))
|
||||
h.a.Nil(err)
|
||||
|
||||
at, err := service.DefaultStore.LookupAuthOa2tokenByAccess(ctx, token.JwtID())
|
||||
h.a.Nil(err)
|
||||
h.a.NotNil(at)
|
||||
h.a.Equal(at.Access, token.JwtID())
|
||||
}
|
||||
361
server/tests/system/dal_connection_crud_test.go
Normal file
361
server/tests/system/dal_connection_crud_test.go
Normal file
@@ -0,0 +1,361 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearDalConnections() {
|
||||
cc, _, err := store.SearchDalConnections(context.Background(), service.DefaultStore, types.DalConnectionFilter{})
|
||||
h.noError(err)
|
||||
|
||||
for _, c := range cc {
|
||||
if c.Type == types.DalPrimaryConnectionResourceType {
|
||||
continue
|
||||
}
|
||||
h.noError(store.DeleteDalConnection(context.Background(), service.DefaultStore, c))
|
||||
}
|
||||
}
|
||||
|
||||
func (h helper) getPrimaryConnection() *types.DalConnection {
|
||||
cc, _, err := store.SearchDalConnections(context.Background(), service.DefaultStore, types.DalConnectionFilter{Type: types.DalPrimaryConnectionResourceType})
|
||||
h.a.NoError(err)
|
||||
|
||||
if len(cc) != 1 {
|
||||
h.a.FailNow("invalid state: no or too many primary connections")
|
||||
}
|
||||
|
||||
return cc[0]
|
||||
}
|
||||
|
||||
func (h helper) createDalConnection(res *types.DalConnection) *types.DalConnection {
|
||||
if res.ID == 0 {
|
||||
res.ID = id.Next()
|
||||
}
|
||||
|
||||
if res.Meta.Name == "" {
|
||||
res.Meta.Name = "Test Connection"
|
||||
}
|
||||
if res.Handle == "" {
|
||||
res.Handle = "test_connection"
|
||||
}
|
||||
if res.Type == "" {
|
||||
res.Type = types.DalConnectionResourceType
|
||||
}
|
||||
if res.Meta.Ownership == "" {
|
||||
res.Meta.Ownership = "tester"
|
||||
}
|
||||
|
||||
if res.Config.DAL == nil {
|
||||
res.Config.DAL = &types.ConnectionConfigDAL{
|
||||
Type: "corteza::dal:connection:dsn",
|
||||
Params: map[string]any{
|
||||
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if res.Config.DAL.ModelIdent == "" {
|
||||
res.Config.DAL.ModelIdent = "compose_records_{{namespace}}_{{module}}"
|
||||
}
|
||||
|
||||
if res.CreatedAt.IsZero() {
|
||||
res.CreatedAt = time.Now()
|
||||
}
|
||||
if res.CreatedBy == 0 {
|
||||
res.CreatedBy = h.cUser.ID
|
||||
}
|
||||
|
||||
h.a.NoError(service.DefaultStore.CreateDalConnection(context.Background(), res))
|
||||
h.a.NoError(service.DefaultDalConnection.ReloadConnections(context.Background()))
|
||||
return res
|
||||
}
|
||||
|
||||
func Test_dal_connection_list(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connections.search")
|
||||
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/dal/connections/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_list_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
h.apiInit().
|
||||
Get("/dal/connections/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-connection.errors.notAllowedToSearch")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_list_forbidden_read(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connections.search")
|
||||
|
||||
h.apiInit().
|
||||
Get("/dal/connections/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(jsonpath.Len("$.response.set", 0)).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_create(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connection.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/dal/connections/").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present("$.response.connectionID")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_create_invalid_type(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connection.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/dal/connections/").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertErrorP("corteza::system:primary-dal-connection")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_create_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
h.apiInit().
|
||||
Post("/dal/connections/").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-connection.errors.notAllowedToCreate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_update(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "update", "dal-config.manage")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/dal/connections/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal("$.response.handle", "test_connection_edited")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_update_primary(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.getPrimaryConnection()
|
||||
|
||||
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "update")
|
||||
|
||||
// a bit of a problem with testing primary connection update
|
||||
//
|
||||
// when using (for running tests) anything else than connection params specified
|
||||
// in the generic.json scenario, the update will fail
|
||||
// with "can not update connection parameters for primary ..."
|
||||
//
|
||||
// see Update on dalConnection service.
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/dal/connections/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal("$.response.meta.name", "Primary Connection EDITED")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_update_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/dal/connections/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-connection.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_read(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/dal/connections/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present("$.response.connectionID")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_read_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/dal/connections/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-connection.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_delete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/dal/connections/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal("$.success.message", "OK")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_delete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/dal/connections/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-connection.errors.notAllowedToDelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_undelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
DeletedAt: &h.cUser.CreatedAt,
|
||||
DeletedBy: h.cUser.ID,
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/dal/connections/%d/undelete", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal("$.success.message", "OK")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_connection_undelete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearDalConnections()
|
||||
|
||||
sl := h.createDalConnection(&types.DalConnection{
|
||||
Handle: "test_connection",
|
||||
DeletedAt: &h.cUser.CreatedAt,
|
||||
DeletedBy: h.cUser.ID,
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/dal/connections/%d/undelete", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-connection.errors.notAllowedToUndelete")).
|
||||
End()
|
||||
}
|
||||
22
server/tests/system/dal_crud_driver_test.go
Normal file
22
server/tests/system/dal_crud_driver_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func Test_dal_crud_driver_list(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
h.apiInit().
|
||||
Get("/dal/drivers/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.GreaterThan("$.response.set", 0)).
|
||||
Assert(jsonpath.Present("$.response.set[0].operations")).
|
||||
End()
|
||||
}
|
||||
22
server/tests/system/dal_driver_crud_test.go
Normal file
22
server/tests/system/dal_driver_crud_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func Test_dal_driver_list(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
h.apiInit().
|
||||
Get("/dal/drivers/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
Assert(jsonpath.Present("$.response.set[0].operations")).
|
||||
End()
|
||||
}
|
||||
251
server/tests/system/dal_sensitivity_level_crud_test.go
Normal file
251
server/tests/system/dal_sensitivity_level_crud_test.go
Normal file
@@ -0,0 +1,251 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearSensitivityLevels() {
|
||||
h.noError(store.TruncateDalSensitivityLevels(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) createSensitivityLevel(res *types.DalSensitivityLevel) *types.DalSensitivityLevel {
|
||||
if res.ID == 0 {
|
||||
res.ID = id.Next()
|
||||
}
|
||||
|
||||
if res.CreatedAt.IsZero() {
|
||||
res.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
h.a.NoError(service.DefaultStore.CreateDalSensitivityLevel(context.Background(), res))
|
||||
return res
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_list(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
|
||||
|
||||
h.apiInit().
|
||||
Get("/dal/sensitivity-levels/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_list_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
h.apiInit().
|
||||
Get("/dal/sensitivity-levels/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-sensitivity-level.errors.notAllowedToManage")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_create(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
|
||||
|
||||
h.apiInit().
|
||||
Post("/dal/sensitivity-levels/").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present("$.response.sensitivityLevelID")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_create_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
h.apiInit().
|
||||
Post("/dal/sensitivity-levels/").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-sensitivity-level.errors.notAllowedToManage")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_update(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/dal/sensitivity-levels/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present("$.response.sensitivityLevelID")).
|
||||
Assert(jsonpath.Equal("$.response.handle", "private_edited")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_update_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/dal/sensitivity-levels/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
Body(loadScenarioRequest(t, "generic.json")).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-sensitivity-level.errors.notAllowedToManage")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_read(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/dal/sensitivity-levels/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present("$.response.sensitivityLevelID")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_read_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/dal/sensitivity-levels/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-sensitivity-level.errors.notAllowedToManage")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_delete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/dal/sensitivity-levels/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal("$.success.message", "OK")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_delete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/dal/sensitivity-levels/%d", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-sensitivity-level.errors.notAllowedToManage")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_undelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
DeletedAt: &h.cUser.CreatedAt,
|
||||
DeletedBy: h.cUser.ID,
|
||||
})
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/dal/sensitivity-levels/%d/undelete", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal("$.success.message", "OK")).
|
||||
End()
|
||||
}
|
||||
|
||||
func Test_dal_sensitivity_level_undelete_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
defer h.clearSensitivityLevels()
|
||||
|
||||
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
|
||||
Handle: "test_sl",
|
||||
DeletedAt: &h.cUser.CreatedAt,
|
||||
DeletedBy: h.cUser.ID,
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/dal/sensitivity-levels/%d/undelete", sl.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("dal-sensitivity-level.errors.notAllowedToManage")).
|
||||
End()
|
||||
}
|
||||
102
server/tests/system/data_privacy_request_comment_test.go
Normal file
102
server/tests/system/data_privacy_request_comment_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
"github.com/spf13/cast"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (h helper) clearDataPrivacyRequestComments() {
|
||||
h.noError(store.TruncateDataPrivacyRequestComments(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) createDataPrivacyRequestComment(comment string, requestID uint64) *types.DataPrivacyRequestComment {
|
||||
res := &types.DataPrivacyRequestComment{
|
||||
ID: id.Next(),
|
||||
RequestID: requestID,
|
||||
Comment: comment,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateDataPrivacyRequestComment(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) createSampleDataPrivacyRequestComment(requestID ...uint64) *types.DataPrivacyRequestComment {
|
||||
rID := id.Next()
|
||||
if len(requestID) == 1 {
|
||||
rID = requestID[0]
|
||||
}
|
||||
return h.createDataPrivacyRequestComment(rs(20), rID)
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestCommentList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
h.clearDataPrivacyRequestComments()
|
||||
|
||||
request := h.createSampleDataPrivacyRequest()
|
||||
reqID := request.ID
|
||||
h.createSampleDataPrivacyRequestComment(reqID)
|
||||
h.createSampleDataPrivacyRequestComment(reqID)
|
||||
h.createSampleDataPrivacyRequestComment()
|
||||
h.createSampleDataPrivacyRequestComment()
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/data-privacy/requests/%d/comments/", reqID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestCreateComment(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequestComments()
|
||||
|
||||
request := h.createSampleDataPrivacyRequest()
|
||||
reqID := request.ID
|
||||
comment := rs(20)
|
||||
|
||||
helpers.AllowMe(h, types.DataPrivacyRequestRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/data-privacy/requests/%d/comments/", reqID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("comment", comment).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.requestID`)).
|
||||
Assert(jsonpath.Present(`$.response.createdAt`)).
|
||||
Assert(jsonpath.Present(`$.response.createdBy`)).
|
||||
Assert(jsonpath.Equal(`$.response.requestID`, cast.ToString(reqID))).
|
||||
Assert(jsonpath.Equal(`$.response.comment`, comment)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestCreateCommentForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequestComments()
|
||||
comment := rs(20)
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/data-privacy/requests/%d/comments/", id.Next())).
|
||||
Header("Accept", "application/json").
|
||||
FormData("comment", comment).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("data-privacy.errors.notFound")).
|
||||
End()
|
||||
}
|
||||
241
server/tests/system/data_privacy_request_test.go
Normal file
241
server/tests/system/data_privacy_request_test.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
"github.com/spf13/cast"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearDataPrivacyRequests() {
|
||||
h.noError(store.TruncateDataPrivacyRequests(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) createDataPrivacyRequest(kind types.RequestKind, status types.RequestStatus, requestedBy uint64) *types.DataPrivacyRequest {
|
||||
res := &types.DataPrivacyRequest{
|
||||
ID: id.Next(),
|
||||
Kind: types.RequestKindCorrect,
|
||||
Status: types.RequestStatusPending,
|
||||
RequestedAt: time.Now(),
|
||||
RequestedBy: requestedBy,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if len(kind) > 0 {
|
||||
res.Kind = kind
|
||||
}
|
||||
|
||||
if len(status) > 0 {
|
||||
res.Status = status
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateDataPrivacyRequest(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) createSampleDataPrivacyRequest() *types.DataPrivacyRequest {
|
||||
return h.createDataPrivacyRequest("", "", 0)
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
h.createSampleDataPrivacyRequest()
|
||||
h.createSampleDataPrivacyRequest()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "data-privacy-requests.search")
|
||||
helpers.AllowMe(h, types.DataPrivacyRequestRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/data-privacy/requests/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestListWithPaging(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
seedCount := 40
|
||||
for i := 0; i < seedCount; i++ {
|
||||
h.createSampleDataPrivacyRequest()
|
||||
}
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "data-privacy-requests.search")
|
||||
helpers.AllowMe(h, types.DataPrivacyRequestRbacResource(0), "read")
|
||||
|
||||
var aux = struct {
|
||||
Response struct {
|
||||
Filter struct {
|
||||
NextPage *string
|
||||
PrevPage *string
|
||||
}
|
||||
}
|
||||
}{}
|
||||
|
||||
h.apiInit().
|
||||
Get("/data-privacy/requests/").
|
||||
Query("limit", "10").
|
||||
// Query("sort", "kind,createdAt+DESC").
|
||||
Query("sort", "kind").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filter`)).
|
||||
Assert(jsonpath.Present(`$.response.set`)).
|
||||
Assert(jsonpath.Len(`$.response.set`, 10)).
|
||||
Assert(jsonpath.Present(`$.response.filter.nextPage`)).
|
||||
End().
|
||||
JSON(&aux)
|
||||
|
||||
h.a.NotNil(aux.Response.Filter.NextPage)
|
||||
|
||||
h.apiInit().
|
||||
Get("/data-privacy/requests/").
|
||||
Header("Accept", "application/json").
|
||||
Query("limit", "10").
|
||||
Query("sort", "kind").
|
||||
// Query("sort", "kind,createdAt+DESC").
|
||||
Query("pageCursor", *aux.Response.Filter.NextPage).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(jsonpath.Len(`$.response.set`, 10)).
|
||||
Assert(jsonpath.Present(`$.response.filter.prevPage`)).
|
||||
Assert(jsonpath.Present(`$.response.filter.nextPage`)).
|
||||
End().
|
||||
JSON(&aux)
|
||||
|
||||
h.a.NotNil(aux.Response.Filter.PrevPage)
|
||||
h.a.NotNil(aux.Response.Filter.NextPage)
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestListFilters(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
h.createSampleDataPrivacyRequest()
|
||||
h.createSampleDataPrivacyRequest()
|
||||
h.createDataPrivacyRequest("", types.RequestStatusApproved, 0)
|
||||
h.createDataPrivacyRequest(types.RequestKindExport, types.RequestStatusApproved, 0)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "data-privacy-requests.search")
|
||||
helpers.AllowMe(h, types.DataPrivacyRequestRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/data-privacy/requests/").
|
||||
Query("query", types.RequestStatusApproved.String()).
|
||||
Query("kind", types.RequestKindExport.String()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestListWithRequestedByFilter(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
user := h.createUserWithEmail(h.randEmail())
|
||||
h.createSampleDataPrivacyRequest()
|
||||
h.createSampleDataPrivacyRequest()
|
||||
h.createDataPrivacyRequest("", types.RequestStatusApproved, user.ID)
|
||||
h.createDataPrivacyRequest(types.RequestKindExport, types.RequestStatusApproved, user.ID)
|
||||
h.createSampleDataPrivacyRequest()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "data-privacy-requests.search")
|
||||
helpers.AllowMe(h, types.DataPrivacyRequestRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/data-privacy/requests/").
|
||||
Query("requestedBy", cast.ToString(user.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
client := h.createSampleDataPrivacyRequest()
|
||||
|
||||
helpers.AllowMe(h, types.DataPrivacyRequestRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Getf("/data-privacy/requests/%d", client.ID).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "data-privacy-request.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/data-privacy/requests/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("kind", "correct").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.requestID`)).
|
||||
Assert(jsonpath.Equal(`$.response.kind`, types.RequestKindCorrect.String())).
|
||||
Assert(jsonpath.Equal(`$.response.status`, types.RequestStatusPending.String())).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestUpdateStatus(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
req := h.createSampleDataPrivacyRequest()
|
||||
|
||||
helpers.AllowMe(h, types.DataPrivacyRequestRbacResource(0), "approve")
|
||||
|
||||
h.apiInit().
|
||||
Patchf("/data-privacy/requests/%d/status/approved", req.ID).
|
||||
Header("Accept", "application/json").
|
||||
JSON(helpers.JSON(req)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.status`, types.RequestStatusApproved.String())).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestDataPrivacyRequestUpdateStatusForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearDataPrivacyRequests()
|
||||
|
||||
req := h.createSampleDataPrivacyRequest()
|
||||
|
||||
h.apiInit().
|
||||
Patchf("/data-privacy/requests/%d/status/approved", req.ID).
|
||||
Header("Accept", "application/json").
|
||||
JSON(helpers.JSON(req)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("data-privacy.errors.notAllowedToApprove")).
|
||||
End()
|
||||
}
|
||||
230
server/tests/system/main_test.go
Normal file
230
server/tests/system/main_test.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/app"
|
||||
"github.com/cortezaproject/corteza/server/auth/handlers"
|
||||
"github.com/cortezaproject/corteza/server/auth/request"
|
||||
"github.com/cortezaproject/corteza/server/auth/saml"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api/server"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/pkg/label"
|
||||
ltype "github.com/cortezaproject/corteza/server/pkg/label/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza/server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza/server/pkg/objstore/plain"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rand"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/store/adapters/rdbms/drivers/sqlite"
|
||||
"github.com/cortezaproject/corteza/server/system/rest"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/steinfletcher/apitest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
//go:embed static
|
||||
var mockData embed.FS
|
||||
|
||||
type (
|
||||
helper struct {
|
||||
t *testing.T
|
||||
a *require.Assertions
|
||||
sp *saml.SamlSPService
|
||||
|
||||
cUser *types.User
|
||||
roleID uint64
|
||||
token []byte
|
||||
data embed.FS
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
testApp *app.CortezaApp
|
||||
r chi.Router
|
||||
hh *handlers.AuthHandlers
|
||||
)
|
||||
|
||||
func init() {
|
||||
helpers.RecursiveDotEnvLoad()
|
||||
}
|
||||
|
||||
// random string, 10 chars long by default
|
||||
func rs(a ...int) string {
|
||||
var l = 10
|
||||
if len(a) > 0 {
|
||||
l = a[0]
|
||||
}
|
||||
|
||||
return string(rand.Bytes(l))
|
||||
}
|
||||
|
||||
func InitTestApp() {
|
||||
var sm *request.SessionManager
|
||||
|
||||
if testApp == nil {
|
||||
ctx := cli.Context()
|
||||
|
||||
testApp = helpers.NewIntegrationTestApp(ctx, func(app *app.CortezaApp) (err error) {
|
||||
service.CurrentSettings.Auth.External.Enabled = true
|
||||
service.DefaultObjectStore, err = plain.NewWithAfero(afero.NewMemMapFs(), "test")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.DefaultStore, err = sqlite.ConnectInMemory(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Tests should be executed w/o any locales
|
||||
locale.SetGlobal(locale.Static(&locale.Language{Tag: language.Und}))
|
||||
|
||||
sm = request.NewSessionManager(service.DefaultStore, app.Opt.Auth, service.DefaultLogger)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
sp, _ := loadSAMLService()
|
||||
|
||||
hh = &handlers.AuthHandlers{
|
||||
SamlSPService: sp,
|
||||
Log: zap.NewNop(),
|
||||
AuthService: service.DefaultAuth,
|
||||
SessionManager: sm,
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
|
||||
helpers.BindAuthMiddleware(r)
|
||||
r.Group(rest.MountRoutes())
|
||||
hh.MountHttpRoutes(r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
InitTestApp()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func newHelper(t *testing.T) helper {
|
||||
h := helper{
|
||||
t: t,
|
||||
a: require.New(t),
|
||||
roleID: id.Next(),
|
||||
cUser: &types.User{
|
||||
ID: id.Next(),
|
||||
},
|
||||
data: mockData,
|
||||
}
|
||||
|
||||
h.cUser.SetRoles(h.roleID)
|
||||
helpers.UpdateRBAC(h.roleID)
|
||||
h.mockPermissionsWithAccess()
|
||||
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
h.token, err = auth.TokenIssuer.Issue(ctx, auth.WithIdentity(h.cUser))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h helper) MyRole() uint64 {
|
||||
return h.roleID
|
||||
}
|
||||
|
||||
// Returns context w/ security details
|
||||
func (h helper) secCtx() context.Context {
|
||||
return auth.SetIdentityToContext(context.Background(), h.cUser)
|
||||
}
|
||||
|
||||
// apitest basics, initialize, set handler, add auth
|
||||
func (h helper) apiInit() *apitest.APITest {
|
||||
InitTestApp()
|
||||
|
||||
return apitest.
|
||||
New().
|
||||
Handler(r).
|
||||
Intercept(helpers.ReqHeaderRawAuthBearer(h.token))
|
||||
}
|
||||
|
||||
func (h helper) mockPermissions(rules ...*rbac.Rule) {
|
||||
h.a.NoError(rbac.Global().Grant(
|
||||
// TestService we use does not have any backend storage,
|
||||
context.Background(),
|
||||
rules...,
|
||||
))
|
||||
}
|
||||
|
||||
// Prepends allow access rule for system service for everyone
|
||||
func (h helper) mockPermissionsWithAccess(rules ...*rbac.Rule) {
|
||||
h.mockPermissions(rules...)
|
||||
}
|
||||
|
||||
// Unwraps error before it passes it to the tester
|
||||
func (h helper) noError(err error) {
|
||||
for errors.Unwrap(err) != nil {
|
||||
err = errors.Unwrap(err)
|
||||
}
|
||||
|
||||
h.a.NoError(err)
|
||||
}
|
||||
|
||||
func (h helper) setLabel(res label.LabeledResource, name, value string) {
|
||||
h.a.NoError(store.UpsertLabel(h.secCtx(), service.DefaultStore, <ype.Label{
|
||||
Kind: res.LabelResourceKind(),
|
||||
ResourceID: res.LabelResourceID(),
|
||||
Name: name,
|
||||
Value: ltype.LabelValue{Val: value},
|
||||
}))
|
||||
}
|
||||
|
||||
func (h helper) assertBody(e string, s io.ReadCloser) {
|
||||
b, err := io.ReadAll(s)
|
||||
h.a.NoError(err)
|
||||
h.a.Equal(e, string(b))
|
||||
}
|
||||
|
||||
func (h helper) clearTemplates() {
|
||||
h.noError(store.TruncateTemplates(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func readStaticFile(f string) []byte {
|
||||
c, _ := mockData.ReadFile(f)
|
||||
return c
|
||||
}
|
||||
|
||||
func loadScenarioRequest(t *testing.T, req string) string {
|
||||
f, err := os.Open(path.Join("testdata", t.Name()[5:], req))
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
bb, err := ioutil.ReadAll(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
return string(bb)
|
||||
}
|
||||
602
server/tests/system/notification_test.go
Normal file
602
server/tests/system/notification_test.go
Normal file
@@ -0,0 +1,602 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearNotifications() {
|
||||
h.noError(store.TruncateNotifications(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) makeNotification() *types.Notification {
|
||||
return h.notification(h.cUser.ID, types.NotificationKindSimple, false)
|
||||
}
|
||||
|
||||
func (h helper) makeNotificationByUserID(userID uint64) *types.Notification {
|
||||
return h.notification(userID, types.NotificationKindSimple, false)
|
||||
}
|
||||
|
||||
func (h helper) makeReadNotification() *types.Notification {
|
||||
ntf := h.makeNotification()
|
||||
now := time.Now()
|
||||
ntf.ReadAt = &now
|
||||
h.noError(store.UpdateNotification(context.Background(), service.DefaultStore, ntf))
|
||||
return ntf
|
||||
}
|
||||
|
||||
func (h helper) makeDeletedNotification() *types.Notification {
|
||||
return h.notification(h.cUser.ID, types.NotificationKindSimple, true)
|
||||
}
|
||||
|
||||
func (h helper) notification(userID uint64, notificationKind types.NotificationKind, deleted bool) *types.Notification {
|
||||
ntf := &types.Notification{
|
||||
Kind: notificationKind,
|
||||
Config: types.NotificationConfig{
|
||||
Simple: types.SimpleNotificationConfig{
|
||||
Title: "Test notification",
|
||||
Description: "This is a test notification",
|
||||
},
|
||||
},
|
||||
Recipient: userID,
|
||||
CreatedBy: h.cUser.ID,
|
||||
}
|
||||
ntf.ID = id.Next()
|
||||
ntf.CreatedAt = time.Now()
|
||||
if deleted {
|
||||
now := time.Now()
|
||||
ntf.DeletedAt = &now
|
||||
}
|
||||
|
||||
h.noError(store.CreateNotification(context.Background(), service.DefaultStore, ntf))
|
||||
return ntf
|
||||
}
|
||||
|
||||
func (h helper) lookupNotificationByID(ID uint64) *types.Notification {
|
||||
ntf, err := store.LookupNotificationByID(context.Background(), service.DefaultStore, ID)
|
||||
h.noError(err)
|
||||
return ntf
|
||||
}
|
||||
|
||||
func TestNotificationCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntfTemplate := h.makeNotification()
|
||||
|
||||
h.apiInit().
|
||||
Post("/notification/").
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
JSON(map[string]interface{}{
|
||||
"kind": string(ntfTemplate.Kind),
|
||||
"config": ntfTemplate.Config,
|
||||
"recipient": strconv.FormatUint(ntfTemplate.Recipient, 10),
|
||||
}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.kind`, string(ntfTemplate.Kind))).
|
||||
Assert(jsonpath.Equal(`$.response.config.simple.title`, ntfTemplate.Config.Simple.Title)).
|
||||
Assert(jsonpath.Equal(`$.response.config.simple.description`, ntfTemplate.Config.Simple.Description)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotification()
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.kind`, string(ntf.Kind))).
|
||||
Assert(jsonpath.Equal(`$.response.config.simple.title`, ntf.Config.Simple.Title)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationReadForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotificationByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("notification.errors.notFound")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Create 3 notifications: 2 for current user (1 read, 1 unread) and 1 for another user
|
||||
h.makeNotification()
|
||||
h.makeReadNotification()
|
||||
h.makeNotificationByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationListUnreadOnly(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Create one unread notification
|
||||
h.makeNotification()
|
||||
|
||||
// Create one read notification
|
||||
h.makeReadNotification()
|
||||
|
||||
// Test with read=0 which should return only unread notifications
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Query("read", "0").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationListWithRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Create one unread notification
|
||||
h.makeNotification()
|
||||
|
||||
// Create one read notification
|
||||
h.makeReadNotification()
|
||||
|
||||
// Test with read=1 which should return both read and unread notifications
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Query("read", "1").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationListReadOnly(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Create one unread notification
|
||||
h.makeNotification()
|
||||
|
||||
// Create one read notification
|
||||
h.makeReadNotification()
|
||||
|
||||
// Test with read=2 which should return only read notifications
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Query("read", "2").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationListNoDeleted(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
h.makeNotification()
|
||||
h.makeDeletedNotification()
|
||||
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Query("deleted", "0").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationListWithDeleted(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
h.makeNotification()
|
||||
h.makeDeletedNotification()
|
||||
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Query("deleted", "1").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationListDeletedOnly(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
h.makeNotification()
|
||||
h.makeDeletedNotification()
|
||||
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Query("deleted", "2").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotification()
|
||||
|
||||
// Create update data
|
||||
updatedConfig := types.NotificationConfig{
|
||||
Simple: types.SimpleNotificationConfig{
|
||||
Title: "Updated Title",
|
||||
Description: "Updated Description",
|
||||
},
|
||||
}
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
JSON(map[string]interface{}{
|
||||
"kind": string(ntf.Kind),
|
||||
"config": updatedConfig,
|
||||
}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.config.simple.title`, updatedConfig.Simple.Title)).
|
||||
Assert(jsonpath.Equal(`$.response.config.simple.description`, updatedConfig.Simple.Description)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationUpdateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotificationByUserID(id.Next())
|
||||
|
||||
// Create update data
|
||||
updatedConfig := types.NotificationConfig{
|
||||
Simple: types.SimpleNotificationConfig{
|
||||
Title: "Updated Title",
|
||||
Description: "Updated Description",
|
||||
},
|
||||
}
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
JSON(map[string]interface{}{
|
||||
"kind": string(ntf.Kind),
|
||||
"config": updatedConfig,
|
||||
}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("notification.errors.notFound")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotification()
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Verify notification is marked as deleted
|
||||
deletedNtf := h.lookupNotificationByID(ntf.ID)
|
||||
if deletedNtf.DeletedAt == nil {
|
||||
t.Error("notification should be marked as deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationDeleteForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotificationByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("notification.errors.notFound")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationMarkAsRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotification()
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/notification/%d/read", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Verify notification is marked as read
|
||||
readNtf := h.lookupNotificationByID(ntf.ID)
|
||||
if readNtf.ReadAt == nil {
|
||||
t.Error("notification should be marked as read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationMarkAsReadForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotificationByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/notification/%d/read", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("notification.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationMarkAsUnread(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotification()
|
||||
|
||||
// Mark as read first so we can verify unread transition
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/notification/%d/read", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/notification/%d/unread", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Verify notification is marked as unread
|
||||
unreadNtf := h.lookupNotificationByID(ntf.ID)
|
||||
if unreadNtf.ReadAt != nil {
|
||||
t.Error("notification should be marked as unread")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationMarkAsUnreadForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotificationByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/notification/%d/unread", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("notification.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationMarkAllAsRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Create multiple unread notifications
|
||||
h.makeNotification()
|
||||
h.makeNotification()
|
||||
h.makeNotification()
|
||||
|
||||
h.apiInit().
|
||||
Patch("/notification/all/read").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// List read notifications and verify all are marked as read
|
||||
h.apiInit().
|
||||
Get("/notification/").
|
||||
Query("read", "2").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 3)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationMarkAllAsUnread(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Create multiple notifications and mark them as read
|
||||
n1 := h.makeNotification()
|
||||
n2 := h.makeNotification()
|
||||
n3 := h.makeNotification()
|
||||
|
||||
h.apiInit().
|
||||
Patch("/notification/all/read").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Now mark all as unread
|
||||
h.apiInit().
|
||||
Patch("/notification/all/unread").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Verify notifications are marked as unread
|
||||
for _, n := range []uint64{n1.ID, n2.ID, n3.ID} {
|
||||
ntf := h.lookupNotificationByID(n)
|
||||
if ntf.ReadAt != nil {
|
||||
t.Errorf("notification %d should be marked as unread", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationAssign_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Try to create a notification for another user without the proper permission
|
||||
h.apiInit().
|
||||
Post("/notification/").
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
JSON(map[string]interface{}{
|
||||
"kind": string(types.NotificationKindSimple),
|
||||
"config": types.NotificationConfig{
|
||||
Simple: types.SimpleNotificationConfig{
|
||||
Title: "Test notification",
|
||||
Description: "This is a test notification",
|
||||
},
|
||||
},
|
||||
"recipient": strconv.FormatUint(id.Next(), 10),
|
||||
}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("notification.errors.notAllowedToAssign")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationAssign(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Allow current user to assign notifications
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "notification.assign")
|
||||
|
||||
otherUserID := id.Next()
|
||||
|
||||
// Create a notification for another user with the permission
|
||||
h.apiInit().
|
||||
Post("/notification/").
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
JSON(map[string]interface{}{
|
||||
"kind": string(types.NotificationKindSimple),
|
||||
"config": types.NotificationConfig{
|
||||
Simple: types.SimpleNotificationConfig{
|
||||
Title: "Test notification",
|
||||
Description: "This is a test notification",
|
||||
},
|
||||
},
|
||||
"recipient": strconv.FormatUint(otherUserID, 10),
|
||||
}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.kind`, string(types.NotificationKindSimple))).
|
||||
Assert(jsonpath.Equal(`$.response.recipient`, strconv.FormatUint(otherUserID, 10))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationUpdateRecipient_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
ntf := h.makeNotification()
|
||||
otherUserID := id.Next()
|
||||
|
||||
// Try to update a notification with a different recipient without permission
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
JSON(map[string]interface{}{
|
||||
"kind": string(ntf.Kind),
|
||||
"config": ntf.Config,
|
||||
"recipient": strconv.FormatUint(otherUserID, 10),
|
||||
}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("notification.errors.notAllowedToAssign")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestNotificationUpdateRecipient(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearNotifications()
|
||||
|
||||
// Allow current user to assign notifications
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "notification.assign")
|
||||
|
||||
ntf := h.makeNotification()
|
||||
otherUserID := id.Next()
|
||||
|
||||
// Update a notification with a different recipient with permission
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Header("Content-Type", "application/json").
|
||||
JSON(map[string]interface{}{
|
||||
"kind": string(ntf.Kind),
|
||||
"config": ntf.Config,
|
||||
"recipient": strconv.FormatUint(otherUserID, 10),
|
||||
}).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.recipient`, strconv.FormatUint(otherUserID, 10))).
|
||||
End()
|
||||
}
|
||||
280
server/tests/system/permissions_test.go
Normal file
280
server/tests/system/permissions_test.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestPermissionsEffective(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "user.create")
|
||||
|
||||
h.apiInit().
|
||||
Get("/permissions/effective").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestPermissionsList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
json := h.apiInit().
|
||||
Get("/permissions/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(fmt.Sprintf(`$.response[? @.type=="%s"]`, types.ComponentResourceType))).
|
||||
End()
|
||||
|
||||
fmt.Println("json: ", json.Response.Body)
|
||||
}
|
||||
|
||||
func TestPermissionsRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "user.create")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/permissions/%d/rules", h.roleID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestPermissionsReadWithFilter(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "user.create")
|
||||
|
||||
// Specific resource related rules
|
||||
testID := id.Next()
|
||||
helpers.AllowMe(h, types.UserRbacResource(testID), "read")
|
||||
helpers.AllowMe(h, types.UserRbacResource(id.Next()), "update")
|
||||
|
||||
t.Log("all component-level and wildcard rules")
|
||||
h.apiInit().
|
||||
Getf("/permissions/%d/rules", h.roleID).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 2)).
|
||||
End()
|
||||
|
||||
t.Log("no rules for all-users")
|
||||
h.apiInit().
|
||||
Getf("/permissions/%d/rules", h.roleID).
|
||||
Query("resource", "corteza::system:user/*").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 0)).
|
||||
End()
|
||||
|
||||
t.Log("1 rule for specific user")
|
||||
h.apiInit().
|
||||
Getf("/permissions/%d/rules", h.roleID).
|
||||
Query("resource", fmt.Sprintf("corteza::system:user/%d", testID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestPermissionsUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/permissions/%d/rules", h.roleID)).
|
||||
Header("Accept", "application/json").
|
||||
JSON(fmt.Sprintf(`{"rules":[{"resource":"%s","operation":"user.create","access":"allow"}]}`, types.ComponentRbacResource())).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestPermissionsDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
p := rbac.Global()
|
||||
|
||||
// Make sure our user can grant
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
// New role.
|
||||
permDelRole := h.roleID + 1
|
||||
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(permDelRole), 0)
|
||||
|
||||
// Setup a few fake rules for new role
|
||||
helpers.Grant(rbac.AllowRule(permDelRole, types.ComponentRbacResource(), "user.create"))
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(permDelRole), 1)
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/permissions/%d/rules", permDelRole)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Make sure all rules for this role are deleted
|
||||
for _, r := range p.FindRulesByRoleID(permDelRole) {
|
||||
h.a.True(r.Access == rbac.Inherit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPermissionsTrace(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
h.apiInit().
|
||||
Get("/permissions/trace").
|
||||
Query("roleID[]", "1").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestPermissionsCloneToSingleRole(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
p := rbac.Global()
|
||||
|
||||
// Make sure our user can grant
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
// New role.
|
||||
roleS := h.roleID + 1
|
||||
roleT := h.roleID + 2
|
||||
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(roleS), 0)
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(roleT), 0)
|
||||
|
||||
// Set up a few fake rules for new role
|
||||
helpers.Grant(rbac.AllowRule(roleS, types.ComponentRbacResource(), "user.create"))
|
||||
|
||||
helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.update"))
|
||||
helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.delete"))
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(roleS), 1)
|
||||
h.a.Len(p.FindRulesByRoleID(roleT), 2)
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/rules/clone", roleS)).
|
||||
Query("cloneToRoleID", strconv.FormatUint(roleT, 10)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Make sure all rules for role S are intact
|
||||
h.a.Len(p.FindRulesByRoleID(roleS), 1)
|
||||
// Make sure all rules for role T are cloned from role S
|
||||
h.a.Len(p.FindRulesByRoleID(roleT), 1)
|
||||
}
|
||||
|
||||
func TestPermissionsCloneToMultipleRole(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
p := rbac.Global()
|
||||
|
||||
// Make sure our user can grant
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
// New role.
|
||||
roleS := h.roleID + 1
|
||||
roleT := h.roleID + 2
|
||||
roleY := h.roleID + 3
|
||||
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(roleS), 0)
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(roleT), 0)
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(roleY), 0)
|
||||
|
||||
// Set up a few fake rules for new role
|
||||
helpers.Grant(rbac.AllowRule(roleS, types.ComponentRbacResource(), "user.create"))
|
||||
|
||||
helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.update"))
|
||||
helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.delete"))
|
||||
|
||||
helpers.Grant(rbac.AllowRule(roleY, types.ComponentRbacResource(), "user.create"))
|
||||
helpers.Grant(rbac.AllowRule(roleY, types.ComponentRbacResource(), "user.update"))
|
||||
helpers.Grant(rbac.AllowRule(roleY, types.ComponentRbacResource(), "user.delete"))
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(roleS), 1)
|
||||
h.a.Len(p.FindRulesByRoleID(roleT), 2)
|
||||
h.a.Len(p.FindRulesByRoleID(roleY), 3)
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/rules/clone", roleS)).
|
||||
Query("cloneToRoleID", strconv.FormatUint(roleT, 10)).
|
||||
Query("cloneToRoleID", strconv.FormatUint(roleY, 10)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Make sure all rules for role S are intact
|
||||
h.a.Len(p.FindRulesByRoleID(roleS), 1)
|
||||
// Make sure all rules for role T are cloned from role S
|
||||
h.a.Len(p.FindRulesByRoleID(roleT), 1)
|
||||
// Make sure all rules for role Y are cloned from role S
|
||||
h.a.Len(p.FindRulesByRoleID(roleY), 1)
|
||||
}
|
||||
|
||||
func TestPermissionsCloneNotAllowed(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
p := rbac.Global()
|
||||
|
||||
// New role.
|
||||
roleS := h.roleID + 1
|
||||
roleT := h.roleID + 2
|
||||
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(roleS), 0)
|
||||
h.a.Len(rbac.Global().FindRulesByRoleID(roleT), 0)
|
||||
|
||||
// Set up a few fake rules for new role
|
||||
helpers.Grant(rbac.AllowRule(roleS, types.ComponentRbacResource(), "user.create"))
|
||||
|
||||
helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.update"))
|
||||
helpers.Grant(rbac.AllowRule(roleT, types.ComponentRbacResource(), "user.delete"))
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(roleS), 1)
|
||||
h.a.Len(p.FindRulesByRoleID(roleT), 2)
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/rules/clone", roleS)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("cloneToRoleID", strconv.FormatUint(roleT, 10)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.notAllowedToCloneRules")).
|
||||
End()
|
||||
}
|
||||
176
server/tests/system/queues_test.go
Normal file
176
server/tests/system/queues_test.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
mtypes "github.com/cortezaproject/corteza/server/pkg/messagebus/types"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearMessagebusQueues() {
|
||||
h.noError(store.TruncateQueues(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) repoMakeMessagebusQueue(consumer ...string) *types.Queue {
|
||||
res := &types.Queue{
|
||||
ID: id.Next(),
|
||||
Queue: rs(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if len(consumer) == 0 {
|
||||
res.Consumer = string(mtypes.ConsumerCorteza)
|
||||
} else {
|
||||
res.Consumer = consumer[0]
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateQueue(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupByID(id uint64) *types.Queue {
|
||||
res, err := store.LookupQueueByID(context.Background(), service.DefaultStore, id)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupByQueue(queue string) *types.Queue {
|
||||
res, err := store.LookupQueueByQueue(context.Background(), service.DefaultStore, queue)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestQueueList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearMessagebusQueues()
|
||||
|
||||
h.repoMakeMessagebusQueue()
|
||||
h.repoMakeMessagebusQueue()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "queues.search")
|
||||
helpers.AllowMe(h, types.QueueRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/queues/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestQueueRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearMessagebusQueues()
|
||||
|
||||
res := h.repoMakeMessagebusQueue()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "queues.search")
|
||||
helpers.AllowMe(h, types.QueueRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/queues/%d", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestQueueCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearMessagebusQueues()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "queue.create")
|
||||
|
||||
consumer := string(mtypes.ConsumerStore)
|
||||
queue := rs()
|
||||
|
||||
h.apiInit().
|
||||
Post("/queues").
|
||||
FormData("consumer", consumer).
|
||||
FormData("queue", queue).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res := h.lookupByQueue(queue)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(consumer, res.Consumer)
|
||||
}
|
||||
|
||||
func TestQueueUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearMessagebusQueues()
|
||||
|
||||
consumer := string(mtypes.ConsumerRedis)
|
||||
res := h.repoMakeMessagebusQueue()
|
||||
res.Consumer = consumer
|
||||
|
||||
helpers.AllowMe(h, types.QueueRbacResource(0), "update")
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/queues/%d", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
JSON(helpers.JSON(res)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(consumer, res.Consumer)
|
||||
}
|
||||
|
||||
func TestQueueDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearMessagebusQueues()
|
||||
|
||||
res := h.repoMakeMessagebusQueue()
|
||||
|
||||
helpers.AllowMe(h, types.QueueRbacResource(0), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/queues/%d", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.NotNil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestQueueUnDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearMessagebusQueues()
|
||||
|
||||
res := h.repoMakeMessagebusQueue()
|
||||
|
||||
helpers.AllowMe(h, types.QueueRbacResource(0), "delete")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/queues/%d/undelete", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Nil(res.DeletedAt)
|
||||
}
|
||||
356
server/tests/system/reminder_test.go
Normal file
356
server/tests/system/reminder_test.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) clearReminders() {
|
||||
h.noError(store.TruncateReminders(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) makeReminder() *types.Reminder {
|
||||
return h.reminder(h.cUser.ID, false)
|
||||
}
|
||||
|
||||
func (h helper) makeReminderByUserID(userID uint64) *types.Reminder {
|
||||
return h.reminder(userID, false)
|
||||
}
|
||||
|
||||
func (h helper) makeDeletedReminder() *types.Reminder {
|
||||
return h.reminder(h.cUser.ID, true)
|
||||
}
|
||||
|
||||
func (h helper) reminder(userID uint64, deleted bool) *types.Reminder {
|
||||
rm := &types.Reminder{Resource: "test:resource", AssignedTo: userID}
|
||||
rm.ID = id.Next()
|
||||
rm.CreatedAt = time.Now()
|
||||
if deleted {
|
||||
now := time.Now()
|
||||
rm.DeletedAt = &now
|
||||
}
|
||||
|
||||
h.noError(store.CreateReminder(context.Background(), service.DefaultStore, rm))
|
||||
return rm
|
||||
}
|
||||
|
||||
func (h helper) lookupReminderByID(ID uint64) *types.Reminder {
|
||||
rm, err := store.LookupReminderByID(context.Background(), service.DefaultStore, ID)
|
||||
h.noError(err)
|
||||
return rm
|
||||
}
|
||||
|
||||
func TestReminderCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
h.apiInit().
|
||||
Post("/reminder/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("resource", "some:resource").
|
||||
FormData("assignedTo", strconv.FormatUint(h.cUser.ID, 10)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.resource`, "some:resource")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderAssign_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
h.apiInit().
|
||||
Post("/reminder/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("resource", "some:resource").
|
||||
FormData("assignedTo", "404").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("reminder.errors.notAllowedToAssign")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderAssign(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "reminder.assign")
|
||||
|
||||
h.apiInit().
|
||||
Post("/reminder/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("resource", "some:resource").
|
||||
FormData("assignedTo", "404").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.resource`, "some:resource")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminder()
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/reminder/%d", rm.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.resource`, rm.Resource)).
|
||||
End()
|
||||
}
|
||||
|
||||
// TestReminderReadForbidden checks only user themself can read reminder assigned to them
|
||||
func TestReminderReadForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminderByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/reminder/%d", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("reminder.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
h.makeReminder()
|
||||
h.makeDeletedReminder()
|
||||
h.makeDeletedReminder()
|
||||
|
||||
h.apiInit().
|
||||
Get("/reminder/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderListIncludeDeleted(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
h.makeReminder()
|
||||
h.makeDeletedReminder()
|
||||
h.makeDeletedReminder()
|
||||
|
||||
h.apiInit().
|
||||
Get("/reminder/").
|
||||
Query("includeDeleted", "true").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 3)).
|
||||
End()
|
||||
}
|
||||
|
||||
// TestReminderListForbidden checks reminders of other users are not listed
|
||||
func TestReminderListForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
h.makeReminder()
|
||||
h.makeReminderByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Get("/reminder/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 1)).
|
||||
End()
|
||||
}
|
||||
|
||||
// TestReminderListAssignable checks users that can assign reminders can also
|
||||
// list reminders of other users
|
||||
func TestReminderListAssignable(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "reminder.assign")
|
||||
|
||||
h.makeReminder()
|
||||
h.makeReminderByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Get("/reminder/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len("$.response.set", 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
// TestReminderReadAssignable checks users that can assign reminders can also
|
||||
// read reminders of other users
|
||||
func TestReminderReadAssignable(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "reminder.assign")
|
||||
|
||||
rm := h.makeReminderByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/reminder/%d", rm.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.resource`, rm.Resource)).
|
||||
End()
|
||||
}
|
||||
|
||||
// TestReminderDismissAssignable checks users that can assign reminders can also
|
||||
// dismiss reminders of other users
|
||||
func TestReminderDismissAssignable(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "reminder.assign")
|
||||
|
||||
rm := h.makeReminderByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/reminder/%d/dismiss", rm.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
// TestReminderSnoozeForbidden checks only user themself can snooze reminder
|
||||
// assigned to them
|
||||
func TestReminderSnoozeForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminderByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/reminder/%d/snooze", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("remindAt", time.Now().Add(time.Hour).Format(time.RFC3339)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("reminder.errors.notAllowedToSnooze")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderUpdateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminder()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/reminder/%d", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("resource", "changed:resource").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("reminder.errors.notAllowedToAssign")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "reminder.assign")
|
||||
|
||||
rm := h.makeReminder()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/reminder/%d", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("resource", "changed:resource").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.resource`, "changed:resource")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminder()
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/reminder/%d", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderDismiss(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminder()
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/reminder/%d/dismiss", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
// TestReminderDismissForbidden checks only user themself can dismiss reminder assigned to them
|
||||
func TestReminderDismissForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminderByUserID(id.Next())
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/reminder/%d/dismiss", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("reminder.errors.notAllowedToDismiss")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReminderSnooze(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReminders()
|
||||
|
||||
rm := h.makeReminder()
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/reminder/%d/snooze", rm.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("remindAt", time.Now().Format(time.RFC3339)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
210
server/tests/system/report_test.go
Normal file
210
server/tests/system/report_test.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) createReport(report *types.Report) *types.Report {
|
||||
if report.ID == 0 {
|
||||
report.ID = id.Next()
|
||||
}
|
||||
|
||||
if report.CreatedAt.IsZero() {
|
||||
report.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
h.a.NoError(service.DefaultStore.CreateReport(context.Background(), report))
|
||||
return report
|
||||
}
|
||||
|
||||
func (h helper) clearReports() {
|
||||
h.noError(store.TruncateReports(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) lookupReportByHandle(handle string) *types.Report {
|
||||
res, err := store.LookupReportByHandle(context.Background(), service.DefaultStore, handle)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestReportFilterCasting(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReports()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "report.create")
|
||||
|
||||
baseAssertPath := "$.response.sources[0].step.load.filter"
|
||||
|
||||
h.apiInit().
|
||||
Post("/reports/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{
|
||||
"handle": "test_report",
|
||||
"meta": { "name": "Test Report", "description": "" },
|
||||
"sources": [
|
||||
{
|
||||
"meta": {},
|
||||
"step": {
|
||||
"load": {
|
||||
"name": "Load",
|
||||
"source": "composeRecords",
|
||||
"definition": {
|
||||
"module": "test",
|
||||
"namespace": "test"
|
||||
},
|
||||
"columns": null,
|
||||
"filter": {
|
||||
"ref": "or",
|
||||
"args": [
|
||||
{
|
||||
"ref": "eq",
|
||||
"args": [
|
||||
{ "symbol": "id_str" },
|
||||
{
|
||||
"value": { "@type": "ID", "@value": "123123123" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ref": "eq",
|
||||
"args": [
|
||||
{ "symbol": "id_uint" },
|
||||
{
|
||||
"value": { "@type": "ID", "@value": 123123123 }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"blocks": []
|
||||
}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
// Strings handled
|
||||
Assert(jsonpath.Equal(baseAssertPath+`.args[0].args[1].value["@value"]`, "123123123")).
|
||||
Assert(jsonpath.Equal(baseAssertPath+`.args[0].args[0].symbol`, "id_str")).
|
||||
// uint handled
|
||||
Assert(jsonpath.Equal(baseAssertPath+`.args[1].args[1].value["@value"]`, "123123123")).
|
||||
Assert(jsonpath.Equal(baseAssertPath+`.args[1].args[0].symbol`, "id_uint")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReportCreate_invalidFilter(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReports()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "report.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/reports/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{
|
||||
"handle": "test_report",
|
||||
"meta": { "name": "Test Report", "description": "" },
|
||||
"sources": [
|
||||
{
|
||||
"meta": {},
|
||||
"step": {
|
||||
"load": {
|
||||
"name": "Load",
|
||||
"source": "composeRecords",
|
||||
"definition": {
|
||||
"module": "test",
|
||||
"namespace": "test"
|
||||
},
|
||||
"columns": null,
|
||||
"filter": {
|
||||
"ref": "or",
|
||||
"args": [
|
||||
{
|
||||
"ref": "eq",
|
||||
"args": [
|
||||
{ "symbol": "id_str" },
|
||||
{
|
||||
"value": { "@type": "", "@value": "" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"blocks": []
|
||||
}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestReportScenarios_create(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReports()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "report.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/reports/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{
|
||||
"handle": "test_report",
|
||||
"scenarios": [{ "label": "scenario 1", "filters": { "ds_1": { "raw": "field == 'value'" } } }]
|
||||
}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
r := h.lookupReportByHandle("test_report")
|
||||
h.a.NotNil(r)
|
||||
|
||||
h.a.Len(r.Scenarios, 1)
|
||||
sc := r.Scenarios[0]
|
||||
h.a.NotNil(sc.Filters)
|
||||
h.a.Contains(sc.Filters, "ds_1")
|
||||
}
|
||||
|
||||
func TestReportScenarios_update(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearReports()
|
||||
helpers.AllowMe(h, types.ReportRbacResource(0), "update")
|
||||
|
||||
r := h.createReport(&types.Report{
|
||||
Handle: "test_report",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/reports/%d", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{
|
||||
"handle": "test_report",
|
||||
"scenarios": [{ "label": "scenario 1", "filters": { "ds_1": { "raw": "field == 'value'" } } }]
|
||||
}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
r = h.lookupReportByHandle("test_report")
|
||||
h.a.NotNil(r)
|
||||
|
||||
h.a.Len(r.Scenarios, 1)
|
||||
sc := r.Scenarios[0]
|
||||
h.a.NotNil(sc.Filters)
|
||||
h.a.Contains(sc.Filters, "ds_1")
|
||||
}
|
||||
34
server/tests/system/resource_activity_log_test.go
Normal file
34
server/tests/system/resource_activity_log_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
discoveryType "github.com/cortezaproject/corteza/server/discovery/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func (h helper) clearActivityLog() {
|
||||
h.noError(store.TruncateResourceActivitys(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) repoMakeActivityLog() *discoveryType.ResourceActivity {
|
||||
var res = &discoveryType.ResourceActivity{
|
||||
ID: id.Next(),
|
||||
ResourceID: id.Next(),
|
||||
ResourceType: "compose:record",
|
||||
ResourceAction: "create",
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateResourceActivity(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func TestCreateActivityLog(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearActionLog()
|
||||
|
||||
h.repoMakeActivityLog()
|
||||
}
|
||||
560
server/tests/system/role_test.go
Normal file
560
server/tests/system/role_test.go
Normal file
@@ -0,0 +1,560 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
labelTypes "github.com/cortezaproject/corteza/server/pkg/label/types"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (h helper) clearRoles() {
|
||||
h.noError(store.TruncateRoles(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) clearRoleMembers() {
|
||||
h.noError(store.TruncateRoleMembers(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) createRole(res *types.Role) *types.Role {
|
||||
if res.ID == 0 {
|
||||
res.ID = id.Next()
|
||||
}
|
||||
|
||||
if res.CreatedAt.IsZero() {
|
||||
res.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
h.a.NoError(service.DefaultStore.CreateRole(context.Background(), res))
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) repoMakeRole(ss ...string) *types.Role {
|
||||
var r = &types.Role{
|
||||
ID: id.Next(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if len(ss) > 1 {
|
||||
r.Handle = ss[1]
|
||||
} else {
|
||||
r.Handle = "h_" + rs()
|
||||
|
||||
}
|
||||
if len(ss) > 0 {
|
||||
r.Name = ss[0]
|
||||
} else {
|
||||
r.Name = "n_" + rs()
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateRole(context.Background(), service.DefaultStore, r))
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (h helper) lookupRoleByID(ID uint64) *types.Role {
|
||||
res, err := store.LookupRoleByID(context.Background(), service.DefaultStore, ID)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) createRoleMember(userID, roleID uint64) *types.RoleMember {
|
||||
var r = &types.RoleMember{
|
||||
RoleID: roleID,
|
||||
Resource: fmt.Sprintf("corteza::system:user/%d", userID),
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateRoleMember(context.Background(), service.DefaultStore, r))
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func TestRoleRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
u := h.repoMakeRole()
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/roles/%d", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.name`, u.Name)).
|
||||
Assert(jsonpath.Equal(`$.response.roleID`, fmt.Sprintf("%d", u.ID))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestRoleList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
h.repoMakeRole(h.randEmail())
|
||||
h.repoMakeRole(h.randEmail())
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "roles.search")
|
||||
|
||||
h.apiInit().
|
||||
Get("/roles/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestRoleList_filterForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
// @todo this can be a problematic test because it leaves
|
||||
// behind roles that are not denied this context
|
||||
// db purge might be needed
|
||||
|
||||
h.repoMakeRole("role")
|
||||
f := h.repoMakeRole()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "roles.search")
|
||||
helpers.DenyMe(h, f.RbacResource(), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/roles/").
|
||||
Query("handle", f.Handle).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.NotPresent(fmt.Sprintf(`$.response.set[? @.handle=="%s"]`, f.Handle))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestRoleCreateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
h.apiInit().
|
||||
Post("/roles/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("name", rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.notAllowedToCreate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestRoleCreateNotUnique(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "role.create")
|
||||
|
||||
role := h.repoMakeRole()
|
||||
h.apiInit().
|
||||
Post("/roles/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("name", rs()).
|
||||
FormData("handle", role.Handle).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.handleNotUnique")).
|
||||
End()
|
||||
|
||||
h.apiInit().
|
||||
Post("/roles/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("name", role.Name).
|
||||
FormData("handle", "handle_"+rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.nameNotUnique")).
|
||||
End()
|
||||
|
||||
}
|
||||
|
||||
func TestRoleCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "role.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/roles/").
|
||||
FormData("name", rs()).
|
||||
FormData("handle", "handle_"+rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestRoleCreateWithJSON(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "role.create")
|
||||
|
||||
h.clearRoles()
|
||||
|
||||
h.apiInit().
|
||||
Post("/roles/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(fmt.Sprintf(`{
|
||||
"name": "fake role",
|
||||
"handle": "fakeRoleFromJSON",
|
||||
"meta": {
|
||||
"description": "this is my description",
|
||||
"context": {
|
||||
"resourceTypes": ["corteza::compose:record"],
|
||||
"expr": "userID == resource.ownedBy"
|
||||
}
|
||||
}
|
||||
}`)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
r, err := store.LookupRoleByHandle(context.Background(), service.DefaultStore, "fakeRoleFromJSON")
|
||||
h.a.NoError(err)
|
||||
h.a.NotNil(r.Meta)
|
||||
h.a.NotNil(r.Meta.Context)
|
||||
h.a.NotEmpty(r.Meta.Context.Resource)
|
||||
h.a.Equal("corteza::compose:record", r.Meta.Context.Resource[0])
|
||||
h.a.Equal("userID == resource.ownedBy", r.Meta.Context.Expr)
|
||||
}
|
||||
|
||||
func TestRoleUpdateWithJSON(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "role.create")
|
||||
|
||||
h.clearRoles()
|
||||
|
||||
h.noError(store.CreateRole(context.Background(), service.DefaultStore, &types.Role{
|
||||
ID: 42,
|
||||
Name: "fix",
|
||||
Handle: "fix",
|
||||
Meta: &types.RoleMeta{
|
||||
Description: "fix",
|
||||
Context: &types.RoleContext{
|
||||
Resource: []string{"corteza::compose:module"},
|
||||
Expr: "1!=2",
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
h.apiInit().
|
||||
Post("/roles/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(fmt.Sprintf(`{
|
||||
"name": "fake role",
|
||||
"handle": "fakeRoleFromJSON",
|
||||
"meta": {
|
||||
"description": "this is my description",
|
||||
"context": {
|
||||
"resourceTypes": ["corteza::compose:record"],
|
||||
"expr": "userID == resource.ownedBy"
|
||||
}
|
||||
}
|
||||
}`)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
r, err := store.LookupRoleByHandle(context.Background(), service.DefaultStore, "fakeRoleFromJSON")
|
||||
h.a.NoError(err)
|
||||
h.a.NotNil(r.Meta)
|
||||
h.a.NotNil(r.Meta.Context)
|
||||
h.a.NotEmpty(r.Meta.Context.Resource)
|
||||
h.a.Equal("corteza::compose:record", r.Meta.Context.Resource[0])
|
||||
h.a.Equal("userID == resource.ownedBy", r.Meta.Context.Expr)
|
||||
}
|
||||
|
||||
func TestRoleUpdateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
u := h.repoMakeRole()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/roles/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("email", h.randEmail()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestRoleUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
res := h.repoMakeRole()
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "update")
|
||||
|
||||
newName := "updated-" + rs()
|
||||
newHandle := "updated-" + rs()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/roles/%d", res.ID)).
|
||||
FormData("name", newName).
|
||||
FormData("handle", newHandle).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupRoleByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(newName, res.Name)
|
||||
h.a.Equal(newHandle, res.Handle)
|
||||
}
|
||||
|
||||
func TestRoleDeleteForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
u := h.repoMakeRole()
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/roles/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.notAllowedToDelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestRoleDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "delete")
|
||||
|
||||
res := h.repoMakeRole()
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/roles/%d", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupRoleByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.NotNil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestRoleUndelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "delete")
|
||||
|
||||
res := h.repoMakeRole()
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/undelete", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupRoleByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Nil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestRoleArchive(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "update")
|
||||
|
||||
res := h.repoMakeRole()
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/archive", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupRoleByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.NotNil(res.ArchivedAt)
|
||||
}
|
||||
|
||||
func TestRoleUnarchive(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "delete")
|
||||
|
||||
n := time.Now()
|
||||
res := h.createRole(&types.Role{
|
||||
Name: "test_role",
|
||||
Handle: "test_role",
|
||||
ArchivedAt: &n,
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/unarchive", res.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupRoleByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Nil(res.ArchivedAt)
|
||||
}
|
||||
|
||||
func TestRoleLabels(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoles()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "roles.search")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "role.create")
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "read", "update", "delete")
|
||||
|
||||
var (
|
||||
ID uint64
|
||||
)
|
||||
|
||||
t.Run("create", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
payload = &types.Role{}
|
||||
)
|
||||
|
||||
helpers.SetLabelsViaAPI(h.apiInit(), t,
|
||||
"/roles/",
|
||||
types.Role{Labels: map[string]labelTypes.LabelValue{"foo": {Val: "bar"}, "bar": {Val: "42"}}},
|
||||
payload,
|
||||
)
|
||||
req.NotZero(payload.ID)
|
||||
|
||||
h.a.Equal(payload.Labels["foo"].Val, "bar",
|
||||
"labels must contain foo with value bar")
|
||||
h.a.Equal(payload.Labels["bar"].Val, "42",
|
||||
"labels must contain bar with value 42")
|
||||
req.Equal(helpers.LabelsToStrings(payload.Labels), helpers.LabelsToStrings(helpers.LoadLabelsFromStore(t, service.DefaultStore, payload.LabelResourceKind(), payload.ID)),
|
||||
"response must match stored labels")
|
||||
|
||||
ID = payload.ID
|
||||
})
|
||||
|
||||
t.Run("update", func(t *testing.T) {
|
||||
if ID == 0 {
|
||||
t.Skip("label/create test not ran")
|
||||
}
|
||||
|
||||
var (
|
||||
req = require.New(t)
|
||||
payload = &types.Role{}
|
||||
)
|
||||
|
||||
helpers.SetLabelsViaAPI(h.apiInit(), t,
|
||||
fmt.Sprintf("PUT /roles/%d", ID),
|
||||
&types.Role{Labels: map[string]labelTypes.LabelValue{"foo": {Val: "baz"}, "baz": {Val: "123"}}},
|
||||
payload,
|
||||
)
|
||||
req.NotZero(payload.ID)
|
||||
//req.Nil(payload.UpdatedAt, "updatedAt must not change after changing labels")
|
||||
|
||||
req.Equal(payload.Labels["foo"].Val, "baz",
|
||||
"labels must contain foo with value baz")
|
||||
req.NotContains(payload.Labels, "bar",
|
||||
"labels must not contain bar")
|
||||
req.Equal(payload.Labels["baz"].Val, "123",
|
||||
"labels must contain baz with value 123")
|
||||
req.Equal(helpers.LabelsToStrings(payload.Labels), helpers.LabelsToStrings(helpers.LoadLabelsFromStore(t, service.DefaultStore, payload.LabelResourceKind(), payload.ID)),
|
||||
"response must match stored labels")
|
||||
})
|
||||
|
||||
t.Run("search", func(t *testing.T) {
|
||||
if ID == 0 {
|
||||
t.Skip("label/create test not ran")
|
||||
}
|
||||
|
||||
var (
|
||||
req = require.New(t)
|
||||
set = types.RoleSet{}
|
||||
)
|
||||
|
||||
helpers.SearchWithLabelsViaAPI(h.apiInit(), t, "/roles/", &set, url.Values{"labels": []string{"baz=123"}})
|
||||
req.NotEmpty(set)
|
||||
req.NotNil(set.FindByID(ID))
|
||||
req.NotNil(set.FindByID(ID).Labels)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMemberList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "read")
|
||||
|
||||
r := h.repoMakeRole(h.randEmail())
|
||||
h.createRoleMember(id.Next(), r.ID)
|
||||
h.createRoleMember(id.Next(), r.ID)
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/roles/%d/members", r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestMemberAdd(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "members.manage")
|
||||
|
||||
r := h.repoMakeRole(h.randEmail())
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/member/%d", r.ID, u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestMemberAdd_denied(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
r := h.repoMakeRole(h.randEmail())
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/member/%d", r.ID, u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.notAllowedToManageMembers")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestMemberRemove(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "members.manage")
|
||||
|
||||
r := h.repoMakeRole(h.randEmail())
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/member/%d", r.ID, u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestMemberRemove_denied(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
r := h.repoMakeRole(h.randEmail())
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/roles/%d/member/%d", r.ID, u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("role.errors.notAllowedToManageMembers")).
|
||||
End()
|
||||
}
|
||||
399
server/tests/system/scim_test.go
Normal file
399
server/tests/system/scim_test.go
Normal file
@@ -0,0 +1,399 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/api/server"
|
||||
"github.com/cortezaproject/corteza/server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/scim"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/steinfletcher/apitest"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
// apitest basics, initialize, set handler, add auth
|
||||
func (h helper) scimApiInit(ffn ...func(*scim.Config)) *apitest.APITest {
|
||||
InitTestApp()
|
||||
var (
|
||||
scimConfig scim.Config
|
||||
scimRoutes = chi.NewRouter()
|
||||
)
|
||||
|
||||
for _, fn := range ffn {
|
||||
fn(&scimConfig)
|
||||
}
|
||||
|
||||
scimRoutes.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
scim.Routes(scimRoutes, scimConfig)
|
||||
|
||||
return apitest.
|
||||
New().
|
||||
Handler(scimRoutes)
|
||||
}
|
||||
|
||||
func TestScimUserGet(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.scimApiInit().
|
||||
Get(fmt.Sprintf("/Users/%d", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(jsonpath.Contains(`$.schemas`, "urn:ietf:params:scim:schemas:core:2.0:User")).
|
||||
Assert(jsonpath.Equal(`$.id`, fmt.Sprintf("%d", u.ID))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestScimUserCreate(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Users").
|
||||
JSON(`{
|
||||
"schemas": [
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User"
|
||||
],
|
||||
"userName": "foo",
|
||||
"nickName": "baz",
|
||||
"emails": [
|
||||
{
|
||||
"value": "foo@bar.com",
|
||||
"primary": true
|
||||
},
|
||||
{
|
||||
"value": "bar@foo.com"
|
||||
}
|
||||
]
|
||||
}`).
|
||||
Expect(t).
|
||||
Status(http.StatusCreated).
|
||||
End()
|
||||
|
||||
u, err := store.LookupUserByEmail(context.Background(), service.DefaultStore, "foo@bar.com")
|
||||
h.a.NoError(err)
|
||||
h.a.Equal("foo", u.Username)
|
||||
h.a.Equal("baz", u.Handle)
|
||||
}
|
||||
|
||||
func TestScimUserCreateNoEmail(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Users").
|
||||
JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusInternalServerError).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestScimUserCreateOverwrite(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail("foo@bar.com")
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Users").
|
||||
JSON(`{"userName":"UPDATED","emails":[{"value":"foo@bar.com"}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
End()
|
||||
|
||||
u, err := store.LookupUserByEmail(context.Background(), service.DefaultStore, "foo@bar.com")
|
||||
h.a.NoError(err)
|
||||
h.a.Equal("UPDATED", u.Username)
|
||||
}
|
||||
|
||||
func TestScimUserExternalID(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Users").
|
||||
JSON(`{"userName":"foo","emails":[{"value":"foo@bar.com"}],"externalId":"foo42","schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusCreated).
|
||||
End()
|
||||
|
||||
u, err := store.LookupUserByEmail(context.Background(), service.DefaultStore, "foo@bar.com")
|
||||
h.a.NoError(err)
|
||||
h.a.Equal("foo", u.Username)
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Users").
|
||||
JSON(`{"userName":"baz","emails":[{"value":"baz@bar.com"}],"externalId":"foo42","schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
End()
|
||||
|
||||
u, err = store.LookupUserByEmail(context.Background(), service.DefaultStore, "baz@bar.com")
|
||||
h.a.NoError(err)
|
||||
h.a.Equal("baz", u.Username)
|
||||
|
||||
}
|
||||
|
||||
func TestScimUserReplace(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.scimApiInit().
|
||||
Put(fmt.Sprintf("/Users/%d", u.ID)).
|
||||
JSON(`{
|
||||
"schemas": [
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User"
|
||||
],
|
||||
"userName": "bar",
|
||||
"emails": [
|
||||
{
|
||||
"value": "foo@bar.com"
|
||||
}
|
||||
]
|
||||
}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
End()
|
||||
|
||||
u, err := store.LookupUserByID(context.Background(), service.DefaultStore, u.ID)
|
||||
h.a.NoError(err)
|
||||
h.a.NotNil(u)
|
||||
h.a.Equal("foo@bar.com", u.Email)
|
||||
}
|
||||
|
||||
func TestScimUserPassword(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
service.CurrentSettings.Auth.Internal.Enabled = true
|
||||
auth := service.Auth(service.AuthOptions{})
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Users").
|
||||
JSON(`{"password":"foo$bar$baz 42","emails":[{"value":"baz@bar.com"}],"externalId":"foo42","schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusCreated).
|
||||
End()
|
||||
|
||||
u, err := auth.InternalLogin(context.Background(), "baz@bar.com", "foo$bar$baz 42")
|
||||
h.a.NoError(err)
|
||||
h.a.NotNil(u)
|
||||
}
|
||||
|
||||
func TestScimUserDelete(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.scimApiInit().
|
||||
Delete(fmt.Sprintf("/Users/%d", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusNoContent).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestScimGroupGet(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearRoles()
|
||||
|
||||
u := h.repoMakeRole()
|
||||
|
||||
h.scimApiInit().
|
||||
Get(fmt.Sprintf("/Groups/%d", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(jsonpath.Contains(`$.schemas`, "urn:ietf:params:scim:schemas:core:2.0:Group")).
|
||||
Assert(jsonpath.Equal(`$.id`, fmt.Sprintf("%d", u.ID))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestScimGroupCreate(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearRoles()
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Groups").
|
||||
JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"foo"}`).
|
||||
Expect(t).
|
||||
Status(http.StatusCreated).
|
||||
End()
|
||||
|
||||
u, err := store.LookupRoleByName(context.Background(), service.DefaultStore, "foo")
|
||||
h.a.NoError(err)
|
||||
h.a.Equal("foo", u.Name)
|
||||
}
|
||||
|
||||
func TestScimGroupExternalId(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearRoles()
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Groups").
|
||||
JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"foo","externalId":"grp42"}`).
|
||||
Expect(t).
|
||||
Status(http.StatusCreated).
|
||||
End()
|
||||
|
||||
u, err := store.LookupRoleByName(context.Background(), service.DefaultStore, "foo")
|
||||
h.a.NoError(err)
|
||||
h.a.Equal("foo", u.Name)
|
||||
|
||||
h.scimApiInit().
|
||||
Post("/Groups").
|
||||
JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"bar","externalId":"grp42"}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
End()
|
||||
|
||||
u, err = store.LookupRoleByName(context.Background(), service.DefaultStore, "bar")
|
||||
h.a.NoError(err)
|
||||
h.a.Equal("bar", u.Name)
|
||||
}
|
||||
|
||||
func TestScimGroupReplace(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearRoles()
|
||||
|
||||
u := h.repoMakeRole()
|
||||
|
||||
h.scimApiInit().
|
||||
Put(fmt.Sprintf("/Groups/%d", u.ID)).
|
||||
JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"bar"}`).
|
||||
Expect(t).
|
||||
End()
|
||||
|
||||
u, err := store.LookupRoleByID(context.Background(), service.DefaultStore, u.ID)
|
||||
h.a.NoError(err)
|
||||
h.a.NotNil(u)
|
||||
h.a.Equal("bar", u.Name)
|
||||
}
|
||||
|
||||
func TestScimGroupDelete(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearRoles()
|
||||
|
||||
u := h.repoMakeRole(h.randEmail())
|
||||
|
||||
h.scimApiInit().
|
||||
Delete(fmt.Sprintf("/Groups/%d", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusNoContent).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestScimUserReplaceOnExternalId(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
// creating a new user and assigning an external ID label to it
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
const externalId = `2819c223-7f76-453a-919d-413861904646`
|
||||
h.setLabel(u, "SCIM_externalId", externalId)
|
||||
|
||||
h.scimApiInit(scimSetWithExternalId, scimSetWithUUIDValidator).
|
||||
Put(fmt.Sprintf("/Users/%s", externalId)).
|
||||
JSON(`{"emails":[{"value":"baz@bar.com"}],"externalId":"` + externalId + `","schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
End()
|
||||
|
||||
u, err := store.LookupUserByID(context.Background(), service.DefaultStore, u.ID)
|
||||
h.a.NoError(err)
|
||||
h.a.NotNil(u)
|
||||
h.a.Equal("baz@bar.com", u.Email)
|
||||
}
|
||||
|
||||
func TestScimPatchingGroupMembership(t *testing.T) {
|
||||
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
h.clearRoles()
|
||||
h.clearRoleMembers()
|
||||
|
||||
isMember := func(r *types.Role, u *types.User) bool {
|
||||
mm, _, err := store.SearchRoleMembers(h.secCtx(), service.DefaultStore, types.RoleMemberFilter{RoleID: r.ID, Resource: fmt.Sprintf("corteza::system:user/%d", u.ID)})
|
||||
h.a.NoError(err)
|
||||
return len(mm) > 0
|
||||
}
|
||||
|
||||
const (
|
||||
user1Id = `00000000-0000-0000-0000-000000000001`
|
||||
user2Id = `00000000-0000-0000-0000-000000000002`
|
||||
groupId = `00000000-0000-0000-0001-000000000001`
|
||||
)
|
||||
|
||||
// creating a new user and assigning an external ID label to it
|
||||
u1 := h.createUserWithEmail(h.randEmail())
|
||||
h.setLabel(u1, "SCIM_externalId", user1Id)
|
||||
|
||||
u2 := h.createUserWithEmail(h.randEmail())
|
||||
h.setLabel(u2, "SCIM_externalId", user2Id)
|
||||
|
||||
r := h.createRole(&types.Role{})
|
||||
h.setLabel(r, "SCIM_externalId", groupId)
|
||||
|
||||
h.a.False(isMember(r, u1))
|
||||
h.a.False(isMember(r, u2))
|
||||
|
||||
// add only user #1
|
||||
h.scimApiInit(scimSetWithExternalId, scimSetWithUUIDValidator).
|
||||
Patch(fmt.Sprintf("/Groups/%s", groupId)).
|
||||
JSON(fmt.Sprintf(
|
||||
`{"Operations":[{"op":"add","path":"members[value eq \"%s\"]"}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:PatchOp"]}`,
|
||||
user1Id,
|
||||
)).
|
||||
Expect(t).
|
||||
Status(http.StatusNoContent).
|
||||
End()
|
||||
|
||||
h.a.True(isMember(r, u1))
|
||||
h.a.False(isMember(r, u2))
|
||||
|
||||
// remove user #1, add user #2
|
||||
h.scimApiInit(scimSetWithExternalId, scimSetWithUUIDValidator).
|
||||
Patch(fmt.Sprintf("/Groups/%s", groupId)).
|
||||
JSON(fmt.Sprintf(
|
||||
`{"Operations":[{"op":"add","path":"members[value eq \"%[1]s\"]"},{"op":"remove","path":"members[value eq \"%[2]s\"]"}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:PatchOp"]}`,
|
||||
user2Id,
|
||||
user1Id,
|
||||
)).
|
||||
Expect(t).
|
||||
Status(http.StatusNoContent).
|
||||
End()
|
||||
|
||||
h.a.False(isMember(r, u1))
|
||||
h.a.True(isMember(r, u2))
|
||||
|
||||
}
|
||||
|
||||
func scimSetWithExternalId(c *scim.Config) {
|
||||
c.ExternalIdAsPrimary = true
|
||||
}
|
||||
func scimSetWithUUIDValidator(c *scim.Config) {
|
||||
c.ExternalIdValidator = regexp.MustCompile(`^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$`)
|
||||
}
|
||||
242
server/tests/system/settings_test.go
Normal file
242
server/tests/system/settings_test.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
sqlTypes "github.com/jmoiron/sqlx/types"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func TestSettingsList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.manage")
|
||||
|
||||
err := service.DefaultSettings.BulkSet(h.secCtx(), types.SettingValueSet{
|
||||
&types.SettingValue{Name: "t_sys_k1.s1", Value: sqlTypes.JSONText(`"t_sys_v1"`)},
|
||||
&types.SettingValue{Name: "t_sys_k1.s2", Value: sqlTypes.JSONText(`"t_sys_v2"`)},
|
||||
})
|
||||
h.a.NoError(err)
|
||||
|
||||
h.apiInit().
|
||||
Get("/settings/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response[? @.name=="t_sys_k1.s1"]`)).
|
||||
Assert(jsonpath.Present(`$.response[? @.value=="t_sys_v1"]`)).
|
||||
Assert(jsonpath.Present(`$.response[? @.name=="t_sys_k1.s2"]`)).
|
||||
Assert(jsonpath.Present(`$.response[? @.value=="t_sys_v2"]`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestSettingsList_noPermissions(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestSettingsUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.manage")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
|
||||
err := service.DefaultSettings.BulkSet(h.secCtx(), types.SettingValueSet{
|
||||
&types.SettingValue{Name: "t_sys_k1.s1", Value: sqlTypes.JSONText(`"t_sys_v1"`)},
|
||||
})
|
||||
h.a.NoError(err)
|
||||
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
JSON(`{"values":[{"name":"t_sys_k1.s1","value":"t_sys_v1_edited"},{"name":"t_sys_k2.s1","value":"t_sys_v2_new"}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
s, err := service.DefaultSettings.Get(h.secCtx(), "t_sys_k1.s1", 0)
|
||||
h.a.NoError(err)
|
||||
h.a.Equal(`"t_sys_v1_edited"`, s.Value.String(), "existing key should be updated")
|
||||
|
||||
s, err = service.DefaultSettings.Get(h.secCtx(), "t_sys_k2.s1", 0)
|
||||
h.a.NoError(err)
|
||||
h.a.Equal(`"t_sys_v2_new"`, s.Value.String(), "new key should be added")
|
||||
}
|
||||
|
||||
func TestSettingsUpdate_noPermissions(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "settings.manage")
|
||||
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.notAllowedToManage")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestSettingsUpdate_validation(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.manage")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
|
||||
// Password constraints: The min password length should be 8 or more
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-length","value": 8}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Password constraints: The min password length should be not be less than 8
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-length","value":"7"}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.invalidPasswordMinLength")).
|
||||
End()
|
||||
|
||||
// Password constraints: The min upper case count
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-upper-case","value": 2}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Password constraints: The min upper case count should not be a negative number
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-upper-case","value":-1}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.invalidPasswordMinUpperCase")).
|
||||
End()
|
||||
|
||||
// Password constraints: The min upper case count should not be a negative number string
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-upper-case","value":"-1"}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.invalidPasswordMinUpperCase")).
|
||||
End()
|
||||
|
||||
// Password constraints: The min lower case count should not be a negative number
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-lower-case","value":"-1"}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.invalidPasswordMinLowerCase")).
|
||||
End()
|
||||
|
||||
// Password constraints: The min number of numeric characters should not be a negative number
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-num-count","value":"-1"}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.invalidPasswordMinNumCount")).
|
||||
End()
|
||||
|
||||
// Password constraints: The min number of special characters should not be a negative number
|
||||
h.apiInit().
|
||||
Patch("/settings/").
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"values":[{"name":"auth.internal.password-constraints.min-special-count","value":"-1"}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.invalidPasswordMinSpecialCharCount")).
|
||||
End()
|
||||
|
||||
}
|
||||
|
||||
func TestSettingsGet(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.manage")
|
||||
|
||||
err := service.DefaultSettings.BulkSet(h.secCtx(), types.SettingValueSet{
|
||||
&types.SettingValue{Name: "t_sys_k1.s1", Value: sqlTypes.JSONText(`"t_sys_v1"`)},
|
||||
})
|
||||
h.a.NoError(err)
|
||||
|
||||
h.apiInit().
|
||||
Get("/settings/t_sys_k1.s1").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.name=="t_sys_k1.s1"`)).
|
||||
Assert(jsonpath.Present(`$.response.value=="t_sys_v1"`)).
|
||||
End()
|
||||
|
||||
h.apiInit().
|
||||
Get("/settings/t_sys_k1.missing").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response`, nil)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestSettingsGet_noPermissions(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/settings/t_sys_k1.s1").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestSettingsSet_noPermissions(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/settings/t_sys_k1.s1").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("settings.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestSettingsCurrent(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "settings.read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/settings/current").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(jsonpath.Present(`$.response`)).
|
||||
End()
|
||||
}
|
||||
1
server/tests/system/static/idp_metadata.xml
Normal file
1
server/tests/system/static/idp_metadata.xml
Normal file
File diff suppressed because one or more lines are too long
1
server/tests/system/static/idp_to_sp.cookie
Normal file
1
server/tests/system/static/idp_to_sp.cookie
Normal file
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODQiLCJpYXQiOjE2MjEyNDI5OTIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4NCIsIm5iZiI6MTYyMTI0Mjk5Miwic3ViIjoidEN1NVBWNkVneGN2VUFhOWU1N3VKMmctYlRrcW5Oa3l5SEhhT3UxNXlFZlpqZ1dLdDAyQXRYR2UiLCJpZCI6ImlkLTdiZmZiMjZkNmJlZTRiNWJkZjg2ZjhkYzRkYTJjZmMxMTdkODlkMGYiLCJ1cmkiOiIvYXV0aC9leHRlcm5hbC9zYW1sL2luaXQiLCJzYW1sLWF1dGhuLXJlcXVlc3QiOnRydWV9.fBI_TorEbXYMtoNRGApQs5_89Q9IZjV-1dwkOeF5ZC9xQ6p3Mbo3r0x4CgKzYS2n8i4mMIEUDI_C4bY5jqyVEfmrwtv4qGbhYCJjnvSu1vAncJGQNfbcWCmSW0RMiiJZzfj3whHTzmK_mLgOch07iwxKGBOyNscdZfxfJp_sDMuHePwiggGssWglCC_KWXNkGh3TPad-_mo4kc_9qUf4onyISms6uyZpbJW-BqGP-iYiTnbEGdbF-24bmbTpVBU8Arv3jQjaHq8teT9XI4vFgWHfEp497LD7snYNOn3-9S05JGWKA74wrgZwFcRBaIhfVMtOqy7YMHBJZ8NNbVH8Tg
|
||||
1
server/tests/system/static/idp_to_sp.post
Normal file
1
server/tests/system/static/idp_to_sp.post
Normal file
File diff suppressed because one or more lines are too long
1
server/tests/system/static/idp_to_sp_token.cookie
Normal file
1
server/tests/system/static/idp_to_sp_token.cookie
Normal file
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODQiLCJleHAiOjE2MjEyNDY2MzAsImlhdCI6MTYyMTI0MzAzMCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg0IiwibmJmIjoxNjIxMjQzMDMwLCJzdWIiOiJBQWR6WldOeVpYUXhFMmR5SFBqRStoTVhQQW5uSUNpbUt2c21XeC9YMW9MaEFOV1dGbU0wWElMS01xa1dBWjFrVldiR0J0ZjhFS0xsb2wvakM2ZlJMa25Cc3dZRDlWNUxHQUErcDFxZHBON2w2MGI3ZHYvMFcwa25VRm1SSm8vWFlTYmRkTVFweUtBMWlCNVFrejZERDVsS0Ixa2VTMzBiYWpuT29OTT0iLCJhdHRyIjp7IlNlc3Npb25JbmRleCI6WyJfYzA5YWFiNDRhNTUyOGVhNzM2NTdkNTNjYTRmODA4NDMiXSwiZGlzcGxheU5hbWUiOlsiUmljayBTYW5jaGV6Il0sImVkdVBlcnNvbkVudGl0bGVtZW50IjpbInVybjptYWNlOmRpcjplbnRpdGxlbWVudDpjb21tb24tbGliLXRlcm1zIl0sImdpdmVuTmFtZSI6WyJSaWNrIl0sIm1haWwiOlsicnNhbmNoZXpAc2FtbHRlc3QuaWQiXSwicm9sZSI6WyJtYW5hZ2VyQHNhbWx0ZXN0LmlkIl0sInNuIjpbIlNhbmNoZXoiXSwidGVsZXBob25lTnVtYmVyIjpbIisxLTU1NS01NTUtNTUxNSJdLCJ1aWQiOlsicmljayJdLCJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDphdHRyaWJ1dGU6c3ViamVjdC1pZCI6WyJyc2FuY2hlekBzYW1sdGVzdC5pZCJdfSwic2FtbC1zZXNzaW9uIjp0cnVlfQ.bNYxkq2dnISDr4x0Wg9pBXcOsSpKEvf5Bb_1vfZDLax6yH2Jjn0Dwjn7LMI6rRcz31YdMkhxrc9sISDD4Peotc1q_QNmJ92xCCzDq9jZM7ptRpKHZJIWkgwUDmmsLjCWj_YiuOwsPP6Zt70AJXNUtOcBg04Dw5ncpcR1cRqf6rTP7rAdNjQ_Euitln1gHK4Zz-fH0GgtS_pYHIzKTRarxiEktMvQGiaNce5e-1DmMOS3L9_6-lSlTa4sYG9BVG4icFf3QnUXqc3DCgGQr7waMuZNA35jqSJ1MSwhLVSHV5gb_l3HxoHKwZhue5d874apTk8cX00wlDO0Q9Z1z2R9Pg
|
||||
19
server/tests/system/static/spCert.cert
Normal file
19
server/tests/system/static/spCert.cert
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAgmgAwIBAgIUB34XNoIgt3k4tLbC40GDnLuvYAkwDQYJKoZIhvcNAQEL
|
||||
BQAwIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1wbGUuY29tMB4XDTIxMDQyMjE3
|
||||
MzIzOVoXDTIyMDQyMjE3MzIzOVowIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1w
|
||||
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp9jElUsjM3We
|
||||
UHA9CjGQLoQJlhNOqkv2oKZBvTpr8bAQF40vP9qGiwrZqRe0se1xCTmlVM/HGrct
|
||||
C0I6aL7+WTamNEvwPimoWsk1V7zYY7cFW+JEmL/E379R+9giQLBxIvvg7hSd7icl
|
||||
ucwG5V6WPenQiyneEegTsEEgKqagIAgSV9zYjNQrQes73WHBj8VCZhTsVEnVO+/V
|
||||
mCJaEwwovZWP0loLebR29/I4gr/LmfQfrIA+j116o5zp44vl1yW98xq5snkgb4YB
|
||||
fm6pTHDl2rn4pM3avL+sK1usz7HQwmKrW6cpLtKVc+Sbjf3MyLUuHxRmkPoY2J56
|
||||
uJ1OWChoYwIDAQABo1MwUTAdBgNVHQ4EFgQUTjg/FEKaUjpqzm3KzCg+WMUsm2ow
|
||||
HwYDVR0jBBgwFoAUTjg/FEKaUjpqzm3KzCg+WMUsm2owDwYDVR0TAQH/BAUwAwEB
|
||||
/zANBgkqhkiG9w0BAQsFAAOCAQEALXtIWymIgZDMKHlVzMWq/Id/jP2aI0hpvZgS
|
||||
2Lv7UDdyF1bj1MDTOtDkERoTVh/Xbhao5ls+GVhxvudjnZ8irdtlQRJNWTgj58Zi
|
||||
zagO3StQ2duwZPyvt9jHgVJ8Jm9w8JVgsNP4Notl5Gj7dMGhxnm4aA2oHScJoC5g
|
||||
u2ukGAAxgUpjNA7j9C/7iVLzijFzhztq7XTmbnJmuFPR50rqHJcTTYMVDc8Pb73c
|
||||
mjDZDz9U91v+7j5oDLV/8khvLsHxi85/GA1+j7z77wWzrIwwNjY4YfbhF0HJZXQz
|
||||
Xf3EHGxtSC01hJyfBjyXh5H8+NbyAYF8+d8y+uFu+GHhLS+Yag==
|
||||
-----END CERTIFICATE-----
|
||||
28
server/tests/system/static/spCert.key
Normal file
28
server/tests/system/static/spCert.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCn2MSVSyMzdZ5Q
|
||||
cD0KMZAuhAmWE06qS/agpkG9OmvxsBAXjS8/2oaLCtmpF7Sx7XEJOaVUz8caty0L
|
||||
Qjpovv5ZNqY0S/A+KahayTVXvNhjtwVb4kSYv8Tfv1H72CJAsHEi++DuFJ3uJyW5
|
||||
zAblXpY96dCLKd4R6BOwQSAqpqAgCBJX3NiM1CtB6zvdYcGPxUJmFOxUSdU779WY
|
||||
IloTDCi9lY/SWgt5tHb38jiCv8uZ9B+sgD6PXXqjnOnji+XXJb3zGrmyeSBvhgF+
|
||||
bqlMcOXaufikzdq8v6wrW6zPsdDCYqtbpyku0pVz5JuN/czItS4fFGaQ+hjYnnq4
|
||||
nU5YKGhjAgMBAAECggEAPlzd/ZJjS9VhswVgyI7NwVqxrR8TVVbQFbRwLHyuaqg9
|
||||
8mI0sgbhgnvPj3INYyaTnxfaA/8HPTfd9pbu2MhN/Ju/eSLV6mLT+JdVyHmT9Mil
|
||||
pxQU5KQr4+5T6bzOTTbBcnwfgJYMb9X/wF68GTDhpbNgFrTBm+mclxo7d11dlUiQ
|
||||
bdA2KBhIM54kKcWEO90AP2FiC/cRSbv8afHVb0Fxy1SBAGVDSscUaEwgyHkRf6H7
|
||||
Mmt4xhfBwLZZk5pVa97pZsas7L0sm4BtyYSs4elJesuo3DTpezZSzWHNreJcCL00
|
||||
v7pm9ZWv9PYeUcQqSw6Ws2fhsydiN93o8s/fPpF0IQKBgQDQqvKPjGo3HLRnBmpc
|
||||
nhUVQgjdh/QobJCGBjdxR/C3NkcatNLONddhefqovfb2jITdHmRKW+7XgRq/q457
|
||||
mZ2JPvvryiWcVqf6D91tSoFYrVg5nU1zr6nTga1BpTH00/eNEXxjMAAguPj3m7Sw
|
||||
3BP7L8yZZI1pOOjzVzuISEDlGwKBgQDN62ZXzRqorn5t+mDywTAJ2qAyZzoDAtUP
|
||||
CjANOKBCECqE9cOW1qBDJcaEF7G+xyB22CMciOELjGXWw6ZwFWjc23tDCEkoHoia
|
||||
5IWvwNs5ncdPCuOm3aay9w2dx9Idy+Eq5Vpe4XDlnZvEfzjwXX3gbQ5YS9kbNE2r
|
||||
XtmgD/BmWQKBgBNKqrBA0BUWT0tzGWRErThQ6ZbpmdYe62Gos3mCqCuYFgzPCOpN
|
||||
qgL2DwmIvotexG3ZAHardzJvWjS8PKkKs7jbnNjY0I9ap58D1nnjOIAlTpHNDDsU
|
||||
04OdapI2Hp8+9ZUSN8jHyEs+Lq5ds9/iCOrhKW5JEJXY0BinSPa5j15fAoGBAM2I
|
||||
dqCQollXwe34CaiD13UeeOOWUTsMKqlWW9v2d085X5dSzyTRmSksnVbfZ5SqoOa+
|
||||
mV0z6pxiSIvywUACvqYjlIa10H9w6pzgF+fzMV3y9CsbDVtSxb7ABSFFf54qD9eH
|
||||
EYq+rrchd4bMDYMtbiUB9V2AZ3VV4Wh5xfKTtjoRAoGAec/nBmX/oHiG/3T2LEmo
|
||||
bTT+/eXc4LUzgHvFG6HW40PI8T2TfMTUGQH+90zkraryUF5PNoUF58tooDAcMWXm
|
||||
DPEHFHeWn8T4obfuUjTw1mwj7Xjzr40HDitjIQa0bvBVwdYyEgiEw1CaeaUpq9Z8
|
||||
ElGWoRHT87pfoZjbdPz7a4Y=
|
||||
-----END PRIVATE KEY-----
|
||||
357
server/tests/system/template_test.go
Normal file
357
server/tests/system/template_test.go
Normal file
@@ -0,0 +1,357 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
)
|
||||
|
||||
func (h helper) repoMakeTemplate(ss ...string) *types.Template {
|
||||
var res = &types.Template{
|
||||
ID: id.Next(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if len(ss) > 0 {
|
||||
res.Handle = ss[0]
|
||||
} else {
|
||||
res.Handle = "n_" + rs()
|
||||
}
|
||||
if len(ss) > 1 {
|
||||
res.Template = ss[1]
|
||||
}
|
||||
if len(ss) > 2 {
|
||||
res.Type = types.DocumentType(ss[2])
|
||||
}
|
||||
|
||||
h.a.NoError(store.CreateTemplate(context.Background(), service.DefaultStore, res))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (h helper) lookupTemplateByID(ID uint64) *types.Template {
|
||||
res, err := store.LookupTemplateByID(context.Background(), service.DefaultStore, ID)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestTemplateRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "read")
|
||||
|
||||
u := h.repoMakeTemplate()
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/template/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.handle`, u.Handle)).
|
||||
Assert(jsonpath.Equal(`$.response.templateID`, fmt.Sprintf("%d", u.ID))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "read")
|
||||
|
||||
h.repoMakeTemplate(rs())
|
||||
h.repoMakeTemplate(rs())
|
||||
|
||||
h.apiInit().
|
||||
Get("/template/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response.set`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateList_filterForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
|
||||
// @todo this can be a problematic test because it leaves
|
||||
// behind templates that are not denied this context
|
||||
// db purge might be needed
|
||||
|
||||
h.repoMakeTemplate("template")
|
||||
f := h.repoMakeTemplate()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.DenyMe(h, f.RbacResource(), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/template/").
|
||||
Header("Accept", "application/json").
|
||||
Query("handle", f.Handle).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.NotPresent(fmt.Sprintf(`$.response.set[? @.handle=="%s"]`, f.Handle))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateCreateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
|
||||
h.apiInit().
|
||||
Post("/template/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("meta", fmt.Sprintf(`{"short": "%s"}`, rs())).
|
||||
FormData("handle", "a"+rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("template.errors.notAllowedToCreate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "template.create")
|
||||
|
||||
h.apiInit().
|
||||
Post("/template/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("meta", fmt.Sprintf(`{"short": "%s"}`, rs())).
|
||||
FormData("handle", "handle_"+rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateUpdateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
u := h.repoMakeTemplate()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/template/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("meta", fmt.Sprintf(`{"short": "%s"}`, rs())).
|
||||
FormData("handle", "handle_"+rs()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("template.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
res := h.repoMakeTemplate()
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "update")
|
||||
|
||||
newHandle := "updated-" + rs()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/template/%d", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("meta", fmt.Sprintf(`{"short": "%s"}`, rs())).
|
||||
FormData("handle", newHandle).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupTemplateByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Equal(newHandle, res.Handle)
|
||||
}
|
||||
|
||||
func TestTemplateDeleteForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
u := h.repoMakeTemplate()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/template/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("template.errors.notAllowedToDelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "delete")
|
||||
|
||||
res := h.repoMakeTemplate()
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/template/%d", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupTemplateByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.NotNil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestTemplateUndelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "delete")
|
||||
|
||||
res := h.repoMakeTemplate()
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/template/%d/undelete", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
res = h.lookupTemplateByID(res.ID)
|
||||
h.a.NotNil(res)
|
||||
h.a.Nil(res.DeletedAt)
|
||||
}
|
||||
|
||||
func TestTemplateRenderForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "read")
|
||||
helpers.DenyMe(h, types.TemplateRbacResource(0), "render")
|
||||
|
||||
res := h.repoMakeTemplate("rendering", "Hello, {{.interpolate}}", "text/plain")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/template/%d/render/testing.txt", res.ID)).
|
||||
JSON(`{"variables": {"interpolate": "world!"}}`).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("template.errors.notAllowedToRender")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateRenderDriverUndefined(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "render")
|
||||
|
||||
res := h.repoMakeTemplate("rendering", "Hello, {{.interpolate}}", "text/notexisting")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/template/%d/render/testing.txt", res.ID)).
|
||||
JSON(`{"variables": {"interpolate": "world!"}}`).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("rendering failed: driver not found")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateRenderPlain(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "render")
|
||||
|
||||
res := h.repoMakeTemplate("rendering", "Hello, {{.interpolate}}", "text/plain")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/template/%d/render/testing.txt", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"variables": {"interpolate": "world!"}}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertBody("Hello, world!")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateRenderHTML(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "render")
|
||||
|
||||
res := h.repoMakeTemplate("rendering", "<h1>Hello, {{.interpolate}}</h1>", "text/html")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/template/%d/render/testing.html", res.ID)).
|
||||
Header("Accept", "application/json").
|
||||
JSON(`{"variables": {"interpolate": "world!"}}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertBody("<h1>Hello, world!</h1>")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestTemplateRenderAuxTemplates(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearTemplates()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "templates.search")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.TemplateRbacResource(0), "render")
|
||||
|
||||
var (
|
||||
ctx = h.secCtx()
|
||||
|
||||
header = h.repoMakeTemplate("header-tpl", "<header>from meta</header>", "text/html")
|
||||
altHeader = h.repoMakeTemplate("alt-header-tpl", "<header>from argument</header>", "text/html")
|
||||
footer = h.repoMakeTemplate("footer-tpl", "<footer>bye</footer>", "text/html")
|
||||
|
||||
body = h.repoMakeTemplate("body-tpl", "<h1>Hello</h1>", "text/html")
|
||||
)
|
||||
|
||||
body.Meta.HeaderTemplateID = header.ID
|
||||
h.a.NoError(store.UpdateTemplate(ctx, service.DefaultStore, body))
|
||||
|
||||
read := func(aux types.TemplateRenderAux) string {
|
||||
doc, err := service.DefaultRenderer.Render(ctx, body.ID, "text/html", nil, nil, aux)
|
||||
h.a.NoError(err)
|
||||
|
||||
out, err := io.ReadAll(doc)
|
||||
h.a.NoError(err)
|
||||
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// no overrides, header comes from the template's own meta
|
||||
out := read(types.TemplateRenderAux{})
|
||||
h.a.Contains(out, "from meta")
|
||||
h.a.NotContains(out, "from argument")
|
||||
h.a.NotContains(out, "bye")
|
||||
|
||||
// header overridden, footer added
|
||||
out = read(types.TemplateRenderAux{HeaderTemplateID: altHeader.ID, FooterTemplateID: footer.ID})
|
||||
h.a.Contains(out, "from argument")
|
||||
h.a.NotContains(out, "from meta")
|
||||
h.a.Contains(out, "bye")
|
||||
h.a.Contains(out, "<h1>Hello</h1>")
|
||||
}
|
||||
48
server/tests/system/testdata/dal_connection_create/generic.json
vendored
Normal file
48
server/tests/system/testdata/dal_connection_create/generic.json
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"handle": "test_connection",
|
||||
"type": "corteza::system:dal-connection",
|
||||
|
||||
"meta": {
|
||||
"name": "Test Connection",
|
||||
"location": {
|
||||
"geometry": {
|
||||
"type": "",
|
||||
"coordinates": null
|
||||
},
|
||||
"properties": {
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"ownership": ""
|
||||
},
|
||||
|
||||
"config": {
|
||||
"connection": {
|
||||
"type": "corteza::dal:connection:dsn",
|
||||
"params": {
|
||||
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
|
||||
}
|
||||
},
|
||||
"privacy": {
|
||||
"sensitivityLevelID": "0"
|
||||
},
|
||||
"dal": {
|
||||
"properties": {},
|
||||
"operations": [
|
||||
"corteza::dal:operation:create",
|
||||
"corteza::dal:operation:update",
|
||||
"corteza::dal:operation:search",
|
||||
"corteza::dal:operation:lookup",
|
||||
"corteza::dal:operation:paging",
|
||||
"corteza::dal:operation:stats",
|
||||
"corteza::dal:operation:sorting",
|
||||
"corteza::dal:operation:RBAC"
|
||||
],
|
||||
|
||||
"modelIdent": "compose_record",
|
||||
"attributeIdent": "values",
|
||||
"partitionFormat": "compose_record_{{namespace}}_{{module}}",
|
||||
"partitionIdentValidator": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
48
server/tests/system/testdata/dal_connection_create_forbidden/generic.json
vendored
Normal file
48
server/tests/system/testdata/dal_connection_create_forbidden/generic.json
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"handle": "test_connection",
|
||||
"type": "corteza::system:dal-connection",
|
||||
|
||||
"meta": {
|
||||
"name": "Test Connection",
|
||||
"location": {
|
||||
"geometry": {
|
||||
"type": "",
|
||||
"coordinates": null
|
||||
},
|
||||
"properties": {
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"ownership": ""
|
||||
},
|
||||
|
||||
"config": {
|
||||
"connection": {
|
||||
"type": "corteza::dal:connection:dsn",
|
||||
"params": {
|
||||
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
|
||||
}
|
||||
},
|
||||
"privacy": {
|
||||
"sensitivityLevelID": "0"
|
||||
},
|
||||
"dal": {
|
||||
"properties": {},
|
||||
"operations": [
|
||||
"corteza::dal:operation:create",
|
||||
"corteza::dal:operation:update",
|
||||
"corteza::dal:operation:search",
|
||||
"corteza::dal:operation:lookup",
|
||||
"corteza::dal:operation:paging",
|
||||
"corteza::dal:operation:stats",
|
||||
"corteza::dal:operation:sorting",
|
||||
"corteza::dal:operation:RBAC"
|
||||
],
|
||||
|
||||
"modelIdent": "compose_record",
|
||||
"attributeIdent": "values",
|
||||
"partitionFormat": "compose_record_{{namespace}}_{{module}}",
|
||||
"partitionIdentValidator": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
48
server/tests/system/testdata/dal_connection_create_invalid_type/generic.json
vendored
Normal file
48
server/tests/system/testdata/dal_connection_create_invalid_type/generic.json
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"handle": "test_connection",
|
||||
"type": "corteza::system:primary-dal-connection",
|
||||
|
||||
"meta": {
|
||||
"name": "Test Connection",
|
||||
"location": {
|
||||
"geometry": {
|
||||
"type": "",
|
||||
"coordinates": null
|
||||
},
|
||||
"properties": {
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"ownership": ""
|
||||
},
|
||||
|
||||
"config": {
|
||||
"connection": {
|
||||
"type": "corteza::dal:connection:dsn",
|
||||
"params": {
|
||||
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
|
||||
}
|
||||
},
|
||||
"privacy": {
|
||||
"sensitivityLevelID": "0"
|
||||
},
|
||||
"dal": {
|
||||
"properties": {},
|
||||
"operations": [
|
||||
"corteza::dal:operation:create",
|
||||
"corteza::dal:operation:update",
|
||||
"corteza::dal:operation:search",
|
||||
"corteza::dal:operation:lookup",
|
||||
"corteza::dal:operation:paging",
|
||||
"corteza::dal:operation:stats",
|
||||
"corteza::dal:operation:sorting",
|
||||
"corteza::dal:operation:RBAC"
|
||||
],
|
||||
|
||||
"modelIdent": "compose_record",
|
||||
"attributeIdent": "values",
|
||||
"partitionFormat": "compose_record_{{namespace}}_{{module}}",
|
||||
"partitionIdentValidator": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
48
server/tests/system/testdata/dal_connection_update/generic.json
vendored
Normal file
48
server/tests/system/testdata/dal_connection_update/generic.json
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"handle": "test_connection_edited",
|
||||
"type": "corteza::system:dal-connection",
|
||||
|
||||
"meta": {
|
||||
"name": "Test Connection EDITED",
|
||||
"location": {
|
||||
"geometry": {
|
||||
"type": "",
|
||||
"coordinates": null
|
||||
},
|
||||
"properties": {
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"ownership": ""
|
||||
},
|
||||
|
||||
"config": {
|
||||
"connection": {
|
||||
"type": "corteza::dal:connection:dsn",
|
||||
"params": {
|
||||
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
|
||||
}
|
||||
},
|
||||
"privacy": {
|
||||
"sensitivityLevelID": "0"
|
||||
},
|
||||
"dal": {
|
||||
"properties": {},
|
||||
"operations": [
|
||||
"corteza::dal:operation:create",
|
||||
"corteza::dal:operation:update",
|
||||
"corteza::dal:operation:search",
|
||||
"corteza::dal:operation:lookup",
|
||||
"corteza::dal:operation:paging",
|
||||
"corteza::dal:operation:stats",
|
||||
"corteza::dal:operation:sorting",
|
||||
"corteza::dal:operation:RBAC"
|
||||
],
|
||||
|
||||
"modelIdent": "compose_record",
|
||||
"attributeIdent": "values",
|
||||
"partitionFormat": "compose_record_{{namespace}}_{{module}}",
|
||||
"partitionIdentValidator": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
48
server/tests/system/testdata/dal_connection_update_forbidden/generic.json
vendored
Normal file
48
server/tests/system/testdata/dal_connection_update_forbidden/generic.json
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"handle": "test_connection_edited",
|
||||
"type": "corteza::system:dal-connection",
|
||||
|
||||
"meta": {
|
||||
"name": "Test Connection EDITED",
|
||||
"location": {
|
||||
"geometry": {
|
||||
"type": "",
|
||||
"coordinates": null
|
||||
},
|
||||
"properties": {
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"ownership": ""
|
||||
},
|
||||
|
||||
"config": {
|
||||
"connection": {
|
||||
"type": "corteza::dal:connection:dsn",
|
||||
"params": {
|
||||
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
|
||||
}
|
||||
},
|
||||
"privacy": {
|
||||
"sensitivityLevelID": "0"
|
||||
},
|
||||
"dal": {
|
||||
"properties": {},
|
||||
"operations": [
|
||||
"corteza::dal:operation:create",
|
||||
"corteza::dal:operation:update",
|
||||
"corteza::dal:operation:search",
|
||||
"corteza::dal:operation:lookup",
|
||||
"corteza::dal:operation:paging",
|
||||
"corteza::dal:operation:stats",
|
||||
"corteza::dal:operation:sorting",
|
||||
"corteza::dal:operation:RBAC"
|
||||
],
|
||||
|
||||
"modelIdent": "compose_record",
|
||||
"attributeIdent": "values",
|
||||
"partitionFormat": "compose_record_{{namespace}}_{{module}}",
|
||||
"partitionIdentValidator": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
48
server/tests/system/testdata/dal_connection_update_primary/generic.json
vendored
Normal file
48
server/tests/system/testdata/dal_connection_update_primary/generic.json
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"handle": "primary_connection",
|
||||
"type": "corteza::system:primary-dal-connection",
|
||||
|
||||
"meta": {
|
||||
"name": "Primary Connection EDITED",
|
||||
"location": {
|
||||
"geometry": {
|
||||
"type": "",
|
||||
"coordinates": null
|
||||
},
|
||||
"properties": {
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"ownership": ""
|
||||
},
|
||||
|
||||
"config": {
|
||||
"connection": {
|
||||
"type": "corteza::dal:connection:dsn",
|
||||
"params": {
|
||||
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
|
||||
}
|
||||
},
|
||||
"privacy": {
|
||||
"sensitivityLevelID": "0"
|
||||
},
|
||||
"dal": {
|
||||
"properties": {},
|
||||
"operations": [
|
||||
"corteza::dal:operation:create",
|
||||
"corteza::dal:operation:update",
|
||||
"corteza::dal:operation:search",
|
||||
"corteza::dal:operation:lookup",
|
||||
"corteza::dal:operation:paging",
|
||||
"corteza::dal:operation:stats",
|
||||
"corteza::dal:operation:sorting",
|
||||
"corteza::dal:operation:RBAC"
|
||||
],
|
||||
|
||||
"modelIdent": "compose_record",
|
||||
"attributeIdent": "values",
|
||||
"partitionFormat": "compose_record_{{namespace}}_{{module}}",
|
||||
"partitionIdentValidator": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
8
server/tests/system/testdata/dal_sensitivity_level_create/generic.json
vendored
Normal file
8
server/tests/system/testdata/dal_sensitivity_level_create/generic.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"handle": "private",
|
||||
"level": 1,
|
||||
"meta": {
|
||||
"name": "private sensitivity",
|
||||
"description": "data is private to the owner"
|
||||
}
|
||||
}
|
||||
8
server/tests/system/testdata/dal_sensitivity_level_create_forbidden/generic.json
vendored
Normal file
8
server/tests/system/testdata/dal_sensitivity_level_create_forbidden/generic.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"handle": "private",
|
||||
"level": 1,
|
||||
"meta": {
|
||||
"name": "private sensitivity",
|
||||
"description": "data is private to the owner"
|
||||
}
|
||||
}
|
||||
8
server/tests/system/testdata/dal_sensitivity_level_update/generic.json
vendored
Normal file
8
server/tests/system/testdata/dal_sensitivity_level_update/generic.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"handle": "private_edited",
|
||||
"level": 1,
|
||||
"meta": {
|
||||
"name": "private sensitivity",
|
||||
"description": "data is private to the owner"
|
||||
}
|
||||
}
|
||||
8
server/tests/system/testdata/dal_sensitivity_level_update_forbidden/generic.json
vendored
Normal file
8
server/tests/system/testdata/dal_sensitivity_level_update_forbidden/generic.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"handle": "private_edited",
|
||||
"level": 1,
|
||||
"meta": {
|
||||
"name": "private sensitivity",
|
||||
"description": "data is private to the owner"
|
||||
}
|
||||
}
|
||||
663
server/tests/system/user_test.go
Normal file
663
server/tests/system/user_test.go
Normal file
@@ -0,0 +1,663 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
labelTypes "github.com/cortezaproject/corteza/server/pkg/label/types"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/cortezaproject/corteza/server/tests/helpers"
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (h helper) randEmail() string {
|
||||
return fmt.Sprintf("%s@test.tld", rs())
|
||||
}
|
||||
|
||||
func (h helper) createUserWithEmail(email string) *types.User {
|
||||
return h.createUser(&types.User{Email: email})
|
||||
}
|
||||
|
||||
func (h helper) createUser(user *types.User) *types.User {
|
||||
if user.ID == 0 {
|
||||
user.ID = id.Next()
|
||||
}
|
||||
|
||||
if user.CreatedAt.IsZero() {
|
||||
user.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
h.a.NoError(service.DefaultStore.CreateUser(context.Background(), user))
|
||||
return user
|
||||
}
|
||||
|
||||
func (h helper) clearUsers() {
|
||||
h.noError(store.TruncateUsers(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func (h helper) lookupUserByEmail(email string) *types.User {
|
||||
res, err := store.LookupUserByEmail(context.Background(), service.DefaultStore, email)
|
||||
h.noError(err)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestUserRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
service.CurrentSettings.Privacy.Mask.Email = true
|
||||
service.CurrentSettings.Privacy.Mask.Name = true
|
||||
defer func() {
|
||||
service.CurrentSettings.Privacy.Mask.Email = false
|
||||
service.CurrentSettings.Privacy.Mask.Name = false
|
||||
}()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/users/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Contains(`$.response.email`, "####")).
|
||||
Assert(jsonpath.Equal(`$.response.userID`, fmt.Sprintf("%d", u.ID))).
|
||||
End()
|
||||
|
||||
u = h.createUserWithEmail(h.randEmail())
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "email.unmask")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/users/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Equal(`$.response.email`, u.Email)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserRead_forbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
service.CurrentSettings.Privacy.Mask.Email = true
|
||||
service.CurrentSettings.Privacy.Mask.Name = true
|
||||
defer func() {
|
||||
service.CurrentSettings.Privacy.Mask.Email = false
|
||||
service.CurrentSettings.Privacy.Mask.Name = false
|
||||
}()
|
||||
|
||||
u := h.createUser(&types.User{
|
||||
Username: "test",
|
||||
Email: "test@domain.tld",
|
||||
Name: "Name Here",
|
||||
Handle: "test_handle",
|
||||
EmailConfirmed: true,
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/users/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("user.errors.notAllowedToRead")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserListAll(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.secCtx()
|
||||
|
||||
seedCount := 5
|
||||
for i := 0; i < seedCount; i++ {
|
||||
h.createUserWithEmail(h.randEmail())
|
||||
}
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filter`)).
|
||||
Assert(jsonpath.Present(`$.response.set`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserListWithPaging(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.secCtx()
|
||||
|
||||
seedCount := 40
|
||||
for i := 0; i < seedCount; i++ {
|
||||
h.createUserWithEmail(h.randEmail())
|
||||
}
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
var aux = struct {
|
||||
Response struct {
|
||||
Filter struct {
|
||||
NextPage *string
|
||||
PrevPage *string
|
||||
}
|
||||
}
|
||||
}{}
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Query("limit", "13").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filter`)).
|
||||
Assert(jsonpath.Present(`$.response.set`)).
|
||||
Assert(jsonpath.Len(`$.response.set`, 13)).
|
||||
Assert(jsonpath.Present(`$.response.filter.nextPage`)).
|
||||
End().
|
||||
JSON(&aux)
|
||||
|
||||
h.a.NotNil(aux.Response.Filter.NextPage)
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Header("Accept", "application/json").
|
||||
Query("limit", "13").
|
||||
Query("pageCursor", *aux.Response.Filter.NextPage).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(jsonpath.Len(`$.response.set`, 13)).
|
||||
Assert(jsonpath.Present(`$.response.filter.prevPage`)).
|
||||
Assert(jsonpath.Present(`$.response.filter.nextPage`)).
|
||||
End().
|
||||
JSON(&aux)
|
||||
|
||||
h.a.NotNil(aux.Response.Filter.PrevPage)
|
||||
h.a.NotNil(aux.Response.Filter.NextPage)
|
||||
|
||||
}
|
||||
|
||||
func TestUserList_filterForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
h.createUserWithEmail("usr")
|
||||
f := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
helpers.DenyMe(h, f.RbacResource(), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.NotPresent(fmt.Sprintf(`$.response.set[? @.email=="%s"]`, f.Email))).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserListQuery(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.secCtx()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Header("Accept", "application/json").
|
||||
Query("query", h.randEmail()).
|
||||
Query("email", h.randEmail()).
|
||||
Query("name", "John Doe").
|
||||
Query("handle", "jdoe").
|
||||
Query("username", "jdoe").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filter`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserListQueryEmail(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.secCtx()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "email.unmask")
|
||||
|
||||
ee := h.randEmail()
|
||||
h.createUserWithEmail(ee)
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Header("Accept", "application/json").
|
||||
Query("email", ee).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.set != null`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserListQueryUsername(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.secCtx()
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
|
||||
ee := h.randEmail()
|
||||
h.createUser(&types.User{
|
||||
Email: "test@test.tld",
|
||||
Username: ee,
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Header("Accept", "application/json").
|
||||
Query("username", ee).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.set != null`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserListQueryHandle(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.secCtx()
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
|
||||
h.createUser(&types.User{
|
||||
Email: "test@test.tld",
|
||||
Handle: "johnDoe",
|
||||
})
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Query("handle", "johnDoe").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.set != null`)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserListWithOneAllowed(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.secCtx()
|
||||
|
||||
newUserWeCanAccess := h.createUserWithEmail(h.randEmail())
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, newUserWeCanAccess.RbacResource(), "read")
|
||||
|
||||
// And one we cannot access
|
||||
h.createUserWithEmail(h.randEmail())
|
||||
|
||||
aux := struct {
|
||||
Response *struct {
|
||||
Filter *types.UserFilter
|
||||
}
|
||||
}{}
|
||||
|
||||
h.apiInit().
|
||||
Get("/users/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response.filter`)).
|
||||
Assert(jsonpath.Present(`$.response.set`)).
|
||||
Assert(jsonpath.Len(`$.response.set`, 1)).
|
||||
End().
|
||||
JSON(&aux)
|
||||
}
|
||||
|
||||
func TestUserCreateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
h.apiInit().
|
||||
Post("/users/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("email", h.randEmail()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("user.errors.notAllowedToCreate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserCreate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "user.create")
|
||||
|
||||
email := h.randEmail()
|
||||
|
||||
h.apiInit().
|
||||
Post("/users/").
|
||||
Header("Accept", "application/json").
|
||||
FormData("email", email).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserUpdateForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/users/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("email", h.randEmail()).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("user.errors.notAllowedToUpdate")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "update")
|
||||
|
||||
newEmail := h.randEmail()
|
||||
|
||||
h.apiInit().
|
||||
Put(fmt.Sprintf("/users/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
FormData("email", newEmail).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserSetPassword(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "update")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/users/%d/password", u.ID)).
|
||||
FormData("password", "newPassword").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserSuspend(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "suspend")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/users/%d/suspend", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
u = h.lookupUserByEmail(u.Email)
|
||||
h.a.NotNil(u)
|
||||
h.a.NotNil(u.SuspendedAt)
|
||||
}
|
||||
|
||||
func TestUserUnsuspend(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "unsuspend")
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/users/%d/unsuspend", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
u = h.lookupUserByEmail(u.Email)
|
||||
h.a.NotNil(u)
|
||||
h.a.Nil(u.SuspendedAt)
|
||||
}
|
||||
|
||||
func TestUserDeleteForbidden(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/users/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("user.errors.notAllowedToDelete")).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "delete")
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/users/%d", u.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserUndelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "delete")
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/users/%d/undelete", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
u = h.lookupUserByEmail(u.Email)
|
||||
h.a.NotNil(u)
|
||||
h.a.Nil(u.DeletedAt)
|
||||
}
|
||||
|
||||
func TestUserLabels(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "user.create")
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "users.search")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "update")
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "delete")
|
||||
|
||||
var (
|
||||
ID uint64
|
||||
)
|
||||
|
||||
t.Run("create", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
payload = &types.User{}
|
||||
)
|
||||
|
||||
helpers.SetLabelsViaAPI(h.apiInit(), t,
|
||||
"/users/",
|
||||
types.User{Email: h.randEmail(), Labels: map[string]labelTypes.LabelValue{"foo": {Val: "bar"}, "bar": {Val: "42"}}},
|
||||
payload,
|
||||
)
|
||||
req.NotZero(payload.ID)
|
||||
|
||||
h.a.Equal(payload.Labels["foo"].Val, "bar",
|
||||
"labels must contain foo with value bar")
|
||||
h.a.Equal(payload.Labels["bar"].Val, "42",
|
||||
"labels must contain bar with value 42")
|
||||
req.Equal(helpers.LabelsToStrings(payload.Labels), helpers.LabelsToStrings(helpers.LoadLabelsFromStore(t, service.DefaultStore, payload.LabelResourceKind(), payload.ID)),
|
||||
"response must match stored labels")
|
||||
|
||||
ID = payload.ID
|
||||
})
|
||||
|
||||
t.Run("update", func(t *testing.T) {
|
||||
if ID == 0 {
|
||||
t.Skip("label/create test not ran")
|
||||
}
|
||||
|
||||
var (
|
||||
req = require.New(t)
|
||||
payload = &types.User{}
|
||||
)
|
||||
|
||||
helpers.SetLabelsViaAPI(h.apiInit(), t,
|
||||
fmt.Sprintf("PUT /users/%d", ID),
|
||||
&types.User{ID: ID, Email: h.randEmail(), Labels: map[string]labelTypes.LabelValue{"foo": {Val: "baz"}, "baz": {Val: "123"}}},
|
||||
payload,
|
||||
)
|
||||
req.NotZero(payload.ID)
|
||||
//req.Nil(payload.UpdatedAt, "updatedAt must not change after changing labels")
|
||||
|
||||
req.Equal(payload.Labels["foo"].Val, "baz",
|
||||
"labels must contain foo with value baz")
|
||||
req.NotContains(payload.Labels, "bar",
|
||||
"labels must not contain bar")
|
||||
req.Equal(payload.Labels["baz"].Val, "123",
|
||||
"labels must contain baz with value 123")
|
||||
req.Equal(helpers.LabelsToStrings(payload.Labels), helpers.LabelsToStrings(helpers.LoadLabelsFromStore(t, service.DefaultStore, payload.LabelResourceKind(), payload.ID)),
|
||||
"response must match stored labels")
|
||||
})
|
||||
|
||||
t.Run("search", func(t *testing.T) {
|
||||
if ID == 0 {
|
||||
t.Skip("label/create test not ran")
|
||||
}
|
||||
|
||||
var (
|
||||
req = require.New(t)
|
||||
set = types.UserSet{}
|
||||
)
|
||||
|
||||
helpers.SearchWithLabelsViaAPI(h.apiInit(), t, "/users/", &set, url.Values{"labels": []string{"baz=123"}})
|
||||
req.NotEmpty(set)
|
||||
req.NotNil(set.FindByID(ID))
|
||||
req.NotNil(set.FindByID(ID).Labels)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserMemberList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "read")
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
r1 := h.repoMakeRole(h.randEmail())
|
||||
r2 := h.repoMakeRole(h.randEmail())
|
||||
h.createRoleMember(u.ID, r1.ID)
|
||||
h.createRoleMember(u.ID, r2.ID)
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/users/%d/membership", u.ID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserMemberAdd(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "members.manage")
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
r := h.repoMakeRole(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Post(fmt.Sprintf("/users/%d/membership/%d", u.ID, r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestUserMemberRemove(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.clearUsers()
|
||||
helpers.AllowMe(h, types.UserRbacResource(0), "read")
|
||||
helpers.AllowMe(h, types.RoleRbacResource(0), "members.manage")
|
||||
|
||||
u := h.createUserWithEmail(h.randEmail())
|
||||
|
||||
r := h.repoMakeRole(h.randEmail())
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/users/%d/membership/%d", u.ID, r.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
Reference in New Issue
Block a user