storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/admin-handlers-users.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2019-2020 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "io" 24 "io/ioutil" 25 "net/http" 26 27 "github.com/gorilla/mux" 28 29 "storj.io/minio/cmd/logger" 30 "storj.io/minio/pkg/auth" 31 iampolicy "storj.io/minio/pkg/iam/policy" 32 "storj.io/minio/pkg/madmin" 33 ) 34 35 func validateAdminUsersReq(ctx context.Context, w http.ResponseWriter, r *http.Request, action iampolicy.AdminAction) (ObjectLayer, auth.Credentials) { 36 var cred auth.Credentials 37 var adminAPIErr APIErrorCode 38 39 // Get current object layer instance. 40 objectAPI := newObjectLayerFn() 41 if objectAPI == nil || GlobalNotificationSys == nil { 42 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 43 return nil, cred 44 } 45 46 // Validate request signature. 47 cred, adminAPIErr = checkAdminRequestAuth(ctx, r, action, "") 48 if adminAPIErr != ErrNone { 49 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) 50 return nil, cred 51 } 52 53 return objectAPI, cred 54 } 55 56 // RemoveUser - DELETE /minio/admin/v3/remove-user?accessKey=<access_key> 57 func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) { 58 ctx := NewContext(r, w, "RemoveUser") 59 60 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 61 62 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.DeleteUserAdminAction) 63 if objectAPI == nil { 64 return 65 } 66 67 vars := mux.Vars(r) 68 accessKey := vars["accessKey"] 69 70 ok, _, err := GlobalIAMSys.IsTempUser(accessKey) 71 if err != nil { 72 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 73 return 74 } 75 if ok { 76 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errIAMActionNotAllowed), r.URL) 77 return 78 } 79 80 if err := GlobalIAMSys.DeleteUser(accessKey); err != nil { 81 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 82 return 83 } 84 85 // Notify all other MinIO peers to delete user. 86 for _, nerr := range GlobalNotificationSys.DeleteUser(accessKey) { 87 if nerr.Err != nil { 88 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 89 logger.LogIf(ctx, nerr.Err) 90 } 91 } 92 } 93 94 // ListUsers - GET /minio/admin/v3/list-users 95 func (a adminAPIHandlers) ListUsers(w http.ResponseWriter, r *http.Request) { 96 ctx := NewContext(r, w, "ListUsers") 97 98 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 99 100 objectAPI, cred := validateAdminUsersReq(ctx, w, r, iampolicy.ListUsersAdminAction) 101 if objectAPI == nil { 102 return 103 } 104 105 password := cred.SecretKey 106 107 allCredentials, err := GlobalIAMSys.ListUsers() 108 if err != nil { 109 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 110 return 111 } 112 113 data, err := json.Marshal(allCredentials) 114 if err != nil { 115 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 116 return 117 } 118 119 econfigData, err := madmin.EncryptData(password, data) 120 if err != nil { 121 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 122 return 123 } 124 125 writeSuccessResponseJSON(w, econfigData) 126 } 127 128 // GetUserInfo - GET /minio/admin/v3/user-info 129 func (a adminAPIHandlers) GetUserInfo(w http.ResponseWriter, r *http.Request) { 130 ctx := NewContext(r, w, "GetUserInfo") 131 132 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 133 134 vars := mux.Vars(r) 135 name := vars["accessKey"] 136 137 // Get current object layer instance. 138 objectAPI := newObjectLayerFn() 139 if objectAPI == nil || GlobalNotificationSys == nil { 140 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 141 return 142 } 143 144 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 145 if s3Err != ErrNone { 146 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 147 return 148 } 149 150 accessKey := cred.AccessKey 151 if cred.ParentUser != "" { 152 accessKey = cred.ParentUser 153 } 154 155 implicitPerm := name == accessKey 156 if !implicitPerm { 157 if !GlobalIAMSys.IsAllowed(iampolicy.Args{ 158 AccountName: accessKey, 159 Groups: cred.Groups, 160 Action: iampolicy.GetUserAdminAction, 161 ConditionValues: getConditionValues(r, "", accessKey, claims), 162 IsOwner: owner, 163 Claims: claims, 164 }) { 165 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 166 return 167 } 168 } 169 170 userInfo, err := GlobalIAMSys.GetUserInfo(ctx, name) 171 if err != nil { 172 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 173 return 174 } 175 176 data, err := json.Marshal(userInfo) 177 if err != nil { 178 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 179 return 180 } 181 182 writeSuccessResponseJSON(w, data) 183 } 184 185 // UpdateGroupMembers - PUT /minio/admin/v3/update-group-members 186 func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Request) { 187 ctx := NewContext(r, w, "UpdateGroupMembers") 188 189 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 190 191 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.AddUserToGroupAdminAction) 192 if objectAPI == nil { 193 return 194 } 195 196 defer r.Body.Close() 197 data, err := ioutil.ReadAll(r.Body) 198 if err != nil { 199 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) 200 return 201 } 202 203 var updReq madmin.GroupAddRemove 204 err = json.Unmarshal(data, &updReq) 205 if err != nil { 206 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) 207 return 208 } 209 210 if updReq.IsRemove { 211 err = GlobalIAMSys.RemoveUsersFromGroup(updReq.Group, updReq.Members) 212 } else { 213 err = GlobalIAMSys.AddUsersToGroup(updReq.Group, updReq.Members) 214 } 215 216 if err != nil { 217 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 218 return 219 } 220 221 // Notify all other MinIO peers to load group. 222 for _, nerr := range GlobalNotificationSys.LoadGroup(updReq.Group) { 223 if nerr.Err != nil { 224 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 225 logger.LogIf(ctx, nerr.Err) 226 } 227 } 228 } 229 230 // GetGroup - /minio/admin/v3/group?group=mygroup1 231 func (a adminAPIHandlers) GetGroup(w http.ResponseWriter, r *http.Request) { 232 ctx := NewContext(r, w, "GetGroup") 233 234 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 235 236 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.GetGroupAdminAction) 237 if objectAPI == nil { 238 return 239 } 240 241 vars := mux.Vars(r) 242 group := vars["group"] 243 244 gdesc, err := GlobalIAMSys.GetGroupDescription(group) 245 if err != nil { 246 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 247 return 248 } 249 250 body, err := json.Marshal(gdesc) 251 if err != nil { 252 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 253 return 254 } 255 256 writeSuccessResponseJSON(w, body) 257 } 258 259 // ListGroups - GET /minio/admin/v3/groups 260 func (a adminAPIHandlers) ListGroups(w http.ResponseWriter, r *http.Request) { 261 ctx := NewContext(r, w, "ListGroups") 262 263 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 264 265 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.ListGroupsAdminAction) 266 if objectAPI == nil { 267 return 268 } 269 270 groups, err := GlobalIAMSys.ListGroups() 271 if err != nil { 272 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 273 return 274 } 275 276 body, err := json.Marshal(groups) 277 if err != nil { 278 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 279 return 280 } 281 282 writeSuccessResponseJSON(w, body) 283 } 284 285 // SetGroupStatus - PUT /minio/admin/v3/set-group-status?group=mygroup1&status=enabled 286 func (a adminAPIHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request) { 287 ctx := NewContext(r, w, "SetGroupStatus") 288 289 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 290 291 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.EnableGroupAdminAction) 292 if objectAPI == nil { 293 return 294 } 295 296 vars := mux.Vars(r) 297 group := vars["group"] 298 status := vars["status"] 299 300 var err error 301 if status == statusEnabled { 302 err = GlobalIAMSys.SetGroupStatus(group, true) 303 } else if status == statusDisabled { 304 err = GlobalIAMSys.SetGroupStatus(group, false) 305 } else { 306 err = errInvalidArgument 307 } 308 if err != nil { 309 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 310 return 311 } 312 313 // Notify all other MinIO peers to reload user. 314 for _, nerr := range GlobalNotificationSys.LoadGroup(group) { 315 if nerr.Err != nil { 316 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 317 logger.LogIf(ctx, nerr.Err) 318 } 319 } 320 } 321 322 // SetUserStatus - PUT /minio/admin/v3/set-user-status?accessKey=<access_key>&status=[enabled|disabled] 323 func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request) { 324 ctx := NewContext(r, w, "SetUserStatus") 325 326 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 327 328 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.EnableUserAdminAction) 329 if objectAPI == nil { 330 return 331 } 332 333 vars := mux.Vars(r) 334 accessKey := vars["accessKey"] 335 status := vars["status"] 336 337 // This API is not allowed to lookup accessKey user status 338 if accessKey == globalActiveCred.AccessKey { 339 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) 340 return 341 } 342 343 if err := GlobalIAMSys.SetUserStatus(accessKey, madmin.AccountStatus(status)); err != nil { 344 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 345 return 346 } 347 348 // Notify all other MinIO peers to reload user. 349 for _, nerr := range GlobalNotificationSys.LoadUser(accessKey, false) { 350 if nerr.Err != nil { 351 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 352 logger.LogIf(ctx, nerr.Err) 353 } 354 } 355 } 356 357 // AddUser - PUT /minio/admin/v3/add-user?accessKey=<access_key> 358 func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) { 359 ctx := NewContext(r, w, "AddUser") 360 361 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 362 363 vars := mux.Vars(r) 364 accessKey := vars["accessKey"] 365 366 // Get current object layer instance. 367 objectAPI := newObjectLayerFn() 368 if objectAPI == nil || GlobalNotificationSys == nil { 369 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 370 return 371 } 372 373 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 374 if s3Err != ErrNone { 375 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 376 return 377 } 378 379 // Not allowed to add a user with same access key as root credential 380 if owner && accessKey == cred.AccessKey { 381 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL) 382 return 383 } 384 385 if (cred.IsTemp() || cred.IsServiceAccount()) && cred.ParentUser == accessKey { 386 // Incoming access key matches parent user then we should 387 // reject password change requests. 388 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL) 389 return 390 } 391 392 implicitPerm := accessKey == cred.AccessKey 393 if !implicitPerm { 394 parentUser := cred.ParentUser 395 if parentUser == "" { 396 parentUser = cred.AccessKey 397 } 398 if !GlobalIAMSys.IsAllowed(iampolicy.Args{ 399 AccountName: parentUser, 400 Groups: cred.Groups, 401 Action: iampolicy.CreateUserAdminAction, 402 ConditionValues: getConditionValues(r, "", parentUser, claims), 403 IsOwner: owner, 404 Claims: claims, 405 }) { 406 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 407 return 408 } 409 } 410 411 if implicitPerm && !GlobalIAMSys.IsAllowed(iampolicy.Args{ 412 AccountName: accessKey, 413 Groups: cred.Groups, 414 Action: iampolicy.CreateUserAdminAction, 415 ConditionValues: getConditionValues(r, "", accessKey, claims), 416 IsOwner: owner, 417 Claims: claims, 418 DenyOnly: true, // check if changing password is explicitly denied. 419 }) { 420 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 421 return 422 } 423 424 if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 { 425 // More than maxConfigSize bytes were available 426 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL) 427 return 428 } 429 430 password := cred.SecretKey 431 configBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) 432 if err != nil { 433 logger.LogIf(ctx, err) 434 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) 435 return 436 } 437 438 var uinfo madmin.UserInfo 439 if err = json.Unmarshal(configBytes, &uinfo); err != nil { 440 logger.LogIf(ctx, err) 441 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) 442 return 443 } 444 445 if err = GlobalIAMSys.CreateUser(accessKey, uinfo); err != nil { 446 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 447 return 448 } 449 450 // Notify all other Minio peers to reload user 451 for _, nerr := range GlobalNotificationSys.LoadUser(accessKey, false) { 452 if nerr.Err != nil { 453 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 454 logger.LogIf(ctx, nerr.Err) 455 } 456 } 457 } 458 459 // AddServiceAccount - PUT /minio/admin/v3/add-service-account 460 func (a adminAPIHandlers) AddServiceAccount(w http.ResponseWriter, r *http.Request) { 461 ctx := NewContext(r, w, "AddServiceAccount") 462 463 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 464 465 // Get current object layer instance. 466 objectAPI := newObjectLayerFn() 467 if objectAPI == nil || GlobalNotificationSys == nil { 468 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 469 return 470 } 471 472 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 473 if s3Err != ErrNone { 474 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 475 return 476 } 477 478 password := cred.SecretKey 479 reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) 480 if err != nil { 481 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 482 return 483 } 484 485 var createReq madmin.AddServiceAccountReq 486 if err = json.Unmarshal(reqBytes, &createReq); err != nil { 487 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 488 return 489 } 490 491 // Disallow creating service accounts by root user. 492 if createReq.TargetUser == globalActiveCred.AccessKey { 493 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminAccountNotEligible), r.URL) 494 return 495 } 496 497 var ( 498 targetUser string 499 targetGroups []string 500 ) 501 502 targetUser = createReq.TargetUser 503 504 // Need permission if we are creating a service acccount 505 // for a user <> to the request sender 506 if targetUser != "" && targetUser != cred.AccessKey { 507 if !GlobalIAMSys.IsAllowed(iampolicy.Args{ 508 AccountName: cred.AccessKey, 509 Action: iampolicy.CreateServiceAccountAdminAction, 510 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 511 IsOwner: owner, 512 Claims: claims, 513 }) { 514 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 515 return 516 } 517 } 518 519 if globalLDAPConfig.Enabled && targetUser != "" { 520 // If LDAP enabled, service accounts need 521 // to be created only for LDAP users. 522 var err error 523 _, targetGroups, err = globalLDAPConfig.LookupUserDN(targetUser) 524 if err != nil { 525 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 526 return 527 } 528 } else { 529 if targetUser == "" { 530 targetUser = cred.AccessKey 531 } 532 if cred.ParentUser != "" { 533 targetUser = cred.ParentUser 534 } 535 targetGroups = cred.Groups 536 } 537 538 opts := newServiceAccountOpts{sessionPolicy: createReq.Policy, accessKey: createReq.AccessKey, secretKey: createReq.SecretKey} 539 newCred, err := GlobalIAMSys.NewServiceAccount(ctx, targetUser, targetGroups, opts) 540 if err != nil { 541 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 542 return 543 } 544 545 // Notify all other Minio peers to reload user the service account 546 for _, nerr := range GlobalNotificationSys.LoadServiceAccount(newCred.AccessKey) { 547 if nerr.Err != nil { 548 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 549 logger.LogIf(ctx, nerr.Err) 550 } 551 } 552 553 var createResp = madmin.AddServiceAccountResp{ 554 Credentials: auth.Credentials{ 555 AccessKey: newCred.AccessKey, 556 SecretKey: newCred.SecretKey, 557 }, 558 } 559 560 data, err := json.Marshal(createResp) 561 if err != nil { 562 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 563 return 564 } 565 566 encryptedData, err := madmin.EncryptData(password, data) 567 if err != nil { 568 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 569 return 570 } 571 572 writeSuccessResponseJSON(w, encryptedData) 573 } 574 575 // UpdateServiceAccount - POST /minio/admin/v3/update-service-account 576 func (a adminAPIHandlers) UpdateServiceAccount(w http.ResponseWriter, r *http.Request) { 577 ctx := NewContext(r, w, "UpdateServiceAccount") 578 579 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 580 581 // Get current object layer instance. 582 objectAPI := newObjectLayerFn() 583 if objectAPI == nil || GlobalNotificationSys == nil { 584 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 585 return 586 } 587 588 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 589 if s3Err != ErrNone { 590 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 591 return 592 } 593 594 accessKey := mux.Vars(r)["accessKey"] 595 if accessKey == "" { 596 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) 597 return 598 } 599 600 // Disallow editing service accounts by root user. 601 if owner { 602 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminAccountNotEligible), r.URL) 603 return 604 } 605 606 svcAccount, _, err := GlobalIAMSys.GetServiceAccount(ctx, accessKey) 607 if err != nil { 608 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 609 return 610 } 611 612 if !GlobalIAMSys.IsAllowed(iampolicy.Args{ 613 AccountName: cred.AccessKey, 614 Action: iampolicy.UpdateServiceAccountAdminAction, 615 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 616 IsOwner: owner, 617 Claims: claims, 618 }) { 619 requestUser := cred.AccessKey 620 if cred.ParentUser != "" { 621 requestUser = cred.ParentUser 622 } 623 624 if requestUser != svcAccount.ParentUser { 625 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 626 return 627 } 628 } 629 630 password := cred.SecretKey 631 reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) 632 if err != nil { 633 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 634 return 635 } 636 637 var updateReq madmin.UpdateServiceAccountReq 638 if err = json.Unmarshal(reqBytes, &updateReq); err != nil { 639 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 640 return 641 } 642 643 opts := updateServiceAccountOpts{sessionPolicy: updateReq.NewPolicy, secretKey: updateReq.NewSecretKey, status: updateReq.NewStatus} 644 err = GlobalIAMSys.UpdateServiceAccount(ctx, accessKey, opts) 645 if err != nil { 646 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 647 return 648 } 649 650 // Notify all other Minio peers to reload user the service account 651 for _, nerr := range GlobalNotificationSys.LoadServiceAccount(accessKey) { 652 if nerr.Err != nil { 653 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 654 logger.LogIf(ctx, nerr.Err) 655 } 656 } 657 658 writeSuccessNoContent(w) 659 } 660 661 // InfoServiceAccount - GET /minio/admin/v3/info-service-account 662 func (a adminAPIHandlers) InfoServiceAccount(w http.ResponseWriter, r *http.Request) { 663 ctx := NewContext(r, w, "InfoServiceAccount") 664 665 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 666 667 // Get current object layer instance. 668 objectAPI := newObjectLayerFn() 669 if objectAPI == nil || GlobalNotificationSys == nil { 670 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 671 return 672 } 673 674 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 675 if s3Err != ErrNone { 676 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 677 return 678 } 679 680 // Disallow creating service accounts by root user. 681 if owner { 682 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminAccountNotEligible), r.URL) 683 return 684 } 685 686 accessKey := mux.Vars(r)["accessKey"] 687 if accessKey == "" { 688 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) 689 return 690 } 691 692 svcAccount, policy, err := GlobalIAMSys.GetServiceAccount(ctx, accessKey) 693 if err != nil { 694 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 695 return 696 } 697 698 if !GlobalIAMSys.IsAllowed(iampolicy.Args{ 699 AccountName: cred.AccessKey, 700 Action: iampolicy.ListServiceAccountsAdminAction, 701 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 702 IsOwner: owner, 703 Claims: claims, 704 }) { 705 requestUser := cred.AccessKey 706 if cred.ParentUser != "" { 707 requestUser = cred.ParentUser 708 } 709 710 if requestUser != svcAccount.ParentUser { 711 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 712 return 713 } 714 } 715 716 var svcAccountPolicy iampolicy.Policy 717 718 impliedPolicy := policy == nil 719 720 // If policy is empty, check for policy of the parent user 721 if !impliedPolicy { 722 svcAccountPolicy = svcAccountPolicy.Merge(*policy) 723 } else { 724 policiesNames, err := GlobalIAMSys.PolicyDBGet(svcAccount.ParentUser, false) 725 if err != nil { 726 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 727 return 728 } 729 svcAccountPolicy = svcAccountPolicy.Merge(GlobalIAMSys.GetCombinedPolicy(policiesNames...)) 730 } 731 732 policyJSON, err := json.Marshal(svcAccountPolicy) 733 if err != nil { 734 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 735 return 736 } 737 738 var infoResp = madmin.InfoServiceAccountResp{ 739 ParentUser: svcAccount.ParentUser, 740 AccountStatus: svcAccount.Status, 741 ImpliedPolicy: impliedPolicy, 742 Policy: string(policyJSON), 743 } 744 745 data, err := json.Marshal(infoResp) 746 if err != nil { 747 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 748 return 749 } 750 751 encryptedData, err := madmin.EncryptData(cred.SecretKey, data) 752 if err != nil { 753 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 754 return 755 } 756 757 writeSuccessResponseJSON(w, encryptedData) 758 } 759 760 // ListServiceAccounts - GET /minio/admin/v3/list-service-accounts 761 func (a adminAPIHandlers) ListServiceAccounts(w http.ResponseWriter, r *http.Request) { 762 ctx := NewContext(r, w, "ListServiceAccounts") 763 764 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 765 766 // Get current object layer instance. 767 objectAPI := newObjectLayerFn() 768 if objectAPI == nil || GlobalNotificationSys == nil { 769 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 770 return 771 } 772 773 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 774 if s3Err != ErrNone { 775 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 776 return 777 } 778 779 // Disallow creating service accounts by root user. 780 if owner { 781 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminAccountNotEligible), r.URL) 782 return 783 } 784 785 var targetAccount string 786 787 user := r.URL.Query().Get("user") 788 if user != "" { 789 if !GlobalIAMSys.IsAllowed(iampolicy.Args{ 790 AccountName: cred.AccessKey, 791 Action: iampolicy.ListServiceAccountsAdminAction, 792 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 793 IsOwner: owner, 794 Claims: claims, 795 }) { 796 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 797 return 798 } 799 targetAccount = user 800 } else { 801 targetAccount = cred.AccessKey 802 if cred.ParentUser != "" { 803 targetAccount = cred.ParentUser 804 } 805 } 806 807 serviceAccounts, err := GlobalIAMSys.ListServiceAccounts(ctx, targetAccount) 808 if err != nil { 809 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 810 return 811 } 812 813 var serviceAccountsNames []string 814 815 for _, svc := range serviceAccounts { 816 serviceAccountsNames = append(serviceAccountsNames, svc.AccessKey) 817 } 818 819 var listResp = madmin.ListServiceAccountsResp{ 820 Accounts: serviceAccountsNames, 821 } 822 823 data, err := json.Marshal(listResp) 824 if err != nil { 825 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 826 return 827 } 828 829 encryptedData, err := madmin.EncryptData(cred.SecretKey, data) 830 if err != nil { 831 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 832 return 833 } 834 835 writeSuccessResponseJSON(w, encryptedData) 836 } 837 838 // DeleteServiceAccount - DELETE /minio/admin/v3/delete-service-account 839 func (a adminAPIHandlers) DeleteServiceAccount(w http.ResponseWriter, r *http.Request) { 840 ctx := NewContext(r, w, "DeleteServiceAccount") 841 842 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 843 844 // Get current object layer instance. 845 objectAPI := newObjectLayerFn() 846 if objectAPI == nil || GlobalNotificationSys == nil { 847 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 848 return 849 } 850 851 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 852 if s3Err != ErrNone { 853 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 854 return 855 } 856 857 // Disallow creating service accounts by root user. 858 if owner { 859 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminAccountNotEligible), r.URL) 860 return 861 } 862 863 serviceAccount := mux.Vars(r)["accessKey"] 864 if serviceAccount == "" { 865 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminInvalidArgument), r.URL) 866 return 867 } 868 869 svcAccount, _, err := GlobalIAMSys.GetServiceAccount(ctx, serviceAccount) 870 if err != nil { 871 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 872 return 873 } 874 875 adminPrivilege := GlobalIAMSys.IsAllowed(iampolicy.Args{ 876 AccountName: cred.AccessKey, 877 Action: iampolicy.RemoveServiceAccountAdminAction, 878 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 879 IsOwner: owner, 880 Claims: claims, 881 }) 882 883 if !adminPrivilege { 884 parentUser := cred.AccessKey 885 if cred.ParentUser != "" { 886 parentUser = cred.ParentUser 887 } 888 if parentUser != svcAccount.ParentUser { 889 // The service account belongs to another user but return not 890 // found error to mitigate brute force attacks. or the 891 // serviceAccount doesn't exist. 892 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminServiceAccountNotFound), r.URL) 893 return 894 } 895 } 896 897 err = GlobalIAMSys.DeleteServiceAccount(ctx, serviceAccount) 898 if err != nil { 899 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 900 return 901 } 902 903 writeSuccessNoContent(w) 904 } 905 906 // AccountInfoHandler returns usage 907 func (a adminAPIHandlers) AccountInfoHandler(w http.ResponseWriter, r *http.Request) { 908 ctx := NewContext(r, w, "AccountInfo") 909 910 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 911 912 // Get current object layer instance. 913 objectAPI := newObjectLayerFn() 914 if objectAPI == nil || GlobalNotificationSys == nil { 915 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 916 return 917 } 918 919 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, "") 920 if s3Err != ErrNone { 921 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 922 return 923 } 924 925 // Set prefix value for "s3:prefix" policy conditionals. 926 r.Header.Set("prefix", "") 927 928 // Set delimiter value for "s3:delimiter" policy conditionals. 929 r.Header.Set("delimiter", SlashSeparator) 930 931 isAllowedAccess := func(bucketName string) (rd, wr bool) { 932 // Use the following trick to filter in place 933 // https://github.com/golang/go/wiki/SliceTricks#filter-in-place 934 if GlobalIAMSys.IsAllowed(iampolicy.Args{ 935 AccountName: cred.AccessKey, 936 Groups: cred.Groups, 937 Action: iampolicy.ListBucketAction, 938 BucketName: bucketName, 939 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 940 IsOwner: owner, 941 ObjectName: "", 942 Claims: claims, 943 }) { 944 rd = true 945 } 946 947 if GlobalIAMSys.IsAllowed(iampolicy.Args{ 948 AccountName: cred.AccessKey, 949 Groups: cred.Groups, 950 Action: iampolicy.PutObjectAction, 951 BucketName: bucketName, 952 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 953 IsOwner: owner, 954 ObjectName: "", 955 Claims: claims, 956 }) { 957 wr = true 958 } 959 960 return rd, wr 961 } 962 963 // Load the latest calculated data usage 964 dataUsageInfo, err := loadDataUsageFromBackend(ctx, objectAPI) 965 if err != nil { 966 // log the error, continue with the accounting response 967 logger.LogIf(ctx, err) 968 } 969 970 buckets, err := objectAPI.ListBuckets(ctx) 971 if err != nil { 972 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 973 return 974 } 975 976 accountName := cred.AccessKey 977 var policies []string 978 switch GlobalIAMSys.usersSysType { 979 case MinIOUsersSysType: 980 policies, err = GlobalIAMSys.PolicyDBGet(accountName, false) 981 case LDAPUsersSysType: 982 parentUser := accountName 983 if cred.ParentUser != "" { 984 parentUser = cred.ParentUser 985 } 986 policies, err = GlobalIAMSys.PolicyDBGet(parentUser, false, cred.Groups...) 987 default: 988 err = errors.New("should not happen!") 989 } 990 if err != nil { 991 logger.LogIf(ctx, err) 992 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 993 return 994 } 995 996 acctInfo := madmin.AccountInfo{ 997 AccountName: accountName, 998 Policy: GlobalIAMSys.GetCombinedPolicy(policies...), 999 } 1000 1001 for _, bucket := range buckets { 1002 rd, wr := isAllowedAccess(bucket.Name) 1003 if rd || wr { 1004 var size uint64 1005 // Fetch the data usage of the current bucket 1006 if !dataUsageInfo.LastUpdate.IsZero() { 1007 size = dataUsageInfo.BucketsUsage[bucket.Name].Size 1008 } 1009 acctInfo.Buckets = append(acctInfo.Buckets, madmin.BucketAccessInfo{ 1010 Name: bucket.Name, 1011 Created: bucket.Created, 1012 Size: size, 1013 Access: madmin.AccountAccess{ 1014 Read: rd, 1015 Write: wr, 1016 }, 1017 }) 1018 } 1019 } 1020 1021 usageInfoJSON, err := json.Marshal(acctInfo) 1022 if err != nil { 1023 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1024 return 1025 } 1026 1027 writeSuccessResponseJSON(w, usageInfoJSON) 1028 } 1029 1030 // InfoCannedPolicyV2 - GET /minio/admin/v2/info-canned-policy?name={policyName} 1031 func (a adminAPIHandlers) InfoCannedPolicyV2(w http.ResponseWriter, r *http.Request) { 1032 ctx := NewContext(r, w, "InfoCannedPolicyV2") 1033 1034 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 1035 1036 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.GetPolicyAdminAction) 1037 if objectAPI == nil { 1038 return 1039 } 1040 1041 policy, err := GlobalIAMSys.InfoPolicy(mux.Vars(r)["name"]) 1042 if err != nil { 1043 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1044 return 1045 } 1046 1047 data, err := json.Marshal(policy) 1048 if err != nil { 1049 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1050 return 1051 } 1052 1053 w.Write(data) 1054 w.(http.Flusher).Flush() 1055 } 1056 1057 // InfoCannedPolicy - GET /minio/admin/v3/info-canned-policy?name={policyName} 1058 func (a adminAPIHandlers) InfoCannedPolicy(w http.ResponseWriter, r *http.Request) { 1059 ctx := NewContext(r, w, "InfoCannedPolicy") 1060 1061 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 1062 1063 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.GetPolicyAdminAction) 1064 if objectAPI == nil { 1065 return 1066 } 1067 1068 policy, err := GlobalIAMSys.InfoPolicy(mux.Vars(r)["name"]) 1069 if err != nil { 1070 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1071 return 1072 } 1073 1074 if err = json.NewEncoder(w).Encode(policy); err != nil { 1075 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1076 return 1077 } 1078 w.(http.Flusher).Flush() 1079 } 1080 1081 // ListCannedPoliciesV2 - GET /minio/admin/v2/list-canned-policies 1082 func (a adminAPIHandlers) ListCannedPoliciesV2(w http.ResponseWriter, r *http.Request) { 1083 ctx := NewContext(r, w, "ListCannedPoliciesV2") 1084 1085 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 1086 1087 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.ListUserPoliciesAdminAction) 1088 if objectAPI == nil { 1089 return 1090 } 1091 1092 policies, err := GlobalIAMSys.ListPolicies() 1093 if err != nil { 1094 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1095 return 1096 } 1097 1098 policyMap := make(map[string][]byte, len(policies)) 1099 for k, p := range policies { 1100 var err error 1101 policyMap[k], err = json.Marshal(p) 1102 if err != nil { 1103 logger.LogIf(ctx, err) 1104 continue 1105 } 1106 } 1107 if err = json.NewEncoder(w).Encode(policyMap); err != nil { 1108 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1109 return 1110 } 1111 1112 w.(http.Flusher).Flush() 1113 } 1114 1115 // ListCannedPolicies - GET /minio/admin/v3/list-canned-policies 1116 func (a adminAPIHandlers) ListCannedPolicies(w http.ResponseWriter, r *http.Request) { 1117 ctx := NewContext(r, w, "ListCannedPolicies") 1118 1119 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 1120 1121 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.ListUserPoliciesAdminAction) 1122 if objectAPI == nil { 1123 return 1124 } 1125 1126 policies, err := GlobalIAMSys.ListPolicies() 1127 if err != nil { 1128 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1129 return 1130 } 1131 1132 var newPolicies = make(map[string]iampolicy.Policy) 1133 for name, p := range policies { 1134 _, err = json.Marshal(p) 1135 if err != nil { 1136 logger.LogIf(ctx, err) 1137 continue 1138 } 1139 newPolicies[name] = p 1140 } 1141 if err = json.NewEncoder(w).Encode(newPolicies); err != nil { 1142 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1143 return 1144 } 1145 1146 w.(http.Flusher).Flush() 1147 } 1148 1149 // RemoveCannedPolicy - DELETE /minio/admin/v3/remove-canned-policy?name=<policy_name> 1150 func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Request) { 1151 ctx := NewContext(r, w, "RemoveCannedPolicy") 1152 1153 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 1154 1155 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.DeletePolicyAdminAction) 1156 if objectAPI == nil { 1157 return 1158 } 1159 1160 vars := mux.Vars(r) 1161 policyName := vars["name"] 1162 1163 if err := GlobalIAMSys.DeletePolicy(policyName); err != nil { 1164 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1165 return 1166 } 1167 1168 // Notify all other MinIO peers to delete policy 1169 for _, nerr := range GlobalNotificationSys.DeletePolicy(policyName) { 1170 if nerr.Err != nil { 1171 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 1172 logger.LogIf(ctx, nerr.Err) 1173 } 1174 } 1175 } 1176 1177 // AddCannedPolicy - PUT /minio/admin/v3/add-canned-policy?name=<policy_name> 1178 func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request) { 1179 ctx := NewContext(r, w, "AddCannedPolicy") 1180 1181 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 1182 1183 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.CreatePolicyAdminAction) 1184 if objectAPI == nil { 1185 return 1186 } 1187 1188 vars := mux.Vars(r) 1189 policyName := vars["name"] 1190 1191 // Error out if Content-Length is missing. 1192 if r.ContentLength <= 0 { 1193 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL) 1194 return 1195 } 1196 1197 // Error out if Content-Length is beyond allowed size. 1198 if r.ContentLength > maxBucketPolicySize { 1199 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrEntityTooLarge), r.URL) 1200 return 1201 } 1202 1203 iamPolicy, err := iampolicy.ParseConfig(io.LimitReader(r.Body, r.ContentLength)) 1204 if err != nil { 1205 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1206 return 1207 } 1208 1209 // Version in policy must not be empty 1210 if iamPolicy.Version == "" { 1211 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPolicy), r.URL) 1212 return 1213 } 1214 1215 if err = GlobalIAMSys.SetPolicy(policyName, *iamPolicy); err != nil { 1216 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1217 return 1218 } 1219 1220 // Notify all other MinIO peers to reload policy 1221 for _, nerr := range GlobalNotificationSys.LoadPolicy(policyName) { 1222 if nerr.Err != nil { 1223 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 1224 logger.LogIf(ctx, nerr.Err) 1225 } 1226 } 1227 } 1228 1229 // SetPolicyForUserOrGroup - PUT /minio/admin/v3/set-policy?policy=xxx&user-or-group=?[&is-group] 1230 func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http.Request) { 1231 ctx := NewContext(r, w, "SetPolicyForUserOrGroup") 1232 1233 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 1234 1235 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.AttachPolicyAdminAction) 1236 if objectAPI == nil { 1237 return 1238 } 1239 1240 vars := mux.Vars(r) 1241 policyName := vars["policyName"] 1242 entityName := vars["userOrGroup"] 1243 isGroup := vars["isGroup"] == "true" 1244 1245 if !isGroup { 1246 ok, _, err := GlobalIAMSys.IsTempUser(entityName) 1247 if err != nil && err != errNoSuchUser { 1248 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1249 return 1250 } 1251 if ok { 1252 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errIAMActionNotAllowed), r.URL) 1253 return 1254 } 1255 } 1256 1257 if err := GlobalIAMSys.PolicyDBSet(entityName, policyName, isGroup); err != nil { 1258 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 1259 return 1260 } 1261 1262 // Notify all other MinIO peers to reload policy 1263 for _, nerr := range GlobalNotificationSys.LoadPolicyMapping(entityName, isGroup) { 1264 if nerr.Err != nil { 1265 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) 1266 logger.LogIf(ctx, nerr.Err) 1267 } 1268 } 1269 }