From 5a4d0663da5095f1d8e8e0ca6020af2bbe9c0890 Mon Sep 17 00:00:00 2001 From: Abhishek K <32607604+abhishek9686@users.noreply.github.com> Date: Sun, 25 Aug 2024 07:25:40 +0530 Subject: [PATCH] NET-1227: User Cli cmds Update (#3064) * generalise smtp config * copy over smtp vars * env new line * fix master key api access * comment user tests * fix network and user invite for master key access * remove email sender type * user mgmt commands * check user role on CE * user role nmtcl cmds * user groups commands * fix role and groups command * fix user create cmd * add usage info * rm user role check * fix user update cmd * fix static check --- cli/cmd/user/create.go | 33 +++++++++-- cli/cmd/user/flags.go | 11 ++-- cli/cmd/user/groups.go | 118 ++++++++++++++++++++++++++++++++++++++++ cli/cmd/user/list.go | 10 +++- cli/cmd/user/roles.go | 121 +++++++++++++++++++++++++++++++++++++++++ cli/cmd/user/update.go | 36 ++++++++++-- cli/config/config.go | 2 +- cli/functions/user.go | 39 ++++++++++++- logic/auth.go | 6 +- 9 files changed, 356 insertions(+), 20 deletions(-) create mode 100644 cli/cmd/user/groups.go create mode 100644 cli/cmd/user/roles.go diff --git a/cli/cmd/user/create.go b/cli/cmd/user/create.go index dc1e2a0b5..16af503d5 100644 --- a/cli/cmd/user/create.go +++ b/cli/cmd/user/create.go @@ -1,6 +1,8 @@ package user import ( + "strings" + "github.com/gravitl/netmaker/cli/functions" "github.com/gravitl/netmaker/models" "github.com/spf13/cobra" @@ -12,18 +14,41 @@ var userCreateCmd = &cobra.Command{ Short: "Create a new user", Long: `Create a new user`, Run: func(cmd *cobra.Command, args []string) { - user := &models.User{UserName: username, Password: password, IsAdmin: admin} + user := &models.User{UserName: username, Password: password, PlatformRoleID: models.UserRoleID(platformID)} + if len(networkRoles) > 0 { + netRolesMap := make(map[models.NetworkID]map[models.UserRoleID]struct{}) + for netID, netRoles := range networkRoles { + roleMap := make(map[models.UserRoleID]struct{}) + for _, roleID := range strings.Split(netRoles, " ") { + roleMap[models.UserRoleID(roleID)] = struct{}{} + } + netRolesMap[models.NetworkID(netID)] = roleMap + } + user.NetworkRoles = netRolesMap + } + if len(groups) > 0 { + grMap := make(map[models.UserGroupID]struct{}) + for _, groupID := range groups { + grMap[models.UserGroupID(groupID)] = struct{}{} + } + user.UserGroups = grMap + } + functions.PrettyPrint(functions.CreateUser(user)) }, } func init() { + userCreateCmd.Flags().StringVar(&username, "name", "", "Name of the user") userCreateCmd.Flags().StringVar(&password, "password", "", "Password of the user") + userCreateCmd.Flags().StringVarP(&platformID, "platform-role", "r", models.ServiceUser.String(), + "Platform Role of the user; run `nmctl roles list` to see available user roles") userCreateCmd.MarkFlagRequired("name") userCreateCmd.MarkFlagRequired("password") - userCreateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ?") - userCreateCmd.Flags().StringVar(&networks, "networks", "", "List of networks the user will access to (comma separated)") - userCreateCmd.Flags().StringVar(&groups, "groups", "", "List of user groups the user will be part of (comma separated)") + userCreateCmd.PersistentFlags().StringToStringVarP(&networkRoles, "network-roles", "n", nil, + "Mapping of networkID and list of roles user will be part of (comma separated)") + userCreateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ? (deprecated v0.25.0 onwards)") + userCreateCmd.Flags().StringArrayVarP(&groups, "groups", "g", nil, "List of user groups the user will be part of (comma separated)") rootCmd.AddCommand(userCreateCmd) } diff --git a/cli/cmd/user/flags.go b/cli/cmd/user/flags.go index 57f07843f..061e8f32b 100644 --- a/cli/cmd/user/flags.go +++ b/cli/cmd/user/flags.go @@ -1,9 +1,10 @@ package user var ( - username string - password string - admin bool - networks string - groups string + username string + password string + platformID string + admin bool + networkRoles map[string]string + groups []string ) diff --git a/cli/cmd/user/groups.go b/cli/cmd/user/groups.go new file mode 100644 index 000000000..0406083e1 --- /dev/null +++ b/cli/cmd/user/groups.go @@ -0,0 +1,118 @@ +package user + +import ( + "fmt" + "os" + "strings" + + "github.com/gravitl/netmaker/cli/cmd/commons" + "github.com/gravitl/netmaker/cli/functions" + "github.com/guumaster/tablewriter" + "github.com/spf13/cobra" +) + +var userGroupCmd = &cobra.Command{ + Use: "group", + Args: cobra.NoArgs, + Short: "Manage User Groups", + Long: `Manage User Groups`, +} + +var userGroupListCmd = &cobra.Command{ + Use: "list", + Args: cobra.NoArgs, + Short: "List all user groups", + Long: `List all user groups`, + Run: func(cmd *cobra.Command, args []string) { + data := functions.ListUserGrps() + switch commons.OutputFormat { + case commons.JsonOutput: + functions.PrettyPrint(data) + default: + table := tablewriter.NewWriter(os.Stdout) + h := []string{"ID", "MetaData", "Network Roles"} + table.SetHeader(h) + for _, d := range data { + + roleInfoStr := "" + for netID, netRoleMap := range d.NetworkRoles { + roleList := []string{} + for roleID := range netRoleMap { + roleList = append(roleList, roleID.String()) + } + roleInfoStr += fmt.Sprintf("[%s]: %s", netID, strings.Join(roleList, ",")) + } + e := []string{d.ID.String(), d.MetaData, roleInfoStr} + table.Append(e) + } + table.Render() + } + }, +} + +var userGroupCreateCmd = &cobra.Command{ + Use: "create", + Args: cobra.NoArgs, + Short: "create user group", + Long: `create user group`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("CLI doesn't support creation of groups currently. Visit the dashboard to create one or refer to our api documentation https://docs.v2.netmaker.io/reference") + }, +} + +var userGroupDeleteCmd = &cobra.Command{ + Use: "delete [groupID]", + Args: cobra.ExactArgs(1), + Short: "delete user group", + Long: `delete user group`, + Run: func(cmd *cobra.Command, args []string) { + resp := functions.DeleteUserGrp(args[0]) + if resp != nil { + fmt.Println(resp.Message) + } + }, +} + +var userGroupGetCmd = &cobra.Command{ + Use: "get [groupID]", + Args: cobra.ExactArgs(1), + Short: "get user group", + Long: `get user group`, + Run: func(cmd *cobra.Command, args []string) { + data := functions.GetUserGrp(args[0]) + switch commons.OutputFormat { + case commons.JsonOutput: + functions.PrettyPrint(data) + default: + table := tablewriter.NewWriter(os.Stdout) + h := []string{"ID", "MetaData", "Network Roles"} + table.SetHeader(h) + roleInfoStr := "" + for netID, netRoleMap := range data.NetworkRoles { + roleList := []string{} + for roleID := range netRoleMap { + roleList = append(roleList, roleID.String()) + } + roleInfoStr += fmt.Sprintf("[%s]: %s", netID, strings.Join(roleList, ",")) + } + e := []string{data.ID.String(), data.MetaData, roleInfoStr} + table.Append(e) + table.Render() + } + }, +} + +func init() { + rootCmd.AddCommand(userGroupCmd) + // list roles cmd + userGroupCmd.AddCommand(userGroupListCmd) + + // create roles cmd + userGroupCmd.AddCommand(userGroupCreateCmd) + + // delete role cmd + userGroupCmd.AddCommand(userGroupDeleteCmd) + + // Get Role + userGroupCmd.AddCommand(userGroupGetCmd) +} diff --git a/cli/cmd/user/list.go b/cli/cmd/user/list.go index 694697a28..68308f62a 100644 --- a/cli/cmd/user/list.go +++ b/cli/cmd/user/list.go @@ -2,7 +2,7 @@ package user import ( "os" - "strconv" + "strings" "github.com/gravitl/netmaker/cli/cmd/commons" "github.com/gravitl/netmaker/cli/functions" @@ -22,9 +22,13 @@ var userListCmd = &cobra.Command{ functions.PrettyPrint(data) default: table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Name", "SuperAdmin", "Admin"}) + table.SetHeader([]string{"Name", "Platform Role", "Groups"}) for _, d := range *data { - table.Append([]string{d.UserName, strconv.FormatBool(d.IsSuperAdmin), strconv.FormatBool(d.IsAdmin)}) + g := []string{} + for gID := range d.UserGroups { + g = append(g, gID.String()) + } + table.Append([]string{d.UserName, d.PlatformRoleID.String(), strings.Join(g, ",")}) } table.Render() } diff --git a/cli/cmd/user/roles.go b/cli/cmd/user/roles.go new file mode 100644 index 000000000..2bb880ef5 --- /dev/null +++ b/cli/cmd/user/roles.go @@ -0,0 +1,121 @@ +package user + +import ( + "fmt" + "os" + "strconv" + + "github.com/gravitl/netmaker/cli/cmd/commons" + "github.com/gravitl/netmaker/cli/functions" + "github.com/guumaster/tablewriter" + "github.com/spf13/cobra" +) + +var userRoleCmd = &cobra.Command{ + Use: "role", + Args: cobra.NoArgs, + Short: "Manage User Roles", + Long: `Manage User Roles`, +} + +// List Roles +var ( + platformRoles bool +) +var userRoleListCmd = &cobra.Command{ + Use: "list", + Args: cobra.NoArgs, + Short: "List all user roles", + Long: `List all user roles`, + Run: func(cmd *cobra.Command, args []string) { + data := functions.ListUserRoles() + switch commons.OutputFormat { + case commons.JsonOutput: + functions.PrettyPrint(data) + default: + table := tablewriter.NewWriter(os.Stdout) + h := []string{"ID", "Default", "Dashboard Access", "Full Access"} + + if !platformRoles { + h = append(h, "Network") + } + table.SetHeader(h) + for _, d := range data { + e := []string{d.ID.String(), strconv.FormatBool(d.Default), strconv.FormatBool(d.DenyDashboardAccess), strconv.FormatBool(d.FullAccess)} + if !platformRoles { + e = append(e, d.NetworkID.String()) + } + table.Append(e) + } + table.Render() + } + }, +} + +var userRoleCreateCmd = &cobra.Command{ + Use: "create", + Args: cobra.NoArgs, + Short: "create user role", + Long: `create user role`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("CLI doesn't support creation of roles currently. Visit the dashboard to create one or refer to our api documentation https://docs.v2.netmaker.io/reference") + }, +} + +var userRoleDeleteCmd = &cobra.Command{ + Use: "delete [roleID]", + Args: cobra.ExactArgs(1), + Short: "delete user role", + Long: `delete user role`, + Run: func(cmd *cobra.Command, args []string) { + resp := functions.DeleteUserRole(args[0]) + if resp != nil { + fmt.Println(resp.Message) + } + }, +} + +var userRoleGetCmd = &cobra.Command{ + Use: "get [roleID]", + Args: cobra.ExactArgs(1), + Short: "get user role", + Long: `get user role`, + Run: func(cmd *cobra.Command, args []string) { + d := functions.GetUserRole(args[0]) + switch commons.OutputFormat { + case commons.JsonOutput: + functions.PrettyPrint(d) + default: + table := tablewriter.NewWriter(os.Stdout) + h := []string{"ID", "Default Role", "Dashboard Access", "Full Access"} + + if d.NetworkID != "" { + h = append(h, "Network") + } + table.SetHeader(h) + e := []string{d.ID.String(), strconv.FormatBool(d.Default), strconv.FormatBool(!d.DenyDashboardAccess), strconv.FormatBool(d.FullAccess)} + if !platformRoles { + e = append(e, d.NetworkID.String()) + } + table.Append(e) + table.Render() + } + }, +} + +func init() { + rootCmd.AddCommand(userRoleCmd) + // list roles cmd + userRoleListCmd.Flags().BoolVar(&platformRoles, "platform-roles", true, + "set to false to list network roles. By default it will only list platform roles") + userRoleCmd.AddCommand(userRoleListCmd) + + // create roles cmd + userRoleCmd.AddCommand(userRoleCreateCmd) + + // delete role cmd + userRoleCmd.AddCommand(userRoleDeleteCmd) + + // Get Role + userRoleCmd.AddCommand(userRoleGetCmd) +} diff --git a/cli/cmd/user/update.go b/cli/cmd/user/update.go index 0a79f26a3..31e9cceef 100644 --- a/cli/cmd/user/update.go +++ b/cli/cmd/user/update.go @@ -1,6 +1,8 @@ package user import ( + "strings" + "github.com/gravitl/netmaker/cli/functions" "github.com/gravitl/netmaker/models" "github.com/spf13/cobra" @@ -12,14 +14,40 @@ var userUpdateCmd = &cobra.Command{ Short: "Update a user", Long: `Update a user`, Run: func(cmd *cobra.Command, args []string) { - user := &models.User{UserName: args[0], IsAdmin: admin} + user := &models.User{UserName: args[0]} + if platformID != "" { + user.PlatformRoleID = models.UserRoleID(platformID) + } + if len(networkRoles) > 0 { + netRolesMap := make(map[models.NetworkID]map[models.UserRoleID]struct{}) + for netID, netRoles := range networkRoles { + roleMap := make(map[models.UserRoleID]struct{}) + for _, roleID := range strings.Split(netRoles, ",") { + roleMap[models.UserRoleID(roleID)] = struct{}{} + } + netRolesMap[models.NetworkID(netID)] = roleMap + } + user.NetworkRoles = netRolesMap + } + if len(groups) > 0 { + grMap := make(map[models.UserGroupID]struct{}) + for _, groupID := range groups { + grMap[models.UserGroupID(groupID)] = struct{}{} + } + user.UserGroups = grMap + } functions.PrettyPrint(functions.UpdateUser(user)) }, } func init() { - userUpdateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ?") - userUpdateCmd.Flags().StringVar(&networks, "networks", "", "List of networks the user will access to (comma separated)") - userUpdateCmd.Flags().StringVar(&groups, "groups", "", "List of user groups the user will be part of (comma separated)") + + userUpdateCmd.Flags().StringVar(&password, "password", "", "Password of the user") + userUpdateCmd.Flags().StringVarP(&platformID, "platform-role", "r", "", + "Platform Role of the user; run `nmctl roles list` to see available user roles") + userUpdateCmd.PersistentFlags().StringToStringVarP(&networkRoles, "network-roles", "n", nil, + "Mapping of networkID and list of roles user will be part of (comma separated)") + userUpdateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ? (deprecated v0.25.0 onwards)") + userUpdateCmd.Flags().StringArrayVarP(&groups, "groups", "g", nil, "List of user groups the user will be part of (comma separated)") rootCmd.AddCommand(userUpdateCmd) } diff --git a/cli/config/config.go b/cli/config/config.go index ba32c48dd..444ad1664 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -86,7 +86,7 @@ func GetCurrentContext() (name string, ctx Context) { return } } - log.Fatalf("No current context set, do so via `netmaker context use `") + log.Fatalf("No current context set, do so via `nmctl context use `") return } diff --git a/cli/functions/user.go b/cli/functions/user.go index b286bc4e8..e5572bae1 100644 --- a/cli/functions/user.go +++ b/cli/functions/user.go @@ -1,6 +1,8 @@ package functions import ( + "encoding/json" + "fmt" "net/http" "github.com/gravitl/netmaker/models" @@ -18,7 +20,7 @@ func CreateUser(payload *models.User) *models.User { // UpdateUser - update a user func UpdateUser(payload *models.User) *models.User { - return request[models.User](http.MethodPut, "/api/users/networks/"+payload.UserName, payload) + return request[models.User](http.MethodPut, "/api/users/"+payload.UserName, payload) } // DeleteUser - delete a user @@ -35,3 +37,38 @@ func GetUser(username string) *models.User { func ListUsers() *[]models.ReturnUser { return request[[]models.ReturnUser](http.MethodGet, "/api/users", nil) } + +func ListUserRoles() (roles []models.UserRolePermissionTemplate) { + resp := request[models.SuccessResponse](http.MethodGet, "/api/v1/users/roles", nil) + d, _ := json.Marshal(resp.Response) + json.Unmarshal(d, &roles) + return +} + +func DeleteUserRole(roleID string) *models.SuccessResponse { + return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/v1/users/role?role_id=%s", roleID), nil) +} +func GetUserRole(roleID string) (role models.UserRolePermissionTemplate) { + resp := request[models.SuccessResponse](http.MethodGet, fmt.Sprintf("/api/v1/users/role?role_id=%s", roleID), nil) + d, _ := json.Marshal(resp.Response) + json.Unmarshal(d, &role) + return +} + +func ListUserGrps() (groups []models.UserGroup) { + resp := request[models.SuccessResponse](http.MethodGet, "/api/v1/users/groups", nil) + d, _ := json.Marshal(resp.Response) + json.Unmarshal(d, &groups) + return +} + +func DeleteUserGrp(grpID string) *models.SuccessResponse { + return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/v1/users/group?group_id=%s", grpID), nil) +} + +func GetUserGrp(grpID string) (group models.UserGroup) { + resp := request[models.SuccessResponse](http.MethodGet, fmt.Sprintf("/api/v1/users/group?group_id=%s", grpID), nil) + d, _ := json.Marshal(resp.Response) + json.Unmarshal(d, &group) + return +} diff --git a/logic/auth.go b/logic/auth.go index 3e2623ff2..7486d310e 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -297,7 +297,9 @@ func UpdateUser(userchange, user *models.User) (*models.User, error) { } // Reset Gw Access for service users go UpdateUserGwAccess(*user, *userchange) - user.PlatformRoleID = userchange.PlatformRoleID + if userchange.PlatformRoleID != "" { + user.PlatformRoleID = userchange.PlatformRoleID + } user.UserGroups = userchange.UserGroups user.NetworkRoles = userchange.NetworkRoles err := ValidateUser(user) @@ -325,7 +327,7 @@ func ValidateUser(user *models.User) error { // check if role is valid _, err := GetRole(user.PlatformRoleID) if err != nil { - return err + return errors.New("failed to fetch platform role " + user.PlatformRoleID.String()) } v := validator.New() _ = v.RegisterValidation("in_charset", func(fl validator.FieldLevel) bool {