diff --git a/crm/repository/attachment.go b/crm/internal/repository/attachment.go similarity index 100% rename from crm/repository/attachment.go rename to crm/internal/repository/attachment.go diff --git a/crm/repository/chart.go b/crm/internal/repository/chart.go similarity index 100% rename from crm/repository/chart.go rename to crm/internal/repository/chart.go diff --git a/crm/repository/flags.go b/crm/internal/repository/flags.go similarity index 100% rename from crm/repository/flags.go rename to crm/internal/repository/flags.go diff --git a/crm/repository/main_test.go b/crm/internal/repository/main_test.go similarity index 100% rename from crm/repository/main_test.go rename to crm/internal/repository/main_test.go diff --git a/crm/repository/module.go b/crm/internal/repository/module.go similarity index 100% rename from crm/repository/module.go rename to crm/internal/repository/module.go diff --git a/crm/repository/page.go b/crm/internal/repository/page.go similarity index 100% rename from crm/repository/page.go rename to crm/internal/repository/page.go diff --git a/crm/repository/ql/README.md b/crm/internal/repository/ql/README.md similarity index 100% rename from crm/repository/ql/README.md rename to crm/internal/repository/ql/README.md diff --git a/crm/repository/ql/ast_nodes.go b/crm/internal/repository/ql/ast_nodes.go similarity index 100% rename from crm/repository/ql/ast_nodes.go rename to crm/internal/repository/ql/ast_nodes.go diff --git a/crm/repository/ql/ast_parser.go b/crm/internal/repository/ql/ast_parser.go similarity index 100% rename from crm/repository/ql/ast_parser.go rename to crm/internal/repository/ql/ast_parser.go diff --git a/crm/repository/ql/ast_parser_test.go b/crm/internal/repository/ql/ast_parser_test.go similarity index 100% rename from crm/repository/ql/ast_parser_test.go rename to crm/internal/repository/ql/ast_parser_test.go diff --git a/crm/repository/ql/handlers.go b/crm/internal/repository/ql/handlers.go similarity index 100% rename from crm/repository/ql/handlers.go rename to crm/internal/repository/ql/handlers.go diff --git a/crm/repository/ql/lexer.go b/crm/internal/repository/ql/lexer.go similarity index 100% rename from crm/repository/ql/lexer.go rename to crm/internal/repository/ql/lexer.go diff --git a/crm/repository/ql/lexer_test.go b/crm/internal/repository/ql/lexer_test.go similarity index 100% rename from crm/repository/ql/lexer_test.go rename to crm/internal/repository/ql/lexer_test.go diff --git a/crm/repository/ql/squirrel.go b/crm/internal/repository/ql/squirrel.go similarity index 100% rename from crm/repository/ql/squirrel.go rename to crm/internal/repository/ql/squirrel.go diff --git a/crm/repository/ql/token_codes.go b/crm/internal/repository/ql/token_codes.go similarity index 100% rename from crm/repository/ql/token_codes.go rename to crm/internal/repository/ql/token_codes.go diff --git a/crm/repository/ql/token_consumers.go b/crm/internal/repository/ql/token_consumers.go similarity index 100% rename from crm/repository/ql/token_consumers.go rename to crm/internal/repository/ql/token_consumers.go diff --git a/crm/repository/record.go b/crm/internal/repository/record.go similarity index 100% rename from crm/repository/record.go rename to crm/internal/repository/record.go diff --git a/crm/repository/record_report_builder.go b/crm/internal/repository/record_report_builder.go similarity index 100% rename from crm/repository/record_report_builder.go rename to crm/internal/repository/record_report_builder.go diff --git a/crm/repository/record_report_builder_test.go b/crm/internal/repository/record_report_builder_test.go similarity index 100% rename from crm/repository/record_report_builder_test.go rename to crm/internal/repository/record_report_builder_test.go diff --git a/crm/repository/record_test.go b/crm/internal/repository/record_test.go similarity index 100% rename from crm/repository/record_test.go rename to crm/internal/repository/record_test.go diff --git a/crm/repository/repository.go b/crm/internal/repository/repository.go similarity index 100% rename from crm/repository/repository.go rename to crm/internal/repository/repository.go diff --git a/crm/repository/repository_test.go b/crm/internal/repository/repository_test.go similarity index 100% rename from crm/repository/repository_test.go rename to crm/internal/repository/repository_test.go diff --git a/crm/repository/sqirrel.go b/crm/internal/repository/sqirrel.go similarity index 100% rename from crm/repository/sqirrel.go rename to crm/internal/repository/sqirrel.go diff --git a/crm/repository/trigger.go b/crm/internal/repository/trigger.go similarity index 100% rename from crm/repository/trigger.go rename to crm/internal/repository/trigger.go diff --git a/crm/service/attachment.go b/crm/internal/service/attachment.go similarity index 100% rename from crm/service/attachment.go rename to crm/internal/service/attachment.go diff --git a/crm/service/chart.go b/crm/internal/service/chart.go similarity index 100% rename from crm/service/chart.go rename to crm/internal/service/chart.go diff --git a/crm/service/chart_test.go b/crm/internal/service/chart_test.go similarity index 100% rename from crm/service/chart_test.go rename to crm/internal/service/chart_test.go diff --git a/crm/service/main_test.go b/crm/internal/service/main_test.go similarity index 100% rename from crm/service/main_test.go rename to crm/internal/service/main_test.go diff --git a/crm/service/module.go b/crm/internal/service/module.go similarity index 100% rename from crm/service/module.go rename to crm/internal/service/module.go diff --git a/crm/service/module_test.go b/crm/internal/service/module_test.go similarity index 100% rename from crm/service/module_test.go rename to crm/internal/service/module_test.go diff --git a/crm/service/notification.go b/crm/internal/service/notification.go similarity index 100% rename from crm/service/notification.go rename to crm/internal/service/notification.go diff --git a/crm/service/notification_mock_test.go b/crm/internal/service/notification_mock_test.go similarity index 100% rename from crm/service/notification_mock_test.go rename to crm/internal/service/notification_mock_test.go diff --git a/crm/service/notification_test.go b/crm/internal/service/notification_test.go similarity index 100% rename from crm/service/notification_test.go rename to crm/internal/service/notification_test.go diff --git a/crm/service/page.go b/crm/internal/service/page.go similarity index 100% rename from crm/service/page.go rename to crm/internal/service/page.go diff --git a/crm/service/page_test.go b/crm/internal/service/page_test.go similarity index 100% rename from crm/service/page_test.go rename to crm/internal/service/page_test.go diff --git a/crm/service/permissions.go b/crm/internal/service/permissions.go similarity index 100% rename from crm/service/permissions.go rename to crm/internal/service/permissions.go diff --git a/crm/service/permissions_test.go b/crm/internal/service/permissions_test.go similarity index 100% rename from crm/service/permissions_test.go rename to crm/internal/service/permissions_test.go diff --git a/crm/service/record.go b/crm/internal/service/record.go similarity index 100% rename from crm/service/record.go rename to crm/internal/service/record.go diff --git a/crm/service/record_test.go b/crm/internal/service/record_test.go similarity index 100% rename from crm/service/record_test.go rename to crm/internal/service/record_test.go diff --git a/crm/service/service.go b/crm/internal/service/service.go similarity index 100% rename from crm/service/service.go rename to crm/internal/service/service.go diff --git a/crm/service/trigger.go b/crm/internal/service/trigger.go similarity index 100% rename from crm/service/trigger.go rename to crm/internal/service/trigger.go diff --git a/crm/service/trigger_test.go b/crm/internal/service/trigger_test.go similarity index 100% rename from crm/service/trigger_test.go rename to crm/internal/service/trigger_test.go diff --git a/messaging/repository/attachment.go b/messaging/internal/repository/attachment.go similarity index 100% rename from messaging/repository/attachment.go rename to messaging/internal/repository/attachment.go diff --git a/messaging/repository/attachment_test.go b/messaging/internal/repository/attachment_test.go similarity index 100% rename from messaging/repository/attachment_test.go rename to messaging/internal/repository/attachment_test.go diff --git a/messaging/repository/channel.go b/messaging/internal/repository/channel.go similarity index 100% rename from messaging/repository/channel.go rename to messaging/internal/repository/channel.go diff --git a/messaging/repository/channel_member.go b/messaging/internal/repository/channel_member.go similarity index 100% rename from messaging/repository/channel_member.go rename to messaging/internal/repository/channel_member.go diff --git a/messaging/repository/channel_test.go b/messaging/internal/repository/channel_test.go similarity index 100% rename from messaging/repository/channel_test.go rename to messaging/internal/repository/channel_test.go diff --git a/messaging/repository/error.go b/messaging/internal/repository/error.go similarity index 100% rename from messaging/repository/error.go rename to messaging/internal/repository/error.go diff --git a/messaging/repository/events.go b/messaging/internal/repository/events.go similarity index 100% rename from messaging/repository/events.go rename to messaging/internal/repository/events.go diff --git a/messaging/repository/events_test.go b/messaging/internal/repository/events_test.go similarity index 100% rename from messaging/repository/events_test.go rename to messaging/internal/repository/events_test.go diff --git a/messaging/repository/flags.go b/messaging/internal/repository/flags.go similarity index 100% rename from messaging/repository/flags.go rename to messaging/internal/repository/flags.go diff --git a/messaging/repository/main_test.go b/messaging/internal/repository/main_test.go similarity index 100% rename from messaging/repository/main_test.go rename to messaging/internal/repository/main_test.go diff --git a/messaging/repository/mention.go b/messaging/internal/repository/mention.go similarity index 100% rename from messaging/repository/mention.go rename to messaging/internal/repository/mention.go diff --git a/messaging/repository/message.go b/messaging/internal/repository/message.go similarity index 100% rename from messaging/repository/message.go rename to messaging/internal/repository/message.go diff --git a/messaging/repository/message_flag.go b/messaging/internal/repository/message_flag.go similarity index 100% rename from messaging/repository/message_flag.go rename to messaging/internal/repository/message_flag.go diff --git a/messaging/repository/message_flag_test.go b/messaging/internal/repository/message_flag_test.go similarity index 100% rename from messaging/repository/message_flag_test.go rename to messaging/internal/repository/message_flag_test.go diff --git a/messaging/repository/message_test.go b/messaging/internal/repository/message_test.go similarity index 100% rename from messaging/repository/message_test.go rename to messaging/internal/repository/message_test.go diff --git a/messaging/repository/pubsub.go b/messaging/internal/repository/pubsub.go similarity index 100% rename from messaging/repository/pubsub.go rename to messaging/internal/repository/pubsub.go diff --git a/messaging/repository/pubsub_memory.go b/messaging/internal/repository/pubsub_memory.go similarity index 100% rename from messaging/repository/pubsub_memory.go rename to messaging/internal/repository/pubsub_memory.go diff --git a/messaging/repository/pubsub_memory_test.go b/messaging/internal/repository/pubsub_memory_test.go similarity index 100% rename from messaging/repository/pubsub_memory_test.go rename to messaging/internal/repository/pubsub_memory_test.go diff --git a/messaging/repository/pubsub_redis.go b/messaging/internal/repository/pubsub_redis.go similarity index 100% rename from messaging/repository/pubsub_redis.go rename to messaging/internal/repository/pubsub_redis.go diff --git a/messaging/repository/repository.go b/messaging/internal/repository/repository.go similarity index 100% rename from messaging/repository/repository.go rename to messaging/internal/repository/repository.go diff --git a/messaging/repository/repository_test.go b/messaging/internal/repository/repository_test.go similarity index 100% rename from messaging/repository/repository_test.go rename to messaging/internal/repository/repository_test.go diff --git a/messaging/repository/unread.go b/messaging/internal/repository/unread.go similarity index 100% rename from messaging/repository/unread.go rename to messaging/internal/repository/unread.go diff --git a/messaging/repository/util.go b/messaging/internal/repository/util.go similarity index 100% rename from messaging/repository/util.go rename to messaging/internal/repository/util.go diff --git a/messaging/service/attachment.go b/messaging/internal/service/attachment.go similarity index 100% rename from messaging/service/attachment.go rename to messaging/internal/service/attachment.go diff --git a/messaging/service/attachment_mock_test.go b/messaging/internal/service/attachment_mock_test.go similarity index 100% rename from messaging/service/attachment_mock_test.go rename to messaging/internal/service/attachment_mock_test.go diff --git a/messaging/service/channel.go b/messaging/internal/service/channel.go similarity index 100% rename from messaging/service/channel.go rename to messaging/internal/service/channel.go diff --git a/messaging/service/channel_mock_test.go b/messaging/internal/service/channel_mock_test.go similarity index 100% rename from messaging/service/channel_mock_test.go rename to messaging/internal/service/channel_mock_test.go diff --git a/messaging/service/channel_test.go b/messaging/internal/service/channel_test.go similarity index 100% rename from messaging/service/channel_test.go rename to messaging/internal/service/channel_test.go diff --git a/messaging/service/error.go b/messaging/internal/service/error.go similarity index 100% rename from messaging/service/error.go rename to messaging/internal/service/error.go diff --git a/messaging/service/events.go b/messaging/internal/service/events.go similarity index 100% rename from messaging/service/events.go rename to messaging/internal/service/events.go diff --git a/messaging/service/main_test.go b/messaging/internal/service/main_test.go similarity index 100% rename from messaging/service/main_test.go rename to messaging/internal/service/main_test.go diff --git a/messaging/service/message.go b/messaging/internal/service/message.go similarity index 100% rename from messaging/service/message.go rename to messaging/internal/service/message.go diff --git a/messaging/service/message_mock_test.go b/messaging/internal/service/message_mock_test.go similarity index 100% rename from messaging/service/message_mock_test.go rename to messaging/internal/service/message_mock_test.go diff --git a/messaging/service/message_test.go b/messaging/internal/service/message_test.go similarity index 100% rename from messaging/service/message_test.go rename to messaging/internal/service/message_test.go diff --git a/messaging/service/permissions.go b/messaging/internal/service/permissions.go similarity index 100% rename from messaging/service/permissions.go rename to messaging/internal/service/permissions.go diff --git a/messaging/service/permissions_test.go b/messaging/internal/service/permissions_test.go similarity index 100% rename from messaging/service/permissions_test.go rename to messaging/internal/service/permissions_test.go diff --git a/messaging/service/pubsub.go b/messaging/internal/service/pubsub.go similarity index 100% rename from messaging/service/pubsub.go rename to messaging/internal/service/pubsub.go diff --git a/messaging/service/service.go b/messaging/internal/service/service.go similarity index 100% rename from messaging/service/service.go rename to messaging/internal/service/service.go diff --git a/system/repository/application.go b/system/internal/repository/application.go similarity index 100% rename from system/repository/application.go rename to system/internal/repository/application.go diff --git a/system/repository/applications_test.go b/system/internal/repository/applications_test.go similarity index 100% rename from system/repository/applications_test.go rename to system/internal/repository/applications_test.go diff --git a/system/repository/credentials.go b/system/internal/repository/credentials.go similarity index 100% rename from system/repository/credentials.go rename to system/internal/repository/credentials.go diff --git a/system/repository/credentials_test.go b/system/internal/repository/credentials_test.go similarity index 100% rename from system/repository/credentials_test.go rename to system/internal/repository/credentials_test.go diff --git a/system/repository/error.go b/system/internal/repository/error.go similarity index 100% rename from system/repository/error.go rename to system/internal/repository/error.go diff --git a/system/repository/main_test.go b/system/internal/repository/main_test.go similarity index 100% rename from system/repository/main_test.go rename to system/internal/repository/main_test.go diff --git a/system/repository/mocks/credentials.go b/system/internal/repository/mocks/credentials.go similarity index 100% rename from system/repository/mocks/credentials.go rename to system/internal/repository/mocks/credentials.go diff --git a/system/repository/mocks/user.go b/system/internal/repository/mocks/user.go similarity index 100% rename from system/repository/mocks/user.go rename to system/internal/repository/mocks/user.go diff --git a/system/repository/organisation.go b/system/internal/repository/organisation.go similarity index 100% rename from system/repository/organisation.go rename to system/internal/repository/organisation.go diff --git a/system/repository/organisation_test.go b/system/internal/repository/organisation_test.go similarity index 100% rename from system/repository/organisation_test.go rename to system/internal/repository/organisation_test.go diff --git a/system/repository/repository.go b/system/internal/repository/repository.go similarity index 100% rename from system/repository/repository.go rename to system/internal/repository/repository.go diff --git a/system/repository/repository_test.go b/system/internal/repository/repository_test.go similarity index 100% rename from system/repository/repository_test.go rename to system/internal/repository/repository_test.go diff --git a/system/repository/role.go b/system/internal/repository/role.go similarity index 100% rename from system/repository/role.go rename to system/internal/repository/role.go diff --git a/system/repository/role_test.go b/system/internal/repository/role_test.go similarity index 100% rename from system/repository/role_test.go rename to system/internal/repository/role_test.go diff --git a/system/repository/settings.go b/system/internal/repository/settings.go similarity index 100% rename from system/repository/settings.go rename to system/internal/repository/settings.go diff --git a/system/repository/user.go b/system/internal/repository/user.go similarity index 100% rename from system/repository/user.go rename to system/internal/repository/user.go diff --git a/system/repository/user_test.go b/system/internal/repository/user_test.go similarity index 100% rename from system/repository/user_test.go rename to system/internal/repository/user_test.go diff --git a/system/repository/util.go b/system/internal/repository/util.go similarity index 100% rename from system/repository/util.go rename to system/internal/repository/util.go diff --git a/system/service/application.go b/system/internal/service/application.go similarity index 100% rename from system/service/application.go rename to system/internal/service/application.go diff --git a/system/service/auth.go b/system/internal/service/auth.go similarity index 100% rename from system/service/auth.go rename to system/internal/service/auth.go diff --git a/system/service/auth_test.go b/system/internal/service/auth_test.go similarity index 100% rename from system/service/auth_test.go rename to system/internal/service/auth_test.go diff --git a/system/service/error.go b/system/internal/service/error.go similarity index 100% rename from system/service/error.go rename to system/internal/service/error.go diff --git a/system/service/main_test.go b/system/internal/service/main_test.go similarity index 100% rename from system/service/main_test.go rename to system/internal/service/main_test.go diff --git a/system/service/organisation.go b/system/internal/service/organisation.go similarity index 100% rename from system/service/organisation.go rename to system/internal/service/organisation.go diff --git a/system/service/organisation_mock_test.go b/system/internal/service/organisation_mock_test.go similarity index 100% rename from system/service/organisation_mock_test.go rename to system/internal/service/organisation_mock_test.go diff --git a/system/service/permissions.go b/system/internal/service/permissions.go similarity index 100% rename from system/service/permissions.go rename to system/internal/service/permissions.go diff --git a/system/service/role.go b/system/internal/service/role.go similarity index 100% rename from system/service/role.go rename to system/internal/service/role.go diff --git a/system/service/role_mock_test.go b/system/internal/service/role_mock_test.go similarity index 100% rename from system/service/role_mock_test.go rename to system/internal/service/role_mock_test.go diff --git a/system/internal/service/rules.go b/system/internal/service/rules.go new file mode 100644 index 000000000..e457ea541 --- /dev/null +++ b/system/internal/service/rules.go @@ -0,0 +1,141 @@ +package service + +import ( + "context" + "strings" + + "github.com/pkg/errors" + + internalRules "github.com/crusttech/crust/internal/rules" + "github.com/crusttech/crust/system/repository" + "github.com/crusttech/crust/system/types" +) + +const ( + delimiter = ":" +) + +type ( + rules struct { + db db + ctx context.Context + + resources internalRules.ResourcesInterface + } + + effectiveRules struct { + Resource string `json:"resource"` + Operation string `json:"operation"` + Allow bool `json:"allow"` + } + + RulesService interface { + With(ctx context.Context) RulesService + + List() (interface{}, error) + Effective(filter string) ([]effectiveRules, error) + + Check(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access + + Read(roleID uint64) (interface{}, error) + Update(roleID uint64, rules []internalRules.Rule) (interface{}, error) + Delete(roleID uint64) (interface{}, error) + } +) + +func Rules() RulesService { + return (&rules{}).With(context.Background()) +} + +func (p *rules) With(ctx context.Context) RulesService { + db := repository.DB(ctx) + return &rules{ + db: db, + ctx: ctx, + + resources: internalRules.NewResources(ctx, db), + } +} + +func (p *rules) List() (interface{}, error) { + perms := []types.Permission{} + for resource, operations := range permissionList { + err := p.checkServiceAccess(resource) + if err == nil { + for ops := range operations { + perms = append(perms, types.Permission{Resource: resource, Operation: ops}) + } + } + } + return perms, nil +} + +func (p *rules) Effective(filter string) (eff []effectiveRules, err error) { + eff = []effectiveRules{} + for resource, operations := range permissionList { + // err := p.checkServiceAccess(resource) + if err == nil { + for ops := range operations { + eff = append(eff, effectiveRules{ + Resource: resource, + Operation: ops, + Allow: false, + }) + } + } + } + return +} + +func (p *rules) Check(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access { + return p.resources.Check(resource, operation, fallbacks...) +} + +func (p *rules) Read(roleID uint64) (interface{}, error) { + ret, err := p.resources.Read(roleID) + if err != nil { + return nil, err + } + + // Only display rules under granted scopes. + rules := []internalRules.Rule{} + for _, rule := range ret { + err = p.checkServiceAccess(rule.Resource) + if err == nil { + rules = append(rules, rule) + } + } + return rules, nil +} + +func (p *rules) Update(roleID uint64, rules []internalRules.Rule) (interface{}, error) { + for _, rule := range rules { + err := validatePermission(rule.Resource, rule.Operation) + if err != nil { + return nil, err + } + err = p.checkServiceAccess(rule.Resource) + if err != nil { + return nil, err + } + } + err := p.resources.Grant(roleID, rules) + if err != nil { + return nil, err + } + return p.resources.Read(roleID) +} + +func (p *rules) Delete(roleID uint64) (interface{}, error) { + return nil, p.resources.Delete(roleID) +} + +func (p *rules) checkServiceAccess(resource string) error { + service := strings.Split(resource, delimiter)[0] + + grant := p.resources.Check(service, "grant") + if grant == internalRules.Allow { + return nil + } + return errors.Errorf("No grant permissions for: %v", service) +} diff --git a/system/service/rules_test.go b/system/internal/service/rules_test.go similarity index 100% rename from system/service/rules_test.go rename to system/internal/service/rules_test.go diff --git a/system/service/service.go b/system/internal/service/service.go similarity index 100% rename from system/service/service.go rename to system/internal/service/service.go diff --git a/system/internal/service/user.go b/system/internal/service/user.go new file mode 100644 index 000000000..9cb0c110e --- /dev/null +++ b/system/internal/service/user.go @@ -0,0 +1,176 @@ +package service + +import ( + "context" + + "github.com/pkg/errors" + "github.com/titpetric/factory" + + "github.com/crusttech/crust/system/repository" + "github.com/crusttech/crust/system/types" +) + +const ( + ErrUserInvalidCredentials = serviceError("UserInvalidCredentials") + ErrUserLocked = serviceError("UserLocked") + + uuidLength = 36 +) + +type ( + user struct { + db *factory.DB + ctx context.Context + + user repository.UserRepository + } + + UserService interface { + With(ctx context.Context) UserService + + FindByUsername(username string) (*types.User, error) + FindByEmail(email string) (*types.User, error) + FindByID(id uint64) (*types.User, error) + FindByIDs(id ...uint64) (types.UserSet, error) + Find(filter *types.UserFilter) (types.UserSet, error) + + FindOrCreate(*types.User) (*types.User, error) + + Create(input *types.User) (*types.User, error) + Update(mod *types.User) (*types.User, error) + + Delete(id uint64) error + Suspend(id uint64) error + Unsuspend(id uint64) error + + ValidateCredentials(username, password string) (*types.User, error) + } +) + +func User() UserService { + return (&user{}).With(context.Background()) +} + +func (svc *user) With(ctx context.Context) UserService { + db := repository.DB(ctx) + + return &user{ + db: db, + ctx: ctx, + user: repository.User(ctx, db), + } +} + +func (svc *user) Delete(id uint64) error { + return svc.user.DeleteByID(id) +} + +func (svc *user) Suspend(id uint64) error { + return svc.user.SuspendByID(id) +} + +func (svc *user) Unsuspend(id uint64) error { + return svc.user.UnsuspendByID(id) +} + +func (svc *user) ValidateCredentials(username, password string) (*types.User, error) { + user, err := svc.user.FindByUsername(username) + if err != nil { + return nil, err + } + + if !user.ValidatePassword(password) { + return nil, ErrUserInvalidCredentials + } + + if !svc.canLogin(user) { + return nil, ErrUserLocked + } + + return user, nil +} + +func (svc *user) FindByID(id uint64) (*types.User, error) { + return svc.user.FindByID(id) +} + +func (svc *user) FindByIDs(ids ...uint64) (types.UserSet, error) { + return svc.user.FindByIDs(ids...) +} + +func (svc *user) FindByEmail(email string) (*types.User, error) { + return svc.user.FindByEmail(email) +} + +func (svc *user) FindByUsername(username string) (*types.User, error) { + return svc.user.FindByUsername(username) +} + +func (svc *user) Find(filter *types.UserFilter) (types.UserSet, error) { + return svc.user.Find(filter) +} + +// Finds if user with a specific satosa id exists and returns it otherwise it creates a fresh one +func (svc *user) FindOrCreate(user *types.User) (out *types.User, err error) { + return out, svc.db.Transaction(func() error { + if len(user.SatosaID) != uuidLength { + // @todo uuid format check + return errors.Errorf("Invalid UUID value (%v) for SATOSA ID", user.SatosaID) + } + + out, err = svc.user.FindBySatosaID(user.SatosaID) + + if err == repository.ErrUserNotFound { + out, err = svc.user.Create(user) + return err + } + + if err != nil { + // FindBySatosaID error + return err + } + + // @todo need to be more selective with fields we update... + out, err = svc.user.Update(out) + if err != nil { + return err + } + + return nil + }) +} + +func (svc *user) Create(input *types.User) (out *types.User, err error) { + return out, svc.db.Transaction(func() error { + // Encrypt user password + if out, err = svc.user.Create(input); err != nil { + return err + } + return nil + }) +} + +func (svc *user) Update(mod *types.User) (u *types.User, err error) { + return u, svc.db.Transaction(func() (err error) { + if u, err = svc.user.FindByID(mod.ID); err != nil { + return + } + + // Assign changed values + u.Email = mod.Email + u.Username = mod.Username + u.Name = mod.Name + u.Handle = mod.Handle + u.Kind = mod.Kind + + if u, err = svc.user.Update(u); err != nil { + return err + } + + return nil + }) +} + +func (svc *user) canLogin(u *types.User) bool { + return u != nil && u.ID > 0 && u.SuspendedAt == nil && u.DeletedAt == nil +} diff --git a/system/service/user_mock_test.go b/system/internal/service/user_mock_test.go similarity index 100% rename from system/service/user_mock_test.go rename to system/internal/service/user_mock_test.go diff --git a/system/service/user_test.go b/system/internal/service/user_test.go similarity index 100% rename from system/service/user_test.go rename to system/internal/service/user_test.go diff --git a/system/service/validation.go b/system/internal/service/validation.go similarity index 100% rename from system/service/validation.go rename to system/internal/service/validation.go diff --git a/system/service/validation_test.go b/system/internal/service/validation_test.go similarity index 100% rename from system/service/validation_test.go rename to system/internal/service/validation_test.go diff --git a/system/service/rules.go b/system/service/rules.go index e457ea541..d6c488045 100644 --- a/system/service/rules.go +++ b/system/service/rules.go @@ -2,140 +2,22 @@ package service import ( "context" - "strings" - "github.com/pkg/errors" - - internalRules "github.com/crusttech/crust/internal/rules" - "github.com/crusttech/crust/system/repository" - "github.com/crusttech/crust/system/types" -) - -const ( - delimiter = ":" + "github.com/crusttech/crust/internal/rules" + "github.com/crusttech/crust/system/internal/service" ) type ( - rules struct { - db db - ctx context.Context - - resources internalRules.ResourcesInterface - } - - effectiveRules struct { - Resource string `json:"resource"` - Operation string `json:"operation"` - Allow bool `json:"allow"` - } - RulesService interface { - With(ctx context.Context) RulesService - List() (interface{}, error) - Effective(filter string) ([]effectiveRules, error) - - Check(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access - + Effective(filter string) ([]service.EffectiveRules, error) + Check(resource string, operation string, fallbacks ...rules.CheckAccessFunc) rules.Access Read(roleID uint64) (interface{}, error) - Update(roleID uint64, rules []internalRules.Rule) (interface{}, error) - Delete(roleID uint64) (interface{}, error) } ) -func Rules() RulesService { - return (&rules{}).With(context.Background()) -} - -func (p *rules) With(ctx context.Context) RulesService { - db := repository.DB(ctx) - return &rules{ - db: db, - ctx: ctx, - - resources: internalRules.NewResources(ctx, db), - } -} - -func (p *rules) List() (interface{}, error) { - perms := []types.Permission{} - for resource, operations := range permissionList { - err := p.checkServiceAccess(resource) - if err == nil { - for ops := range operations { - perms = append(perms, types.Permission{Resource: resource, Operation: ops}) - } - } - } - return perms, nil -} - -func (p *rules) Effective(filter string) (eff []effectiveRules, err error) { - eff = []effectiveRules{} - for resource, operations := range permissionList { - // err := p.checkServiceAccess(resource) - if err == nil { - for ops := range operations { - eff = append(eff, effectiveRules{ - Resource: resource, - Operation: ops, - Allow: false, - }) - } - } - } - return -} - -func (p *rules) Check(resource string, operation string, fallbacks ...internalRules.CheckAccessFunc) internalRules.Access { - return p.resources.Check(resource, operation, fallbacks...) -} - -func (p *rules) Read(roleID uint64) (interface{}, error) { - ret, err := p.resources.Read(roleID) - if err != nil { - return nil, err - } - - // Only display rules under granted scopes. - rules := []internalRules.Rule{} - for _, rule := range ret { - err = p.checkServiceAccess(rule.Resource) - if err == nil { - rules = append(rules, rule) - } - } - return rules, nil -} - -func (p *rules) Update(roleID uint64, rules []internalRules.Rule) (interface{}, error) { - for _, rule := range rules { - err := validatePermission(rule.Resource, rule.Operation) - if err != nil { - return nil, err - } - err = p.checkServiceAccess(rule.Resource) - if err != nil { - return nil, err - } - } - err := p.resources.Grant(roleID, rules) - if err != nil { - return nil, err - } - return p.resources.Read(roleID) -} - -func (p *rules) Delete(roleID uint64) (interface{}, error) { - return nil, p.resources.Delete(roleID) -} - -func (p *rules) checkServiceAccess(resource string) error { - service := strings.Split(resource, delimiter)[0] - - grant := p.resources.Check(service, "grant") - if grant == internalRules.Allow { - return nil - } - return errors.Errorf("No grant permissions for: %v", service) +var DefaultRules = service.DefaultRules + +func Rules(ctx context.Context) RulesService { + return DefaultRules.With(ctx) } diff --git a/system/service/user.go b/system/service/user.go index 9cb0c110e..04d858e1f 100644 --- a/system/service/user.go +++ b/system/service/user.go @@ -3,174 +3,22 @@ package service import ( "context" - "github.com/pkg/errors" - "github.com/titpetric/factory" - - "github.com/crusttech/crust/system/repository" "github.com/crusttech/crust/system/types" -) - -const ( - ErrUserInvalidCredentials = serviceError("UserInvalidCredentials") - ErrUserLocked = serviceError("UserLocked") - - uuidLength = 36 + "github.com/crusttech/crust/system/internal/service" ) type ( - user struct { - db *factory.DB - ctx context.Context - - user repository.UserRepository - } - UserService interface { - With(ctx context.Context) UserService - FindByUsername(username string) (*types.User, error) FindByEmail(email string) (*types.User, error) FindByID(id uint64) (*types.User, error) FindByIDs(id ...uint64) (types.UserSet, error) Find(filter *types.UserFilter) (types.UserSet, error) - - FindOrCreate(*types.User) (*types.User, error) - - Create(input *types.User) (*types.User, error) - Update(mod *types.User) (*types.User, error) - - Delete(id uint64) error - Suspend(id uint64) error - Unsuspend(id uint64) error - - ValidateCredentials(username, password string) (*types.User, error) } ) -func User() UserService { - return (&user{}).With(context.Background()) -} - -func (svc *user) With(ctx context.Context) UserService { - db := repository.DB(ctx) - - return &user{ - db: db, - ctx: ctx, - user: repository.User(ctx, db), - } -} - -func (svc *user) Delete(id uint64) error { - return svc.user.DeleteByID(id) -} - -func (svc *user) Suspend(id uint64) error { - return svc.user.SuspendByID(id) -} - -func (svc *user) Unsuspend(id uint64) error { - return svc.user.UnsuspendByID(id) -} - -func (svc *user) ValidateCredentials(username, password string) (*types.User, error) { - user, err := svc.user.FindByUsername(username) - if err != nil { - return nil, err - } - - if !user.ValidatePassword(password) { - return nil, ErrUserInvalidCredentials - } - - if !svc.canLogin(user) { - return nil, ErrUserLocked - } - - return user, nil -} - -func (svc *user) FindByID(id uint64) (*types.User, error) { - return svc.user.FindByID(id) -} - -func (svc *user) FindByIDs(ids ...uint64) (types.UserSet, error) { - return svc.user.FindByIDs(ids...) -} - -func (svc *user) FindByEmail(email string) (*types.User, error) { - return svc.user.FindByEmail(email) -} - -func (svc *user) FindByUsername(username string) (*types.User, error) { - return svc.user.FindByUsername(username) -} - -func (svc *user) Find(filter *types.UserFilter) (types.UserSet, error) { - return svc.user.Find(filter) -} - -// Finds if user with a specific satosa id exists and returns it otherwise it creates a fresh one -func (svc *user) FindOrCreate(user *types.User) (out *types.User, err error) { - return out, svc.db.Transaction(func() error { - if len(user.SatosaID) != uuidLength { - // @todo uuid format check - return errors.Errorf("Invalid UUID value (%v) for SATOSA ID", user.SatosaID) - } - - out, err = svc.user.FindBySatosaID(user.SatosaID) - - if err == repository.ErrUserNotFound { - out, err = svc.user.Create(user) - return err - } - - if err != nil { - // FindBySatosaID error - return err - } - - // @todo need to be more selective with fields we update... - out, err = svc.user.Update(out) - if err != nil { - return err - } - - return nil - }) -} - -func (svc *user) Create(input *types.User) (out *types.User, err error) { - return out, svc.db.Transaction(func() error { - // Encrypt user password - if out, err = svc.user.Create(input); err != nil { - return err - } - return nil - }) -} - -func (svc *user) Update(mod *types.User) (u *types.User, err error) { - return u, svc.db.Transaction(func() (err error) { - if u, err = svc.user.FindByID(mod.ID); err != nil { - return - } - - // Assign changed values - u.Email = mod.Email - u.Username = mod.Username - u.Name = mod.Name - u.Handle = mod.Handle - u.Kind = mod.Kind - - if u, err = svc.user.Update(u); err != nil { - return err - } - - return nil - }) -} - -func (svc *user) canLogin(u *types.User) bool { - return u != nil && u.ID > 0 && u.SuspendedAt == nil && u.DeletedAt == nil +var DefaultUser = service.DefaultUser + +func User(ctx context.Context) UserService { + return DefaultUser.With(ctx) }