diff --git a/api/system/spec.json b/api/system/spec.json index cd45c1b97..2e28d51a0 100644 --- a/api/system/spec.json +++ b/api/system/spec.json @@ -1005,7 +1005,7 @@ "name": "name", "type": "string", "required": true, - "title": "Email" + "title": "Application name" }, { "name": "enabled", diff --git a/compose/service/service.go b/compose/service/service.go index 28edbb34a..20be0e6f2 100644 --- a/compose/service/service.go +++ b/compose/service/service.go @@ -141,7 +141,7 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) { DefaultPage = Page() DefaultChart = Chart() DefaultNotification = Notification() - DefaultAttachment = Attachment(fs) + DefaultAttachment = Attachment(DefaultStore) return nil } diff --git a/tests/compose/main_test.go b/tests/compose/main_test.go new file mode 100644 index 000000000..454a385cd --- /dev/null +++ b/tests/compose/main_test.go @@ -0,0 +1,158 @@ +package compose + +import ( + "context" + "os" + "testing" + + _ "github.com/joho/godotenv/autoload" + "github.com/spf13/afero" + "github.com/steinfletcher/apitest" + "github.com/stretchr/testify/require" + "github.com/titpetric/factory" + + "github.com/go-chi/chi" + "go.uber.org/zap" + + "github.com/cortezaproject/corteza-server/compose" + migrate "github.com/cortezaproject/corteza-server/compose/db" + "github.com/cortezaproject/corteza-server/compose/rest" + "github.com/cortezaproject/corteza-server/compose/service" + "github.com/cortezaproject/corteza-server/compose/types" + "github.com/cortezaproject/corteza-server/internal/auth" + "github.com/cortezaproject/corteza-server/internal/permissions" + "github.com/cortezaproject/corteza-server/internal/rand" + "github.com/cortezaproject/corteza-server/internal/store" + "github.com/cortezaproject/corteza-server/pkg/api" + "github.com/cortezaproject/corteza-server/pkg/cli" + "github.com/cortezaproject/corteza-server/pkg/logger" + sysTypes "github.com/cortezaproject/corteza-server/system/types" + "github.com/cortezaproject/corteza-server/tests/helpers" +) + +type ( + helper struct { + t *testing.T + a *require.Assertions + + cUser *sysTypes.User + roleID uint64 + } +) + +var ( + cfg *cli.Config + r chi.Router + p = permissions.NewTestService() +) + +func InitConfig() { + var err error + + if cfg != nil { + return + } + + helpers.RecursiveDotEnvLoad() + + ctx := context.Background() + log, _ := zap.NewDevelopment() + + cfg = compose.Configure() + cfg.Log = log + + cfg.Init() + + auth.SetupDefault(string(rand.Bytes(32)), 10) + + if err = cfg.RootCommandDBSetup.Run(ctx, nil, cfg); err != nil { + panic(err) + } else if err := migrate.Migrate(factory.Database.MustGet("compose"), log); err != nil { + panic(err) + } + + logger.SetDefault(log) + service.DefaultPermissions = p + if service.DefaultStore, err = store.NewWithAfero(afero.NewMemMapFs(), "test"); err != nil { + panic(err) + } + + cfg.InitServices(ctx, cfg) +} + +func InitApp() { + InitConfig() + helpers.InitAuth() + + if r != nil { + return + } + + r = chi.NewRouter() + r.Use(api.Base(logger.Default())...) + helpers.BindAuthMiddleware(r) + rest.MountRoutes(r) +} + +func TestMain(m *testing.M) { + InitApp() + os.Exit(m.Run()) +} + +func newHelper(t *testing.T) helper { + h := helper{ + t: t, + a: require.New(t), + roleID: factory.Sonyflake.NextID(), + cUser: &sysTypes.User{ + ID: factory.Sonyflake.NextID(), + }, + } + + h.cUser.SetRoles([]uint64{h.roleID}) + + p.ClearGrants() + h.mockPermissionsWithAccess() + + return h +} + +// apitest basics, initialize, set handler, add auth +func (h helper) apiInit() *apitest.APITest { + InitApp() + + return apitest. + New(). + Handler(r). + Intercept(helpers.ReqHeaderAuthBearer(h.cUser)) +} + +func (h helper) mockPermissions(rules ...*permissions.Rule) { + h.a.NoError(p.Grant( + // TestService we use does not have any backend storage, + context.Background(), + // We want to make sure we did not make a mistake with any of the mocked resources or actions + service.DefaultAccessControl.Whitelist(), + rules..., + )) +} + +// Prepends allow access rule for compose service for everyone +func (h helper) mockPermissionsWithAccess(rules ...*permissions.Rule) { + rules = append( + rules, + permissions.AllowRule(permissions.EveryoneRoleID, types.ComposePermissionResource, "access"), + ) + + h.mockPermissions(rules...) +} + +// Set allow permision for test role +func (h helper) allow(r permissions.Resource, o permissions.Operation) { + h.mockPermissions(permissions.AllowRule(h.roleID, r, o)) +} + +// set deny permission for test role +func (h helper) deny(r permissions.Resource, o permissions.Operation) { + h.mockPermissions(permissions.DenyRule(h.roleID, r, o)) +} diff --git a/tests/compose/permissions_delete_test.go b/tests/compose/permissions_delete_test.go new file mode 100644 index 000000000..441e5bc11 --- /dev/null +++ b/tests/compose/permissions_delete_test.go @@ -0,0 +1,45 @@ +package compose + +import ( + "fmt" + "net/http" + "testing" + + "github.com/cortezaproject/corteza-server/compose/types" + "github.com/cortezaproject/corteza-server/internal/permissions" + "github.com/cortezaproject/corteza-server/tests/helpers" +) + +func TestPermissionsDelete(t *testing.T) { + h := newHelper(t) + + // Make sure our user can grant + h.allow(types.ComposePermissionResource, "grant") + + // New role. + permDelRole := h.roleID + 1 + + h.a.Len(p.FindRulesByRoleID(permDelRole), 0) + + // Setup a few fake rules for new roke + h.mockPermissions( + permissions.AllowRule(permDelRole, types.ComposePermissionResource, "access"), + permissions.DenyRule(permDelRole, types.ComposePermissionResource, "namespace.create"), + ) + + h.a.Len(p.FindRulesByRoleID(permDelRole), 2) + + h.apiInit(). + Delete(fmt.Sprintf("/permissions/%d/rules", permDelRole)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + // Make sure everything is deleted + rr, _ := p.FindRulesByRoleID(permDelRole).Filter(func(r *permissions.Rule) (b bool, e error) { + return r.Access != permissions.Inherit, nil + }) + + h.a.Empty(rr) +} diff --git a/tests/compose/permissions_effective_test.go b/tests/compose/permissions_effective_test.go new file mode 100644 index 000000000..be4c7d1ad --- /dev/null +++ b/tests/compose/permissions_effective_test.go @@ -0,0 +1,22 @@ +package compose + +import ( + "net/http" + "testing" + + "github.com/cortezaproject/corteza-server/compose/types" + "github.com/cortezaproject/corteza-server/tests/helpers" +) + +func TestPermissionsEffective(t *testing.T) { + h := newHelper(t) + h.allow(types.ComposePermissionResource, "access") + h.deny(types.ComposePermissionResource, "namespace.create") + + h.apiInit(). + Get("/permissions/effective"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} diff --git a/tests/compose/permissions_list_test.go b/tests/compose/permissions_list_test.go new file mode 100644 index 000000000..a0b51a54e --- /dev/null +++ b/tests/compose/permissions_list_test.go @@ -0,0 +1,22 @@ +package compose + +import ( + "net/http" + "testing" + + jsonpath "github.com/steinfletcher/apitest-jsonpath" + + "github.com/cortezaproject/corteza-server/tests/helpers" +) + +func TestPermissionsList(t *testing.T) { + h := newHelper(t) + + h.apiInit(). + Get("/permissions/"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + Assert(jsonpath.Present(`$.response[? @.resource=="compose"]`)). + End() +} diff --git a/tests/compose/permissions_read_test.go b/tests/compose/permissions_read_test.go new file mode 100644 index 000000000..9c3d95c04 --- /dev/null +++ b/tests/compose/permissions_read_test.go @@ -0,0 +1,24 @@ +package compose + +import ( + "fmt" + "net/http" + "testing" + + "github.com/cortezaproject/corteza-server/compose/types" + "github.com/cortezaproject/corteza-server/tests/helpers" +) + +func TestPermissionsRead(t *testing.T) { + h := newHelper(t) + h.allow(types.ComposePermissionResource, "access") + h.allow(types.ComposePermissionResource, "grant") + h.deny(types.ComposePermissionResource, "namespace.create") + + h.apiInit(). + Get(fmt.Sprintf("/permissions/%d/rules", h.roleID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} diff --git a/tests/compose/permissions_update_test.go b/tests/compose/permissions_update_test.go new file mode 100644 index 000000000..8339a7b17 --- /dev/null +++ b/tests/compose/permissions_update_test.go @@ -0,0 +1,22 @@ +package compose + +import ( + "fmt" + "net/http" + "testing" + + "github.com/cortezaproject/corteza-server/tests/helpers" +) + +func TestPermissionsUpdate(t *testing.T) { + h := newHelper(t) + h.allow("compose", "grant") + + h.apiInit(). + Patch(fmt.Sprintf("/permissions/%d/rules", h.roleID)). + JSON(`{"rules":[{"resource":"compose","operation":"namespace.create","access":"allow"}]}`). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} diff --git a/tests/messaging/main_test.go b/tests/messaging/main_test.go index e4b059c19..63d34f4f7 100644 --- a/tests/messaging/main_test.go +++ b/tests/messaging/main_test.go @@ -19,6 +19,7 @@ import ( "github.com/cortezaproject/corteza-server/internal/rand" "github.com/cortezaproject/corteza-server/internal/store" "github.com/cortezaproject/corteza-server/messaging" + migrate "github.com/cortezaproject/corteza-server/messaging/db" "github.com/cortezaproject/corteza-server/messaging/rest" "github.com/cortezaproject/corteza-server/messaging/service" "github.com/cortezaproject/corteza-server/messaging/types" @@ -72,8 +73,9 @@ func InitConfig() { service.DefaultPermissions = p if service.DefaultStore, err = store.NewWithAfero(afero.NewMemMapFs(), "test"); err != nil { panic(err) + } else if err := migrate.Migrate(factory.Database.MustGet("messaging"), log); err != nil { + panic(err) } - cfg.InitServices(ctx, cfg) } @@ -109,11 +111,7 @@ func newHelper(t *testing.T) helper { h.cUser.SetRoles([]uint64{h.roleID}) p.ClearGrants() - - // Setup permissions with allowed access to messaging - h.mockPermissions( - permissions.AllowRule(permissions.EveryoneRoleID, types.MessagingPermissionResource, "access"), - ) + h.mockPermissionsWithAccess() return h } diff --git a/tests/messaging/permissions_effective_test.go b/tests/messaging/permissions_effective_test.go index 6ed0f9444..a06e2953f 100644 --- a/tests/messaging/permissions_effective_test.go +++ b/tests/messaging/permissions_effective_test.go @@ -4,13 +4,14 @@ import ( "net/http" "testing" + "github.com/cortezaproject/corteza-server/messaging/types" "github.com/cortezaproject/corteza-server/tests/helpers" ) func TestPermissionsEffective(t *testing.T) { h := newHelper(t) - h.allow("messaging", "access") - h.deny("messaging", "channel.group.create") + h.allow(types.MessagingPermissionResource, "access") + h.deny(types.MessagingPermissionResource, "channel.group.create") h.apiInit(). Get("/permissions/effective"). diff --git a/tests/messaging/permissions_read_test.go b/tests/messaging/permissions_read_test.go index 27dc982b0..08b1e7509 100644 --- a/tests/messaging/permissions_read_test.go +++ b/tests/messaging/permissions_read_test.go @@ -5,14 +5,15 @@ import ( "net/http" "testing" + "github.com/cortezaproject/corteza-server/messaging/types" "github.com/cortezaproject/corteza-server/tests/helpers" ) func TestPermissionsRead(t *testing.T) { h := newHelper(t) - h.allow("messaging", "access") - h.allow("messaging", "grant") - h.deny("messaging", "channel.group.create") + h.allow(types.MessagingPermissionResource, "access") + h.allow(types.MessagingPermissionResource, "grant") + h.deny(types.MessagingPermissionResource, "channel.group.create") h.apiInit(). Get(fmt.Sprintf("/permissions/%d/rules", h.roleID)). diff --git a/tests/messaging/permissions_update_test.go b/tests/messaging/permissions_update_test.go index e2016fc63..b9dffef2a 100644 --- a/tests/messaging/permissions_update_test.go +++ b/tests/messaging/permissions_update_test.go @@ -5,12 +5,13 @@ import ( "net/http" "testing" + "github.com/cortezaproject/corteza-server/messaging/types" "github.com/cortezaproject/corteza-server/tests/helpers" ) func TestPermissionsUpdate(t *testing.T) { h := newHelper(t) - h.allow("messaging", "grant") + h.allow(types.MessagingPermissionResource, "grant") h.apiInit(). Patch(fmt.Sprintf("/permissions/%d/rules", h.roleID)). diff --git a/tests/system/application_crud_test.go b/tests/system/application_crud_test.go deleted file mode 100644 index 7de3af96a..000000000 --- a/tests/system/application_crud_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package system - -import ( - "testing" -) - -func TestApplicationCreate(t *testing.T) { - t.Skip("to be implemented") -} - -func TestApplicationRead(t *testing.T) { - t.Skip("to be implemented") -} - -func TestApplicationUpdate(t *testing.T) { - t.Skip("to be implemented") -} - -func TestApplicationDelete(t *testing.T) { - t.Skip("to be implemented") -} diff --git a/tests/system/application_test.go b/tests/system/application_test.go new file mode 100644 index 000000000..b0c68f143 --- /dev/null +++ b/tests/system/application_test.go @@ -0,0 +1,142 @@ +package system + +import ( + "context" + "fmt" + "net/http" + "testing" + + jsonpath "github.com/steinfletcher/apitest-jsonpath" + + "github.com/cortezaproject/corteza-server/system/repository" + "github.com/cortezaproject/corteza-server/system/types" + "github.com/cortezaproject/corteza-server/tests/helpers" +) + +func (h helper) repoApplication() repository.ApplicationRepository { + return repository.Application(context.Background(), db()) +} + +func (h helper) repoMakeApplication(name string) *types.Application { + a, err := h. + repoApplication(). + Create(&types.Application{Name: name}) + h.a.NoError(err) + + return a +} + +func TestApplicationRead(t *testing.T) { + h := newHelper(t) + + a := h.repoMakeApplication("one-app") + + h.apiInit(). + Get(fmt.Sprintf("/application/%d", a.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + Assert(jsonpath.Equal(`$.response.name`, a.Name)). + Assert(jsonpath.Equal(`$.response.applicationID`, fmt.Sprintf("%d", a.ID))). + End() +} + +func TestApplicationList(t *testing.T) { + h := newHelper(t) + + h.repoMakeApplication("app") + h.repoMakeApplication("app") + + h.apiInit(). + Get("/application/"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestApplicationCreateForbidden(t *testing.T) { + h := newHelper(t) + + h.apiInit(). + Post("/application/"). + FormData("name", "my-app"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("Not allowed to create application")). + End() +} + +func TestApplicationCreate(t *testing.T) { + h := newHelper(t) + h.allow(types.SystemPermissionResource, "application.create") + + h.apiInit(). + Post("/application/"). + FormData("name", "my-app"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestApplicationUpdateForbidden(t *testing.T) { + h := newHelper(t) + a := h.repoMakeApplication("one-app") + + h.apiInit(). + Put(fmt.Sprintf("/application/%d", a.ID)). + FormData("name", "changed-name"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("Not allowed to update application")). + End() +} + +func TestApplicationUpdate(t *testing.T) { + h := newHelper(t) + a := h.repoMakeApplication("one-app") + h.allow(types.ApplicationPermissionResource.AppendWildcard(), "update") + + h.apiInit(). + Put(fmt.Sprintf("/application/%d", a.ID)). + FormData("name", "changed-name"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + a, err := h.repoApplication().FindByID(a.ID) + h.a.NoError(err) + h.a.NotNil(a) + h.a.Equal(a.Name, "changed-name") +} + +func TestApplicationDeleteForbidden(t *testing.T) { + h := newHelper(t) + a := h.repoMakeApplication("one-app") + + h.apiInit(). + Delete(fmt.Sprintf("/application/%d", a.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("Not allowed to delete application")). + End() +} + +func TestApplicationDelete(t *testing.T) { + h := newHelper(t) + h.allow(types.ApplicationPermissionResource.AppendWildcard(), "delete") + + a := h.repoMakeApplication("one-app") + + h.apiInit(). + Delete(fmt.Sprintf("/application/%d", a.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + a, err := h.repoApplication().FindByID(a.ID) + h.a.Error(err, "system.repository.ApplicationNotFound") +} diff --git a/tests/system/main_test.go b/tests/system/main_test.go index a2bf1f812..9bb9eb6dc 100644 --- a/tests/system/main_test.go +++ b/tests/system/main_test.go @@ -20,6 +20,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/cli" "github.com/cortezaproject/corteza-server/pkg/logger" "github.com/cortezaproject/corteza-server/system" + migrate "github.com/cortezaproject/corteza-server/system/db" "github.com/cortezaproject/corteza-server/system/rest" "github.com/cortezaproject/corteza-server/system/service" "github.com/cortezaproject/corteza-server/system/types" @@ -42,6 +43,10 @@ var ( p = permissions.NewTestService() ) +func db() *factory.DB { + return factory.Database.MustGet("system").With(context.Background()) +} + func InitConfig() { var err error @@ -63,6 +68,8 @@ func InitConfig() { if err = cfg.RootCommandDBSetup.Run(ctx, nil, cfg); err != nil { panic(err) + } else if err := migrate.Migrate(factory.Database.MustGet("system"), log); err != nil { + panic(err) } logger.SetDefault(log) @@ -106,11 +113,7 @@ func newHelper(t *testing.T) helper { h.cUser.SetRoles([]uint64{h.roleID}) p.ClearGrants() - - // Setup permissions with allowed access to system - h.mockPermissions( - permissions.AllowRule(permissions.EveryoneRoleID, types.SystemPermissionResource, "access"), - ) + h.mockPermissionsWithAccess() return h } @@ -135,6 +138,16 @@ func (h helper) mockPermissions(rules ...*permissions.Rule) { )) } +// Prepends allow access rule for messaging service for everyone +func (h helper) mockPermissionsWithAccess(rules ...*permissions.Rule) { + rules = append( + rules, + permissions.AllowRule(permissions.EveryoneRoleID, types.SystemPermissionResource, "access"), + ) + + h.mockPermissions(rules...) +} + // Set allow permision for test role func (h helper) allow(r permissions.Resource, o permissions.Operation) { h.mockPermissions(permissions.AllowRule(h.roleID, r, o))