From 3ef3fefd5483696bd5d38f8cd9e2f37e6feaeed3 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sat, 13 Oct 2018 00:18:43 -0700 Subject: [PATCH] Add ListUsers API to list all configured users in IAM (#6619) --- cmd/admin-handlers.go | 44 +++++++++++++++++++++++++++++++++++++ cmd/admin-router.go | 5 +++++ cmd/iam.go | 21 ++++++++++++++++++ pkg/iam/policy/statement.go | 2 ++ pkg/madmin/API.md | 18 ++++++++++++++- pkg/madmin/user-commands.go | 33 +++++++++++++++++++++++++++- 6 files changed, 121 insertions(+), 2 deletions(-) diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 61e714702..dc1c58629 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -769,6 +769,48 @@ func (a adminAPIHandlers) RemoveUserPolicy(w http.ResponseWriter, r *http.Reques } } +// ListUsers - GET /minio/admin/v1/list-users +func (a adminAPIHandlers) ListUsers(w http.ResponseWriter, r *http.Request) { + ctx := newContext(r, w, "ListUsers") + + // Get current object layer instance. + objectAPI := newObjectLayerFn() + if objectAPI == nil { + writeErrorResponseJSON(w, ErrServerNotInitialized, r.URL) + return + } + + // Validate request signature. + adminAPIErr := checkAdminRequestAuthType(r, "") + if adminAPIErr != ErrNone { + writeErrorResponseJSON(w, adminAPIErr, r.URL) + return + } + + allCredentials, err := globalIAMSys.ListUsers() + if err != nil { + writeErrorResponseJSON(w, toAdminAPIErrCode(err), r.URL) + return + } + + data, err := json.Marshal(allCredentials) + if err != nil { + logger.LogIf(ctx, err) + writeErrorResponseJSON(w, ErrInternalError, r.URL) + return + } + + password := globalServerConfig.GetCredential().SecretKey + econfigData, err := madmin.EncryptData(password, data) + if err != nil { + logger.LogIf(ctx, err) + writeErrorResponseJSON(w, ErrInternalError, r.URL) + return + } + + writeSuccessResponseJSON(w, econfigData) +} + // AddUser - PUT /minio/admin/v1/add-user?accessKey= func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "AddUser") @@ -815,12 +857,14 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) { writeErrorResponseJSON(w, ErrAdminConfigBadJSON, r.URL) return } + var uinfo madmin.UserInfo if err = json.Unmarshal(configBytes, &uinfo); err != nil { logger.LogIf(ctx, err) writeErrorResponseJSON(w, ErrAdminConfigBadJSON, r.URL) return } + if err = globalIAMSys.SetUser(accessKey, uinfo); err != nil { logger.LogIf(ctx, err) writeErrorResponseJSON(w, ErrInternalError, r.URL) diff --git a/cmd/admin-router.go b/cmd/admin-router.go index cd4f81b6e..c91454a97 100644 --- a/cmd/admin-router.go +++ b/cmd/admin-router.go @@ -79,6 +79,8 @@ func registerAdminRouter(router *mux.Router) { // Set config keys/values adminV1Router.Methods(http.MethodPut).Path("/config-keys").HandlerFunc(httpTraceHdrs(adminAPI.SetConfigKeysHandler)) + // -- IAM APIs -- + // Add user IAM adminV1Router.Methods(http.MethodPut).Path("/add-user").HandlerFunc(httpTraceHdrs(adminAPI.AddUser)).Queries("accessKey", "{accessKey:.*}") adminV1Router.Methods(http.MethodPut).Path("/add-user-policy").HandlerFunc(httpTraceHdrs(adminAPI.AddUserPolicy)).Queries("accessKey", "{accessKey:.*}") @@ -87,4 +89,7 @@ func registerAdminRouter(router *mux.Router) { adminV1Router.Methods(http.MethodDelete).Path("/remove-user").HandlerFunc(httpTraceHdrs(adminAPI.RemoveUser)).Queries("accessKey", "{accessKey:.*}") adminV1Router.Methods(http.MethodDelete).Path("/remove-user-policy").HandlerFunc(httpTraceHdrs(adminAPI.RemoveUserPolicy)).Queries("accessKey", "{accessKey:.*}") + // List users + adminV1Router.Methods(http.MethodGet).Path("/list-users").HandlerFunc(httpTraceHdrs(adminAPI.ListUsers)) + } diff --git a/cmd/iam.go b/cmd/iam.go index 36f0d7151..372d2a983 100644 --- a/cmd/iam.go +++ b/cmd/iam.go @@ -256,6 +256,27 @@ func (sys *IAMSys) SetTempUser(accessKey string, cred auth.Credentials) error { return nil } +// ListUsers - list all users. +func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) { + objectAPI := newObjectLayerFn() + if objectAPI == nil { + return nil, errServerNotInitialized + } + + var users = make(map[string]madmin.UserInfo) + + sys.RLock() + defer sys.RUnlock() + + for k, v := range sys.iamUsersMap { + users[k] = madmin.UserInfo{ + Status: madmin.AccountStatus(v.Status), + } + } + + return users, nil +} + // SetUser - set user credentials. func (sys *IAMSys) SetUser(accessKey string, uinfo madmin.UserInfo) error { objectAPI := newObjectLayerFn() diff --git a/pkg/iam/policy/statement.go b/pkg/iam/policy/statement.go index 8e0d1ec99..e231c6c36 100644 --- a/pkg/iam/policy/statement.go +++ b/pkg/iam/policy/statement.go @@ -48,6 +48,8 @@ func (statement Statement) IsAllowed(args Args) bool { } resource += args.ObjectName + } else { + resource += "/" } if !statement.Resources.Match(resource) { diff --git a/pkg/madmin/API.md b/pkg/madmin/API.md index 47f024e7f..94ef5481a 100644 --- a/pkg/madmin/API.md +++ b/pkg/madmin/API.md @@ -40,7 +40,7 @@ func main() { |:----------------------------|:----------------------------|:--------------------------------------|:--------------------------|:------------------------------------|:------------------------------------| | [`ServiceStatus`](#ServiceStatus) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`AddUser()`](#AddUser) | [`SetAdminCredentials`](#SetAdminCredentials) | | [`ServiceSendAction`](#ServiceSendAction) | | | [`SetConfig`](#SetConfig) | [`AddUserPolicy`](#AddUserPolicy) | [`StartProfiling`](#StartProfiling) | -| | | | [`GetConfigKeys`](#GetConfigKeys) | [`DownloadProfilingData`](#DownloadProfilingData) | +| | | | [`GetConfigKeys`](#GetConfigKeys) | [`ListUsers`](#ListUsers) | [`DownloadProfilingData`](#DownloadProfilingData) | | | | | [`SetConfigKeys`](#SetConfigKeys) | | @@ -375,6 +375,22 @@ __Example__ } ``` + +### ListUsers() (map[string]UserInfo, error) +Lists all users on Minio server. + +__Example__ + +``` go + users, err := madmClnt.ListUsers(); + if err != nil { + log.Fatalln(err) + } + for k, v := range users { + fmt.Printf("User %s Status %s\n", k, v.Status) + } +``` + ## 9. Misc operations diff --git a/pkg/madmin/user-commands.go b/pkg/madmin/user-commands.go index 2ab513cb4..a31df5193 100644 --- a/pkg/madmin/user-commands.go +++ b/pkg/madmin/user-commands.go @@ -34,7 +34,7 @@ const ( // UserInfo carries information about long term users. type UserInfo struct { - SecretKey string `json:"secretKey"` + SecretKey string `json:"secretKey,omitempty"` Status AccountStatus `json:"status"` } @@ -63,6 +63,37 @@ func (adm *AdminClient) RemoveUser(accessKey string) error { return nil } +// ListUsers - list all users. +func (adm *AdminClient) ListUsers() (map[string]UserInfo, error) { + reqData := requestData{ + relPath: "/v1/list-users", + } + + // Execute GET on /minio/admin/v1/list-users + resp, err := adm.executeMethod("GET", reqData) + + defer closeResponse(resp) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, httpRespToErrorResponse(resp) + } + + data, err := DecryptData(adm.secretAccessKey, resp.Body) + if err != nil { + return nil, err + } + + var users = make(map[string]UserInfo) + if err = json.Unmarshal(data, &users); err != nil { + return nil, err + } + + return users, nil +} + // SetUser - sets a user info. func (adm *AdminClient) SetUser(accessKey, secretKey string, status AccountStatus) error { data, err := json.Marshal(UserInfo{