From 32e2069c4513e8bc7e853f9578090d544eb8618d Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Wed, 3 Apr 2019 23:49:03 +0200 Subject: [PATCH] Refactor "roles" CLI command --- system/cli/cli.go | 42 +----- system/cli/roles.go | 336 ++++++++++++++++++++++++-------------------- 2 files changed, 186 insertions(+), 192 deletions(-) diff --git a/system/cli/cli.go b/system/cli/cli.go index 516b34677..1c5f1a9f0 100644 --- a/system/cli/cli.go +++ b/system/cli/cli.go @@ -14,8 +14,9 @@ import ( func Init(ctx context.Context) { // Main command. rootCmd := &cobra.Command{Use: "system-cli"} + db := repository.DB(ctx) - settingsService := settings.NewService(settings.NewRepository(repository.DB(ctx), "sys_settings")) + settingsService := settings.NewService(settings.NewRepository(db, "sys_settings")) Settings(rootCmd, settingsService) @@ -40,44 +41,7 @@ func Init(ctx context.Context) { } cmdUsers.AddCommand(cmdUsersList) - // Assign role to user. - var cmdUserAssignRole = &cobra.Command{ - Use: "roleadd [userID] [roleID]", - Short: "Assign role to user", - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - RoleAssignUser(args[1], args[0]) - }, - } - cmdUsers.AddCommand(cmdUserAssignRole) - - // Role management commands. - var cmdRole = &cobra.Command{ - Use: "roles", - Short: "Role management", - } - rootCmd.AddCommand(cmdRole) - - // Reset roles. - var cmdRolesReset = &cobra.Command{ - Use: "reset", - Short: "Reset roles", - Run: func(cmd *cobra.Command, args []string) { - RolesReset() - }, - } - cmdRole.AddCommand(cmdRolesReset) - - // Add user to role. - var cmdRoleAddUser = &cobra.Command{ - Use: "useradd [roleID] [userID]", - Short: "Add user to role", - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - RoleAssignUser(args[0], args[1]) - }, - } - cmdRole.AddCommand(cmdRoleAddUser) + roles(ctx, rootCmd, db) err := rootCmd.Execute() if err != nil { diff --git a/system/cli/roles.go b/system/cli/roles.go index 8d28f10ee..8270caa68 100644 --- a/system/cli/roles.go +++ b/system/cli/roles.go @@ -3,179 +3,209 @@ package cli import ( "context" "fmt" - "os" "strconv" "time" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/titpetric/factory" + "github.com/crusttech/crust/internal/rules" "github.com/crusttech/crust/system/internal/repository" "github.com/crusttech/crust/system/types" ) -func RolesReset() { - var ( - err error - ctx = context.Background() - db = repository.DB(ctx) - roleRepo = repository.Role(ctx, db) - ruleRepo = rules.NewResources(ctx, db) - ) - - // Recreates core roles (will not reset memberships!) - err = types.RoleSet{ - &types.Role{ID: 1, Handle: "everyone", Name: "Everyone"}, - &types.Role{ID: 2, Handle: "admins", Name: "Administrators"}, - }.Walk(func(r *types.Role) error { - // Update will exec REPLACE and take care of creation as well - r.CreatedAt = time.Now() - _, err := roleRepo.Update(r) - return err - }) - - if err != nil { - fmt.Printf("could not reset roles: %v", err) - return +func roles(ctx context.Context, rootCmd *cobra.Command, db *factory.DB) { + resetCmd := &cobra.Command{ + Use: "reset", + Short: "Reset roles", + Run: rolesReset(ctx, db), } - rules := []rules.Rule{ - {1, "system", "user.create", 2}, - {1, "compose", "access", 2}, - {1, "messaging", "access", 2}, - {2, "compose", "namespace.create", 2}, - {2, "compose", "access", 2}, - {2, "compose", "grant", 2}, - {2, "compose:namespace:*", "page.create", 2}, - {2, "compose:namespace:*", "read", 2}, - {2, "compose:namespace:*", "update", 2}, - {2, "compose:namespace:*", "delete", 2}, - {2, "compose:namespace:*", "module.create", 2}, - {2, "compose:namespace:*", "chart.create", 2}, - {2, "compose:namespace:*", "trigger.create", 2}, - {2, "compose:chart:*", "read", 2}, - {2, "compose:chart:*", "update", 2}, - {2, "compose:chart:*", "delete", 2}, - {2, "compose:trigger:*", "read", 2}, - {2, "compose:trigger:*", "update", 2}, - {2, "compose:trigger:*", "delete", 2}, - {2, "compose:page:*", "read", 2}, - {2, "compose:page:*", "update", 2}, - {2, "compose:page:*", "delete", 2}, - {2, "system", "access", 2}, - {2, "system", "grant", 2}, - {2, "system", "settings.read", 2}, - {2, "system", "settings.manage", 2}, - {2, "system", "organisation.create", 2}, - {2, "system", "user.create", 2}, - {2, "system", "role.create", 2}, - {2, "system:organisation:*", "access", 2}, - {2, "system:user:*", "read", 2}, - {2, "system:user:*", "update", 2}, - {2, "system:user:*", "suspend", 2}, - {2, "system:user:*", "unsuspend", 2}, - {2, "system:user:*", "delete", 2}, - {2, "system:role:*", "read", 2}, - {2, "system:role:*", "update", 2}, - {2, "system:role:*", "delete", 2}, - {2, "system:role:*", "members.manage", 2}, - {2, "messaging", "access", 2}, - {2, "messaging", "grant", 2}, - {2, "messaging", "channel.public.create", 2}, - {2, "messaging", "channel.private.create", 2}, - {2, "messaging", "channel.group.create", 2}, - {2, "messaging:channel:*", "update", 2}, - {2, "messaging:channel:*", "leave", 2}, - {2, "messaging:channel:*", "read", 2}, - {2, "messaging:channel:*", "join", 2}, - {2, "messaging:channel:*", "delete", 2}, - {2, "messaging:channel:*", "undelete", 2}, - {2, "messaging:channel:*", "archive", 2}, - {2, "messaging:channel:*", "unarchive", 2}, - {2, "messaging:channel:*", "members.manage", 2}, - {2, "messaging:channel:*", "webhooks.manage", 2}, - {2, "messaging:channel:*", "attachments.manage", 2}, - {2, "messaging:channel:*", "message.attach", 2}, - {2, "messaging:channel:*", "message.update.all", 2}, - {2, "messaging:channel:*", "message.update.own", 2}, - {2, "messaging:channel:*", "message.delete.all", 2}, - {2, "messaging:channel:*", "message.delete.own", 2}, - {2, "messaging:channel:*", "message.embed", 2}, - {2, "messaging:channel:*", "message.send", 2}, - {2, "messaging:channel:*", "message.reply", 2}, - {2, "messaging:channel:*", "message.react", 2}, - {2, "compose:module:*", "read", 2}, - {2, "compose:module:*", "update", 2}, - {2, "compose:module:*", "delete", 2}, - {2, "compose:module:*", "record.create", 2}, - {2, "compose:module:*", "record.read", 2}, - {2, "compose:module:*", "record.update", 2}, - {2, "compose:module:*", "record.delete", 2}, + addUserCmd := &cobra.Command{ + Use: "useradd [role-ID-or-name-or-handle] [user-ID-or-email]", + Short: "Add user to role", + Args: cobra.ExactArgs(2), + Run: rolesUserAdd(ctx, db), } - var purgedRoles = map[uint64]bool{} - for _, r := range rules { - if !purgedRoles[r.RoleID] { - if err = ruleRepo.Delete(r.RoleID); err != nil { - fmt.Printf("could not reset rules: %v", err) - return + // Role management commands. + var cmdRole = &cobra.Command{ + Use: "roles", + Short: "Role management", + } + + cmdRole.AddCommand(resetCmd, addUserCmd) + rootCmd.AddCommand(cmdRole) +} + +func rolesReset(ctx context.Context, db *factory.DB) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + var ( + err error + roleRepo = repository.Role(ctx, db) + ruleRepo = rules.NewResources(ctx, db) + ) + + // Recreates core roles (will not reset memberships!) + err = types.RoleSet{ + &types.Role{ID: 1, Handle: "everyone", Name: "Everyone"}, + &types.Role{ID: 2, Handle: "admins", Name: "Administrators"}, + }.Walk(func(r *types.Role) error { + // Update will exec REPLACE and take care of creation as well + r.CreatedAt = time.Now() + _, err := roleRepo.Update(r) + return err + }) + + if err != nil { + fmt.Printf("could not reset roles: %v", err) + return + } + + rules := []rules.Rule{ + {1, "system", "user.create", 2}, + {1, "compose", "access", 2}, + {1, "messaging", "access", 2}, + {2, "compose", "namespace.create", 2}, + {2, "compose", "access", 2}, + {2, "compose", "grant", 2}, + {2, "compose:namespace:*", "page.create", 2}, + {2, "compose:namespace:*", "read", 2}, + {2, "compose:namespace:*", "update", 2}, + {2, "compose:namespace:*", "delete", 2}, + {2, "compose:namespace:*", "module.create", 2}, + {2, "compose:namespace:*", "chart.create", 2}, + {2, "compose:namespace:*", "trigger.create", 2}, + {2, "compose:chart:*", "read", 2}, + {2, "compose:chart:*", "update", 2}, + {2, "compose:chart:*", "delete", 2}, + {2, "compose:trigger:*", "read", 2}, + {2, "compose:trigger:*", "update", 2}, + {2, "compose:trigger:*", "delete", 2}, + {2, "compose:page:*", "read", 2}, + {2, "compose:page:*", "update", 2}, + {2, "compose:page:*", "delete", 2}, + {2, "system", "access", 2}, + {2, "system", "grant", 2}, + {2, "system", "settings.read", 2}, + {2, "system", "settings.manage", 2}, + {2, "system", "organisation.create", 2}, + {2, "system", "user.create", 2}, + {2, "system", "role.create", 2}, + {2, "system:organisation:*", "access", 2}, + {2, "system:user:*", "read", 2}, + {2, "system:user:*", "update", 2}, + {2, "system:user:*", "suspend", 2}, + {2, "system:user:*", "unsuspend", 2}, + {2, "system:user:*", "delete", 2}, + {2, "system:role:*", "read", 2}, + {2, "system:role:*", "update", 2}, + {2, "system:role:*", "delete", 2}, + {2, "system:role:*", "members.manage", 2}, + {2, "messaging", "access", 2}, + {2, "messaging", "grant", 2}, + {2, "messaging", "channel.public.create", 2}, + {2, "messaging", "channel.private.create", 2}, + {2, "messaging", "channel.group.create", 2}, + {2, "messaging:channel:*", "update", 2}, + {2, "messaging:channel:*", "leave", 2}, + {2, "messaging:channel:*", "read", 2}, + {2, "messaging:channel:*", "join", 2}, + {2, "messaging:channel:*", "delete", 2}, + {2, "messaging:channel:*", "undelete", 2}, + {2, "messaging:channel:*", "archive", 2}, + {2, "messaging:channel:*", "unarchive", 2}, + {2, "messaging:channel:*", "members.manage", 2}, + {2, "messaging:channel:*", "webhooks.manage", 2}, + {2, "messaging:channel:*", "attachments.manage", 2}, + {2, "messaging:channel:*", "message.attach", 2}, + {2, "messaging:channel:*", "message.update.all", 2}, + {2, "messaging:channel:*", "message.update.own", 2}, + {2, "messaging:channel:*", "message.delete.all", 2}, + {2, "messaging:channel:*", "message.delete.own", 2}, + {2, "messaging:channel:*", "message.embed", 2}, + {2, "messaging:channel:*", "message.send", 2}, + {2, "messaging:channel:*", "message.reply", 2}, + {2, "messaging:channel:*", "message.react", 2}, + {2, "compose:module:*", "read", 2}, + {2, "compose:module:*", "update", 2}, + {2, "compose:module:*", "delete", 2}, + {2, "compose:module:*", "record.create", 2}, + {2, "compose:module:*", "record.read", 2}, + {2, "compose:module:*", "record.update", 2}, + {2, "compose:module:*", "record.delete", 2}, + } + + var purgedRoles = map[uint64]bool{} + for _, r := range rules { + if !purgedRoles[r.RoleID] { + if err = ruleRepo.Delete(r.RoleID); err != nil { + fmt.Printf("could not reset rules: %v", err) + return + } + + purgedRoles[r.RoleID] = true } + } - purgedRoles[r.RoleID] = true + // Recreates rules for core roles + err = ruleRepo.Reset(rules) + + if err != nil { + fmt.Printf("could not reset rules: %v", err) } } - - // Recreates rules for core roles - err = ruleRepo.Reset(rules) - - if err != nil { - fmt.Printf("could not reset rules: %v", err) - } } -func RoleAssignUser(roleStr string, userStr string) { - ctx := context.Background() - db := repository.DB(ctx) +func rolesUserAdd(ctx context.Context, db *factory.DB) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + // Create role and user repository. + var ( + roleStr, userStr = args[0], args[1] - // Create role and user repository. - roleRepo := repository.Role(ctx, db) - userRepo := repository.User(ctx, db) + roleRepo = repository.Role(ctx, db) + userRepo = repository.User(ctx, db) - var err error + rr []*types.Role + role *types.Role + user *types.User + ID uint64 - // Try to parse roleID. - roleID, err := strconv.ParseUint(roleStr, 10, 64) - if err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) + err error + ) + + // Try to find role by name and by ID + if rr, err = roleRepo.Find(&types.RoleFilter{Query: roleStr}); err != nil { + exit(cmd, err) + } else if len(rr) == 1 { + role = rr[0] + } else if len(rr) > 1 { + exit(cmd, errors.Errorf("too many roles found with name %q", roleStr)) + } else if role == nil { + if ID, err = strconv.ParseUint(roleStr, 10, 64); err != nil { + // Could not parse ID out of role string + return + } else if role, err = roleRepo.FindByID(ID); err != nil { + return + } + } + + if user, err = userRepo.FindByEmail(userStr); err != nil && err != repository.ErrUserNotFound { + exit(cmd, err) + } else if user == nil || user.ID == 0 { + if ID, err = strconv.ParseUint(userStr, 10, 64); err != nil { + exit(cmd, err) + } else if user, err = userRepo.FindByID(ID); err != nil { + exit(cmd, err) + } + } + + // Add user to role. + if err = roleRepo.MemberAddByID(role.ID, user.ID); err != nil { + exit(cmd, err) + } + + cmd.Printf("Added user [%d] %q to [%d] %q role\n", user.ID, user.Email, role.ID, role.Name) } - - // Check if role ID exists. - role, err := roleRepo.FindByID(roleID) - if err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) - } - - var userID uint64 - var user *types.User - - // Try to parse userID. - userID, err = strconv.ParseUint(userStr, 10, 64) - if err != nil { - user, err = userRepo.FindByEmail(userStr) - } else { - user, err = userRepo.FindByID(userID) - } - if err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) - } - - // Add user to role. - err = roleRepo.MemberAddByID(role.ID, user.ID) - if err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) - } - - fmt.Printf("Added user: %d %s to role: %s\n", user.ID, user.Email, role.Name) }