github.com/minio/madmin-go/v3@v3.0.51/user-commands.go (about)

     1  //
     2  // Copyright (c) 2015-2022 MinIO, Inc.
     3  //
     4  // This file is part of MinIO Object Storage stack
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU Affero General Public License as
     8  // published by the Free Software Foundation, either version 3 of the
     9  // License, or (at your option) any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU Affero General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU Affero General Public License
    17  // along with this program. If not, see <http://www.gnu.org/licenses/>.
    18  //
    19  
    20  package madmin
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"errors"
    26  	"io"
    27  	"net/http"
    28  	"net/url"
    29  	"regexp"
    30  	"time"
    31  
    32  	"github.com/minio/minio-go/v7/pkg/tags"
    33  )
    34  
    35  // AccountAccess contains information about
    36  type AccountAccess struct {
    37  	Read  bool `json:"read"`
    38  	Write bool `json:"write"`
    39  }
    40  
    41  // BucketDetails provides information about features currently
    42  // turned-on per bucket.
    43  type BucketDetails struct {
    44  	Versioning          bool         `json:"versioning"`
    45  	VersioningSuspended bool         `json:"versioningSuspended"`
    46  	Locking             bool         `json:"locking"`
    47  	Replication         bool         `json:"replication"`
    48  	Tagging             *tags.Tags   `json:"tags"`
    49  	Quota               *BucketQuota `json:"quota"`
    50  }
    51  
    52  // BucketAccessInfo represents bucket usage of a bucket, and its relevant
    53  // access type for an account
    54  type BucketAccessInfo struct {
    55  	Name                    string            `json:"name"`
    56  	Size                    uint64            `json:"size"`
    57  	Objects                 uint64            `json:"objects"`
    58  	ObjectSizesHistogram    map[string]uint64 `json:"objectHistogram"`
    59  	ObjectVersionsHistogram map[string]uint64 `json:"objectsVersionsHistogram"`
    60  	Details                 *BucketDetails    `json:"details"`
    61  	PrefixUsage             map[string]uint64 `json:"prefixUsage"`
    62  	Created                 time.Time         `json:"created"`
    63  	Access                  AccountAccess     `json:"access"`
    64  }
    65  
    66  // AccountInfo represents the account usage info of an
    67  // account across buckets.
    68  type AccountInfo struct {
    69  	AccountName string
    70  	Server      BackendInfo
    71  	Policy      json.RawMessage // Use iam/policy.Parse to parse the result, to be done by the caller.
    72  	Buckets     []BucketAccessInfo
    73  }
    74  
    75  // AccountOpts allows for configurable behavior with "prefix-usage"
    76  type AccountOpts struct {
    77  	PrefixUsage bool
    78  }
    79  
    80  // AccountInfo returns the usage info for the authenticating account.
    81  func (adm *AdminClient) AccountInfo(ctx context.Context, opts AccountOpts) (AccountInfo, error) {
    82  	q := make(url.Values)
    83  	if opts.PrefixUsage {
    84  		q.Set("prefix-usage", "true")
    85  	}
    86  	resp, err := adm.executeMethod(ctx, http.MethodGet,
    87  		requestData{
    88  			relPath:     adminAPIPrefix + "/accountinfo",
    89  			queryValues: q,
    90  		},
    91  	)
    92  	defer closeResponse(resp)
    93  	if err != nil {
    94  		return AccountInfo{}, err
    95  	}
    96  
    97  	// Check response http status code
    98  	if resp.StatusCode != http.StatusOK {
    99  		return AccountInfo{}, httpRespToErrorResponse(resp)
   100  	}
   101  
   102  	// Unmarshal the server's json response
   103  	var accountInfo AccountInfo
   104  
   105  	respBytes, err := io.ReadAll(resp.Body)
   106  	if err != nil {
   107  		return AccountInfo{}, err
   108  	}
   109  
   110  	err = json.Unmarshal(respBytes, &accountInfo)
   111  	if err != nil {
   112  		return AccountInfo{}, err
   113  	}
   114  
   115  	return accountInfo, nil
   116  }
   117  
   118  // AccountStatus - account status.
   119  type AccountStatus string
   120  
   121  // Account status per user.
   122  const (
   123  	AccountEnabled  AccountStatus = "enabled"
   124  	AccountDisabled AccountStatus = "disabled"
   125  )
   126  
   127  // UserAuthType indicates the type of authentication for the user.
   128  type UserAuthType string
   129  
   130  // Valid values for UserAuthType.
   131  const (
   132  	BuiltinUserAuthType UserAuthType = "builtin"
   133  	LDAPUserAuthType                 = "ldap"
   134  )
   135  
   136  // UserAuthInfo contains info about how the user is authenticated.
   137  type UserAuthInfo struct {
   138  	Type UserAuthType `json:"type"`
   139  
   140  	// Specifies the external server that authenticated the server (empty for
   141  	// builtin IDP)
   142  	AuthServer string `json:"authServer,omitempty"`
   143  
   144  	// Specifies the user ID as present in the external auth server (e.g. in
   145  	// OIDC could be the email of the user). For builtin, this would be the same
   146  	// as the access key.
   147  	AuthServerUserID string `json:"authServerUserID,omitempty"`
   148  }
   149  
   150  // UserInfo carries information about long term users.
   151  type UserInfo struct {
   152  	AuthInfo   *UserAuthInfo `json:"userAuthInfo,omitempty"`
   153  	SecretKey  string        `json:"secretKey,omitempty"`
   154  	PolicyName string        `json:"policyName,omitempty"`
   155  	Status     AccountStatus `json:"status"`
   156  	MemberOf   []string      `json:"memberOf,omitempty"`
   157  	UpdatedAt  time.Time     `json:"updatedAt,omitempty"`
   158  }
   159  
   160  // RemoveUser - remove a user.
   161  func (adm *AdminClient) RemoveUser(ctx context.Context, accessKey string) error {
   162  	queryValues := url.Values{}
   163  	queryValues.Set("accessKey", accessKey)
   164  
   165  	reqData := requestData{
   166  		relPath:     adminAPIPrefix + "/remove-user",
   167  		queryValues: queryValues,
   168  	}
   169  
   170  	// Execute DELETE on /minio/admin/v3/remove-user to remove a user.
   171  	resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData)
   172  
   173  	defer closeResponse(resp)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	if resp.StatusCode != http.StatusOK {
   179  		return httpRespToErrorResponse(resp)
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  // ListUsers - list all users.
   186  func (adm *AdminClient) ListUsers(ctx context.Context) (map[string]UserInfo, error) {
   187  	reqData := requestData{
   188  		relPath: adminAPIPrefix + "/list-users",
   189  	}
   190  
   191  	// Execute GET on /minio/admin/v3/list-users
   192  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   193  
   194  	defer closeResponse(resp)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	if resp.StatusCode != http.StatusOK {
   200  		return nil, httpRespToErrorResponse(resp)
   201  	}
   202  
   203  	data, err := DecryptData(adm.getSecretKey(), resp.Body)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	users := make(map[string]UserInfo)
   209  	if err = json.Unmarshal(data, &users); err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	return users, nil
   214  }
   215  
   216  // GetUserInfo - get info on a user
   217  func (adm *AdminClient) GetUserInfo(ctx context.Context, name string) (u UserInfo, err error) {
   218  	queryValues := url.Values{}
   219  	queryValues.Set("accessKey", name)
   220  
   221  	reqData := requestData{
   222  		relPath:     adminAPIPrefix + "/user-info",
   223  		queryValues: queryValues,
   224  	}
   225  
   226  	// Execute GET on /minio/admin/v3/user-info
   227  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   228  
   229  	defer closeResponse(resp)
   230  	if err != nil {
   231  		return u, err
   232  	}
   233  
   234  	if resp.StatusCode != http.StatusOK {
   235  		return u, httpRespToErrorResponse(resp)
   236  	}
   237  
   238  	b, err := io.ReadAll(resp.Body)
   239  	if err != nil {
   240  		return u, err
   241  	}
   242  
   243  	if err = json.Unmarshal(b, &u); err != nil {
   244  		return u, err
   245  	}
   246  
   247  	return u, nil
   248  }
   249  
   250  // AddOrUpdateUserReq allows to update
   251  //   - user details such as secret key
   252  //   - account status.
   253  //   - optionally a comma separated list of policies
   254  //     to be applied for the user.
   255  type AddOrUpdateUserReq struct {
   256  	SecretKey string        `json:"secretKey,omitempty"`
   257  	Policy    string        `json:"policy,omitempty"`
   258  	Status    AccountStatus `json:"status"`
   259  }
   260  
   261  // SetUserReq - update user secret key, account status or policies.
   262  func (adm *AdminClient) SetUserReq(ctx context.Context, accessKey string, req AddOrUpdateUserReq) error {
   263  	data, err := json.Marshal(req)
   264  	if err != nil {
   265  		return err
   266  	}
   267  	econfigBytes, err := EncryptData(adm.getSecretKey(), data)
   268  	if err != nil {
   269  		return err
   270  	}
   271  
   272  	queryValues := url.Values{}
   273  	queryValues.Set("accessKey", accessKey)
   274  
   275  	reqData := requestData{
   276  		relPath:     adminAPIPrefix + "/add-user",
   277  		queryValues: queryValues,
   278  		content:     econfigBytes,
   279  	}
   280  
   281  	// Execute PUT on /minio/admin/v3/add-user to set a user.
   282  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   283  
   284  	defer closeResponse(resp)
   285  	if err != nil {
   286  		return err
   287  	}
   288  
   289  	if resp.StatusCode != http.StatusOK {
   290  		return httpRespToErrorResponse(resp)
   291  	}
   292  
   293  	return nil
   294  }
   295  
   296  // SetUser - update user secret key or account status.
   297  func (adm *AdminClient) SetUser(ctx context.Context, accessKey, secretKey string, status AccountStatus) error {
   298  	return adm.SetUserReq(ctx, accessKey, AddOrUpdateUserReq{
   299  		SecretKey: secretKey,
   300  		Status:    status,
   301  	})
   302  }
   303  
   304  // AddUser - adds a user.
   305  func (adm *AdminClient) AddUser(ctx context.Context, accessKey, secretKey string) error {
   306  	return adm.SetUser(ctx, accessKey, secretKey, AccountEnabled)
   307  }
   308  
   309  // SetUserStatus - adds a status for a user.
   310  func (adm *AdminClient) SetUserStatus(ctx context.Context, accessKey string, status AccountStatus) error {
   311  	queryValues := url.Values{}
   312  	queryValues.Set("accessKey", accessKey)
   313  	queryValues.Set("status", string(status))
   314  
   315  	reqData := requestData{
   316  		relPath:     adminAPIPrefix + "/set-user-status",
   317  		queryValues: queryValues,
   318  	}
   319  
   320  	// Execute PUT on /minio/admin/v3/set-user-status to set status.
   321  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   322  
   323  	defer closeResponse(resp)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	if resp.StatusCode != http.StatusOK {
   329  		return httpRespToErrorResponse(resp)
   330  	}
   331  
   332  	return nil
   333  }
   334  
   335  // AddServiceAccountReq is the request options of the add service account admin call
   336  type AddServiceAccountReq struct {
   337  	Policy     json.RawMessage `json:"policy,omitempty"` // Parsed value from iam/policy.Parse()
   338  	TargetUser string          `json:"targetUser,omitempty"`
   339  	AccessKey  string          `json:"accessKey,omitempty"`
   340  	SecretKey  string          `json:"secretKey,omitempty"`
   341  
   342  	// Name for this access key
   343  	Name string `json:"name,omitempty"`
   344  	// Description for this access key
   345  	Description string `json:"description,omitempty"`
   346  	// Time at which this access key expires
   347  	Expiration *time.Time `json:"expiration,omitempty"`
   348  
   349  	// Deprecated: use description instead
   350  	Comment string `json:"comment,omitempty"`
   351  }
   352  
   353  var serviceAcctValidNameRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_-]*`)
   354  
   355  func validateSAName(name string) error {
   356  	if name == "" {
   357  		return nil
   358  	}
   359  	if len(name) > 32 {
   360  		return errors.New("name must not be longer than 32 characters")
   361  	}
   362  	if !serviceAcctValidNameRegex.MatchString(name) {
   363  		return errors.New("name must contain only ASCII letters, digits, underscores and hyphens and must start with a letter")
   364  	}
   365  	return nil
   366  }
   367  
   368  func validateSADescription(desc string) error {
   369  	if desc == "" {
   370  		return nil
   371  	}
   372  	if len(desc) > 256 {
   373  		return errors.New("description must be at most 256 bytes long")
   374  	}
   375  	return nil
   376  }
   377  
   378  // Validate validates the request parameters.
   379  func (r *AddServiceAccountReq) Validate() error {
   380  	err := validateSAName(r.Name)
   381  	if err != nil {
   382  		return err
   383  	}
   384  	if r.Expiration != nil && r.Expiration.Before(time.Now()) {
   385  		return errors.New("the expiration time should be in the future")
   386  	}
   387  	return validateSADescription(r.Description)
   388  }
   389  
   390  // AddServiceAccountResp is the response body of the add service account admin call
   391  type AddServiceAccountResp struct {
   392  	Credentials Credentials `json:"credentials"`
   393  }
   394  
   395  // AddServiceAccount - creates a new service account belonging to the user sending
   396  // the request while restricting the service account permission by the given policy document.
   397  func (adm *AdminClient) AddServiceAccount(ctx context.Context, opts AddServiceAccountReq) (Credentials, error) {
   398  	if err := opts.Validate(); err != nil {
   399  		return Credentials{}, err
   400  	}
   401  	data, err := json.Marshal(opts)
   402  	if err != nil {
   403  		return Credentials{}, err
   404  	}
   405  
   406  	econfigBytes, err := EncryptData(adm.getSecretKey(), data)
   407  	if err != nil {
   408  		return Credentials{}, err
   409  	}
   410  
   411  	reqData := requestData{
   412  		relPath: adminAPIPrefix + "/add-service-account",
   413  		content: econfigBytes,
   414  	}
   415  
   416  	// Execute PUT on /minio/admin/v3/add-service-account to set a user.
   417  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   418  	defer closeResponse(resp)
   419  	if err != nil {
   420  		return Credentials{}, err
   421  	}
   422  
   423  	if resp.StatusCode != http.StatusOK {
   424  		return Credentials{}, httpRespToErrorResponse(resp)
   425  	}
   426  
   427  	data, err = DecryptData(adm.getSecretKey(), resp.Body)
   428  	if err != nil {
   429  		return Credentials{}, err
   430  	}
   431  
   432  	var serviceAccountResp AddServiceAccountResp
   433  	if err = json.Unmarshal(data, &serviceAccountResp); err != nil {
   434  		return Credentials{}, err
   435  	}
   436  	return serviceAccountResp.Credentials, nil
   437  }
   438  
   439  // AddServiceAccountLDAP - AddServiceAccount with extra features, restricted to LDAP users.
   440  func (adm *AdminClient) AddServiceAccountLDAP(ctx context.Context, opts AddServiceAccountReq) (Credentials, error) {
   441  	if err := opts.Validate(); err != nil {
   442  		return Credentials{}, err
   443  	}
   444  	data, err := json.Marshal(opts)
   445  	if err != nil {
   446  		return Credentials{}, err
   447  	}
   448  
   449  	econfigBytes, err := EncryptData(adm.getSecretKey(), data)
   450  	if err != nil {
   451  		return Credentials{}, err
   452  	}
   453  
   454  	reqData := requestData{
   455  		relPath: adminAPIPrefix + "/idp/ldap/add-service-account",
   456  		content: econfigBytes,
   457  	}
   458  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   459  	defer closeResponse(resp)
   460  	if err != nil {
   461  		return Credentials{}, err
   462  	}
   463  
   464  	if resp.StatusCode != http.StatusOK {
   465  		return Credentials{}, httpRespToErrorResponse(resp)
   466  	}
   467  
   468  	data, err = DecryptData(adm.getSecretKey(), resp.Body)
   469  	if err != nil {
   470  		return Credentials{}, err
   471  	}
   472  
   473  	var serviceAccountResp AddServiceAccountResp
   474  	if err = json.Unmarshal(data, &serviceAccountResp); err != nil {
   475  		return Credentials{}, err
   476  	}
   477  	return serviceAccountResp.Credentials, nil
   478  }
   479  
   480  // UpdateServiceAccountReq is the request options of the edit service account admin call
   481  type UpdateServiceAccountReq struct {
   482  	NewPolicy      json.RawMessage `json:"newPolicy,omitempty"` // Parsed policy from iam/policy.Parse
   483  	NewSecretKey   string          `json:"newSecretKey,omitempty"`
   484  	NewStatus      string          `json:"newStatus,omitempty"`
   485  	NewName        string          `json:"newName,omitempty"`
   486  	NewDescription string          `json:"newDescription,omitempty"`
   487  	NewExpiration  *time.Time      `json:"newExpiration,omitempty"`
   488  }
   489  
   490  func (u *UpdateServiceAccountReq) Validate() error {
   491  	if err := validateSAName(u.NewName); err != nil {
   492  		return err
   493  	}
   494  
   495  	if u.NewExpiration != nil && u.NewExpiration.Before(time.Now()) {
   496  		return errors.New("the expiration time should be in the future")
   497  	}
   498  	return validateSADescription(u.NewDescription)
   499  }
   500  
   501  // UpdateServiceAccount - edit an existing service account
   502  func (adm *AdminClient) UpdateServiceAccount(ctx context.Context, accessKey string, opts UpdateServiceAccountReq) error {
   503  	if err := opts.Validate(); err != nil {
   504  		return err
   505  	}
   506  	data, err := json.Marshal(opts)
   507  	if err != nil {
   508  		return err
   509  	}
   510  
   511  	econfigBytes, err := EncryptData(adm.getSecretKey(), data)
   512  	if err != nil {
   513  		return err
   514  	}
   515  
   516  	queryValues := url.Values{}
   517  	queryValues.Set("accessKey", accessKey)
   518  
   519  	reqData := requestData{
   520  		relPath:     adminAPIPrefix + "/update-service-account",
   521  		content:     econfigBytes,
   522  		queryValues: queryValues,
   523  	}
   524  
   525  	// Execute POST on /minio/admin/v3/update-service-account to edit a service account
   526  	resp, err := adm.executeMethod(ctx, http.MethodPost, reqData)
   527  	defer closeResponse(resp)
   528  	if err != nil {
   529  		return err
   530  	}
   531  
   532  	if resp.StatusCode != http.StatusNoContent {
   533  		return httpRespToErrorResponse(resp)
   534  	}
   535  
   536  	return nil
   537  }
   538  
   539  type ServiceAccountInfo struct {
   540  	ParentUser    string     `json:"parentUser"`
   541  	AccountStatus string     `json:"accountStatus"`
   542  	ImpliedPolicy bool       `json:"impliedPolicy"`
   543  	AccessKey     string     `json:"accessKey"`
   544  	Name          string     `json:"name,omitempty"`
   545  	Description   string     `json:"description,omitempty"`
   546  	Expiration    *time.Time `json:"expiration,omitempty"`
   547  }
   548  
   549  // ListServiceAccountsResp is the response body of the list service accounts call
   550  type ListServiceAccountsResp struct {
   551  	Accounts []ServiceAccountInfo `json:"accounts"`
   552  }
   553  
   554  // ListServiceAccounts - list service accounts belonging to the specified user
   555  func (adm *AdminClient) ListServiceAccounts(ctx context.Context, user string) (ListServiceAccountsResp, error) {
   556  	queryValues := url.Values{}
   557  	queryValues.Set("user", user)
   558  
   559  	reqData := requestData{
   560  		relPath:     adminAPIPrefix + "/list-service-accounts",
   561  		queryValues: queryValues,
   562  	}
   563  
   564  	// Execute GET on /minio/admin/v3/list-service-accounts
   565  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   566  	defer closeResponse(resp)
   567  	if err != nil {
   568  		return ListServiceAccountsResp{}, err
   569  	}
   570  
   571  	if resp.StatusCode != http.StatusOK {
   572  		return ListServiceAccountsResp{}, httpRespToErrorResponse(resp)
   573  	}
   574  
   575  	data, err := DecryptData(adm.getSecretKey(), resp.Body)
   576  	if err != nil {
   577  		return ListServiceAccountsResp{}, err
   578  	}
   579  
   580  	var listResp ListServiceAccountsResp
   581  	if err = json.Unmarshal(data, &listResp); err != nil {
   582  		return ListServiceAccountsResp{}, err
   583  	}
   584  	return listResp, nil
   585  }
   586  
   587  // ListAccessKeysLDAPResp is the response body of the list service accounts call
   588  type ListAccessKeysLDAPResp struct {
   589  	ServiceAccounts []ServiceAccountInfo `json:"serviceAccounts"`
   590  	STSKeys         []ServiceAccountInfo `json:"stsKeys"`
   591  }
   592  
   593  // ListAccessKeysLDAP - list service accounts belonging to the specified user
   594  func (adm *AdminClient) ListAccessKeysLDAP(ctx context.Context, userDN string, listType string) (ListAccessKeysLDAPResp, error) {
   595  	queryValues := url.Values{}
   596  	queryValues.Set("listType", listType)
   597  	queryValues.Set("userDN", userDN)
   598  
   599  	reqData := requestData{
   600  		relPath:     adminAPIPrefix + "/idp/ldap/list-access-keys",
   601  		queryValues: queryValues,
   602  	}
   603  
   604  	// Execute GET on /minio/admin/v3/list-service-accounts
   605  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   606  	defer closeResponse(resp)
   607  	if err != nil {
   608  		return ListAccessKeysLDAPResp{}, err
   609  	}
   610  
   611  	if resp.StatusCode != http.StatusOK {
   612  		return ListAccessKeysLDAPResp{}, httpRespToErrorResponse(resp)
   613  	}
   614  
   615  	data, err := DecryptData(adm.getSecretKey(), resp.Body)
   616  	if err != nil {
   617  		return ListAccessKeysLDAPResp{}, err
   618  	}
   619  
   620  	var listResp ListAccessKeysLDAPResp
   621  	if err = json.Unmarshal(data, &listResp); err != nil {
   622  		return ListAccessKeysLDAPResp{}, err
   623  	}
   624  	return listResp, nil
   625  }
   626  
   627  // InfoServiceAccountResp is the response body of the info service account call
   628  type InfoServiceAccountResp struct {
   629  	ParentUser    string     `json:"parentUser"`
   630  	AccountStatus string     `json:"accountStatus"`
   631  	ImpliedPolicy bool       `json:"impliedPolicy"`
   632  	Policy        string     `json:"policy"`
   633  	Name          string     `json:"name,omitempty"`
   634  	Description   string     `json:"description,omitempty"`
   635  	Expiration    *time.Time `json:"expiration,omitempty"`
   636  }
   637  
   638  // InfoServiceAccount - returns the info of service account belonging to the specified user
   639  func (adm *AdminClient) InfoServiceAccount(ctx context.Context, accessKey string) (InfoServiceAccountResp, error) {
   640  	queryValues := url.Values{}
   641  	queryValues.Set("accessKey", accessKey)
   642  
   643  	reqData := requestData{
   644  		relPath:     adminAPIPrefix + "/info-service-account",
   645  		queryValues: queryValues,
   646  	}
   647  
   648  	// Execute GET on /minio/admin/v3/info-service-account
   649  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   650  	defer closeResponse(resp)
   651  	if err != nil {
   652  		return InfoServiceAccountResp{}, err
   653  	}
   654  
   655  	if resp.StatusCode != http.StatusOK {
   656  		return InfoServiceAccountResp{}, httpRespToErrorResponse(resp)
   657  	}
   658  
   659  	data, err := DecryptData(adm.getSecretKey(), resp.Body)
   660  	if err != nil {
   661  		return InfoServiceAccountResp{}, err
   662  	}
   663  
   664  	var infoResp InfoServiceAccountResp
   665  	if err = json.Unmarshal(data, &infoResp); err != nil {
   666  		return InfoServiceAccountResp{}, err
   667  	}
   668  	return infoResp, nil
   669  }
   670  
   671  // DeleteServiceAccount - delete a specified service account. The server will reject
   672  // the request if the service account does not belong to the user initiating the request
   673  func (adm *AdminClient) DeleteServiceAccount(ctx context.Context, serviceAccount string) error {
   674  	queryValues := url.Values{}
   675  	queryValues.Set("accessKey", serviceAccount)
   676  
   677  	reqData := requestData{
   678  		relPath:     adminAPIPrefix + "/delete-service-account",
   679  		queryValues: queryValues,
   680  	}
   681  
   682  	// Execute DELETE on /minio/admin/v3/delete-service-account
   683  	resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData)
   684  	defer closeResponse(resp)
   685  	if err != nil {
   686  		return err
   687  	}
   688  
   689  	if resp.StatusCode != http.StatusNoContent {
   690  		return httpRespToErrorResponse(resp)
   691  	}
   692  
   693  	return nil
   694  }
   695  
   696  // TemporaryAccountInfoResp is the response body of the info temporary call
   697  type TemporaryAccountInfoResp InfoServiceAccountResp
   698  
   699  // TemporaryAccountInfo - returns the info of a temporary account
   700  func (adm *AdminClient) TemporaryAccountInfo(ctx context.Context, accessKey string) (TemporaryAccountInfoResp, error) {
   701  	queryValues := url.Values{}
   702  	queryValues.Set("accessKey", accessKey)
   703  
   704  	reqData := requestData{
   705  		relPath:     adminAPIPrefix + "/temporary-account-info",
   706  		queryValues: queryValues,
   707  	}
   708  
   709  	// Execute GET on /minio/admin/v3/temporary-account-info
   710  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   711  	defer closeResponse(resp)
   712  	if err != nil {
   713  		return TemporaryAccountInfoResp{}, err
   714  	}
   715  
   716  	if resp.StatusCode != http.StatusOK {
   717  		return TemporaryAccountInfoResp{}, httpRespToErrorResponse(resp)
   718  	}
   719  
   720  	data, err := DecryptData(adm.getSecretKey(), resp.Body)
   721  	if err != nil {
   722  		return TemporaryAccountInfoResp{}, err
   723  	}
   724  
   725  	var infoResp TemporaryAccountInfoResp
   726  	if err = json.Unmarshal(data, &infoResp); err != nil {
   727  		return TemporaryAccountInfoResp{}, err
   728  	}
   729  	return infoResp, nil
   730  }