github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/iam/user.go (about) 1 package iam 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 "path" 10 "strconv" 11 12 validation "github.com/go-ozzo/ozzo-validation/v4" 13 "github.com/go-ozzo/ozzo-validation/v4/is" 14 ) 15 16 type ( 17 // Users is the IAM user identity API interface 18 Users interface { 19 // CreateUser creates a user in the account specified in your own API client credentials or clone an existing user's role assignments 20 // 21 // See: https://techdocs.akamai.com/iam-user-admin/reference/post-ui-identity 22 CreateUser(context.Context, CreateUserRequest) (*User, error) 23 24 // GetUser gets a specific user's profile 25 // 26 // See: https://techdocs.akamai.com/iam-user-admin/reference/get-ui-identity 27 GetUser(context.Context, GetUserRequest) (*User, error) 28 29 // ListUsers returns a list of users who have access on this account 30 // 31 // See: https://techdocs.akamai.com/iam-user-admin/reference/get-ui-identities 32 ListUsers(context.Context, ListUsersRequest) ([]UserListItem, error) 33 34 // RemoveUser removes a user identity 35 // 36 // See: https://techdocs.akamai.com/iam-user-admin/reference/delete-ui-identity 37 RemoveUser(context.Context, RemoveUserRequest) error 38 39 // UpdateUserAuthGrants edits what groups a user has access to, and how the user can interact with the objects in those groups 40 // 41 // See: https://techdocs.akamai.com/iam-user-admin/reference/put-ui-uiidentity-auth-grants 42 UpdateUserAuthGrants(context.Context, UpdateUserAuthGrantsRequest) ([]AuthGrant, error) 43 44 // UpdateUserInfo updates a user's information 45 // 46 // See: https://techdocs.akamai.com/iam-user-admin/reference/put-ui-identity-basic-info 47 UpdateUserInfo(context.Context, UpdateUserInfoRequest) (*UserBasicInfo, error) 48 49 // UpdateUserNotifications subscribes or un-subscribe user to product notification emails 50 // 51 // See: https://techdocs.akamai.com/iam-user-admin/reference/put-notifications 52 UpdateUserNotifications(context.Context, UpdateUserNotificationsRequest) (*UserNotifications, error) 53 54 // UpdateTFA updates a user's two-factor authentication setting and can reset tfa 55 // 56 // See: https://techdocs.akamai.com/iam-user-admin/reference/put-ui-identity-tfa 57 UpdateTFA(context.Context, UpdateTFARequest) error 58 } 59 60 // CreateUserRequest contains the request parameters for the create user endpoint 61 CreateUserRequest struct { 62 UserBasicInfo 63 AuthGrants []AuthGrantRequest `json:"authGrants,omitempty"` 64 Notifications UserNotifications `json:"notifications,omitempty"` 65 SendEmail bool `json:"-"` 66 } 67 68 // ListUsersRequest contains the request parameters for the list users endpoint 69 ListUsersRequest struct { 70 GroupID *int64 71 AuthGrants bool 72 Actions bool 73 } 74 75 // GetUserRequest contains the request parameters of the get user endpoint 76 GetUserRequest struct { 77 IdentityID string 78 Actions bool 79 AuthGrants bool 80 Notifications bool 81 } 82 83 // UpdateUserInfoRequest contains the request parameters of the update user endpoint 84 UpdateUserInfoRequest struct { 85 IdentityID string 86 User UserBasicInfo 87 } 88 89 // UpdateUserNotificationsRequest contains the request parameters of the update user notifications endpoint 90 UpdateUserNotificationsRequest struct { 91 IdentityID string 92 Notifications UserNotifications 93 } 94 95 // UpdateUserAuthGrantsRequest contains the request parameters of the update user auth grants endpoint 96 UpdateUserAuthGrantsRequest struct { 97 IdentityID string 98 AuthGrants []AuthGrantRequest 99 } 100 101 // RemoveUserRequest contains the request parameters of the remove user endpoint 102 RemoveUserRequest struct { 103 IdentityID string 104 } 105 106 // User describes the response of the get and create user endpoints 107 User struct { 108 UserBasicInfo 109 IdentityID string `json:"uiIdentityId"` 110 IsLocked bool `json:"isLocked"` 111 LastLoginDate string `json:"lastLoginDate,omitempty"` 112 PasswordExpiryDate string `json:"passwordExpiryDate,omitempty"` 113 TFAConfigured bool `json:"tfaConfigured"` 114 EmailUpdatePending bool `json:"emailUpdatePending"` 115 AuthGrants []AuthGrant `json:"authGrants,omitempty"` 116 Notifications UserNotifications `json:"notifications,omitempty"` 117 } 118 119 // UserListItem describes the response of the list endpoint 120 UserListItem struct { 121 FirstName string `json:"firstName"` 122 LastName string `json:"lastName"` 123 UserName string `json:"uiUserName,omitempty"` 124 Email string `json:"email"` 125 TFAEnabled bool `json:"tfaEnabled"` 126 IdentityID string `json:"uiIdentityId"` 127 IsLocked bool `json:"isLocked"` 128 LastLoginDate string `json:"lastLoginDate,omitempty"` 129 TFAConfigured bool `json:"tfaConfigured"` 130 AccountID string `json:"accountId"` 131 Actions *UserActions `json:"actions,omitempty"` 132 AuthGrants []AuthGrant `json:"authGrants,omitempty"` 133 } 134 135 // UserBasicInfo is the user basic info structure 136 UserBasicInfo struct { 137 FirstName string `json:"firstName"` 138 LastName string `json:"lastName"` 139 UserName string `json:"uiUserName,omitempty"` 140 Email string `json:"email"` 141 Phone string `json:"phone,omitempty"` 142 TimeZone string `json:"timeZone,omitempty"` 143 JobTitle string `json:"jobTitle"` 144 TFAEnabled bool `json:"tfaEnabled"` 145 SecondaryEmail string `json:"secondaryEmail,omitempty"` 146 MobilePhone string `json:"mobilePhone,omitempty"` 147 Address string `json:"address,omitempty"` 148 City string `json:"city,omitempty"` 149 State string `json:"state,omitempty"` 150 ZipCode string `json:"zipCode,omitempty"` 151 Country string `json:"country"` 152 ContactType string `json:"contactType,omitempty"` 153 PreferredLanguage string `json:"preferredLanguage,omitempty"` 154 SessionTimeOut *int `json:"sessionTimeOut,omitempty"` 155 } 156 157 // UserActions encapsulates permissions available to the user for this group 158 UserActions struct { 159 APIClient bool `json:"apiClient"` 160 Delete bool `json:"delete"` 161 Edit bool `json:"edit"` 162 IsCloneable bool `json:"isCloneable"` 163 ResetPassword bool `json:"resetPassword"` 164 ThirdPartyAccess bool `json:"thirdPartyAccess"` 165 CanEditTFA bool `json:"canEditTFA"` 166 EditProfile bool `json:"editProfile"` 167 } 168 169 // AuthGrant is user’s role assignments, per group 170 AuthGrant struct { 171 GroupID int64 `json:"groupId"` 172 GroupName string `json:"groupName"` 173 IsBlocked bool `json:"isBlocked"` 174 RoleDescription string `json:"roleDescription"` 175 RoleID *int `json:"roleId,omitempty"` 176 RoleName string `json:"roleName"` 177 Subgroups []AuthGrant `json:"subGroups,omitempty"` 178 } 179 180 // AuthGrantRequest is user’s role assignments, per group for the create/update operation 181 AuthGrantRequest struct { 182 GroupID int64 `json:"groupId"` 183 IsBlocked bool `json:"isBlocked"` 184 RoleID *int `json:"roleId,omitempty"` 185 Subgroups []AuthGrantRequest `json:"subGroups,omitempty"` 186 } 187 188 // UserNotifications types of notification emails the user receives 189 UserNotifications struct { 190 EnableEmail bool `json:"enableEmailNotifications"` 191 Options UserNotificationOptions `json:"options"` 192 } 193 194 // UserNotificationOptions types of notification emails the user receives 195 UserNotificationOptions struct { 196 NewUser bool `json:"newUserNotification"` 197 PasswordExpiry bool `json:"passwordExpiry"` 198 Proactive []string `json:"proactive"` 199 Upgrade []string `json:"upgrade"` 200 } 201 202 // TFAActionType is a type for tfa action constants 203 TFAActionType string 204 205 // UpdateTFARequest contains the request parameters of the tfa user endpoint 206 UpdateTFARequest struct { 207 IdentityID string 208 Action TFAActionType 209 } 210 ) 211 212 const ( 213 // TFAActionEnable ia an action value to use to enable tfa 214 TFAActionEnable TFAActionType = "enable" 215 // TFAActionDisable ia an action value to use to disable tfa 216 TFAActionDisable TFAActionType = "disable" 217 // TFAActionReset ia an action value to use to reset tfa 218 TFAActionReset TFAActionType = "reset" 219 ) 220 221 var ( 222 // ErrCreateUser is returned when CreateUser fails 223 ErrCreateUser = errors.New("create user") 224 225 // ErrGetUser is returned when GetUser fails 226 ErrGetUser = errors.New("get user") 227 228 // ErrListUsers is returned when GetUser fails 229 ErrListUsers = errors.New("list users") 230 231 // ErrRemoveUser is returned when RemoveUser fails 232 ErrRemoveUser = errors.New("remove user") 233 234 // ErrUpdateUserAuthGrants is returned when UpdateUserAuthGrants fails 235 ErrUpdateUserAuthGrants = errors.New("update user auth grants") 236 237 // ErrUpdateUserInfo is returned when UpdateUserInfo fails 238 ErrUpdateUserInfo = errors.New("update user info") 239 240 // ErrUpdateUserNotifications is returned when UpdateUserNotifications fails 241 ErrUpdateUserNotifications = errors.New("update user notifications") 242 243 // ErrUpdateTFA is returned when UpdateTFA fails 244 ErrUpdateTFA = errors.New("update user's two-factor authentication") 245 ) 246 247 // Validate performs validation on AuthGrant 248 func (r AuthGrant) Validate() error { 249 return validation.Errors{ 250 "group_id": validation.Validate(r.GroupID, validation.Required), 251 "role_id": validation.Validate(r.RoleID, validation.Required), 252 }.Filter() 253 } 254 255 // Validate validates CreateUserRequest 256 func (r CreateUserRequest) Validate() error { 257 return validation.Errors{ 258 "country": validation.Validate(r.Country, validation.Required), 259 "email": validation.Validate(r.Email, validation.Required, is.EmailFormat), 260 "firstName": validation.Validate(r.FirstName, validation.Required), 261 "lastName": validation.Validate(r.LastName, validation.Required), 262 "phone": validation.Validate(r.Phone, validation.Required), 263 "authGrants": validation.Validate(r.AuthGrants, validation.Required), 264 "notifications": validation.Validate(r.Notifications, validation.Required), 265 }.Filter() 266 } 267 268 // Validate validates GetUserRequest 269 func (r GetUserRequest) Validate() error { 270 return validation.Errors{ 271 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 272 }.Filter() 273 } 274 275 // Validate validates UpdateUserInfoRequest 276 func (r UpdateUserInfoRequest) Validate() error { 277 return validation.Errors{ 278 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 279 "firstName": validation.Validate(r.User.FirstName, validation.Required), 280 "lastName": validation.Validate(r.User.LastName, validation.Required), 281 "country": validation.Validate(r.User.Country, validation.Required), 282 "phone": validation.Validate(r.User.Phone, validation.Required), 283 "contactType": validation.Validate(r.User.ContactType, validation.Required), 284 "timeZone": validation.Validate(r.User.TimeZone, validation.Required), 285 "preferredLanguage": validation.Validate(r.User.PreferredLanguage, validation.Required), 286 "sessionTimeOut": validation.Validate(r.User.SessionTimeOut, validation.Required), 287 }.Filter() 288 } 289 290 // Validate validates UpdateUserNotificationsRequest 291 func (r UpdateUserNotificationsRequest) Validate() error { 292 return validation.Errors{ 293 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 294 }.Filter() 295 } 296 297 // Validate validates UpdateUserAuthGrantsRequest 298 func (r UpdateUserAuthGrantsRequest) Validate() error { 299 return validation.Errors{ 300 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 301 "authGrants": validation.Validate(r.AuthGrants, validation.Required), 302 }.Filter() 303 } 304 305 // Validate validates RemoveUserRequest 306 func (r RemoveUserRequest) Validate() error { 307 return validation.Errors{ 308 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 309 }.Filter() 310 } 311 312 // Validate validates UpdateTFARequest 313 func (r UpdateTFARequest) Validate() error { 314 return validation.Errors{ 315 "IdentityID": validation.Validate(r.IdentityID, validation.Required), 316 "Action": validation.Validate(r.Action, validation.Required, validation.In(TFAActionEnable, TFAActionDisable, TFAActionReset). 317 Error(fmt.Sprintf("value '%s' is invalid. Must be one of: 'enable', 'disable' or 'reset'", r.Action))), 318 }.Filter() 319 } 320 321 func (i *iam) CreateUser(ctx context.Context, params CreateUserRequest) (*User, error) { 322 if err := params.Validate(); err != nil { 323 return nil, fmt.Errorf("%s: %w:\n%s", ErrCreateUser, ErrStructValidation, err) 324 } 325 326 u, err := url.Parse(path.Join("/identity-management/v2/user-admin", "ui-identities")) 327 if err != nil { 328 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateUser, err) 329 } 330 331 q := u.Query() 332 q.Add("sendEmail", strconv.FormatBool(params.SendEmail)) 333 334 u.RawQuery = q.Encode() 335 336 req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), nil) 337 if err != nil { 338 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateUser, err) 339 } 340 341 var result User 342 resp, err := i.Exec(req, &result, params) 343 if err != nil { 344 return nil, fmt.Errorf("%w: request failed: %s", ErrCreateUser, err) 345 } 346 347 if resp.StatusCode != http.StatusCreated { 348 return nil, fmt.Errorf("%s: %w", ErrCreateUser, i.Error(resp)) 349 } 350 351 return &result, nil 352 } 353 354 func (i *iam) GetUser(ctx context.Context, params GetUserRequest) (*User, error) { 355 if err := params.Validate(); err != nil { 356 return nil, fmt.Errorf("%s: %w:\n%s", ErrGetUser, ErrStructValidation, err) 357 } 358 359 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s", params.IdentityID)) 360 if err != nil { 361 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetUser, err) 362 } 363 364 q := u.Query() 365 q.Add("actions", strconv.FormatBool(params.Actions)) 366 q.Add("authGrants", strconv.FormatBool(params.AuthGrants)) 367 q.Add("notifications", strconv.FormatBool(params.Notifications)) 368 369 u.RawQuery = q.Encode() 370 371 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) 372 if err != nil { 373 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetUser, err) 374 } 375 376 var rval User 377 resp, err := i.Exec(req, &rval) 378 if err != nil { 379 return nil, fmt.Errorf("%w: request failed: %s", ErrGetUser, err) 380 } 381 382 if resp.StatusCode != http.StatusOK { 383 return nil, fmt.Errorf("%s: %w", ErrGetUser, i.Error(resp)) 384 } 385 386 return &rval, nil 387 } 388 389 func (i *iam) ListUsers(ctx context.Context, params ListUsersRequest) ([]UserListItem, error) { 390 u, err := url.Parse("/identity-management/v2/user-admin/ui-identities") 391 if err != nil { 392 return nil, fmt.Errorf("%w: failed to parse the URL:\n%s", ErrListUsers, err) 393 } 394 395 q := u.Query() 396 q.Add("actions", strconv.FormatBool(params.Actions)) 397 q.Add("authGrants", strconv.FormatBool(params.AuthGrants)) 398 if params.GroupID != nil { 399 q.Add("groupId", strconv.FormatInt(int64(*params.GroupID), 10)) 400 } 401 u.RawQuery = q.Encode() 402 403 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) 404 if err != nil { 405 return nil, fmt.Errorf("%w: failed to create request:\n%s", ErrListUsers, err) 406 } 407 408 var users []UserListItem 409 resp, err := i.Exec(req, &users) 410 if err != nil { 411 return nil, fmt.Errorf("%w: request failed:\n%s", ErrListUsers, err) 412 } 413 414 if resp.StatusCode != http.StatusOK { 415 return nil, fmt.Errorf("%s: %w", ErrListUsers, i.Error(resp)) 416 } 417 418 return users, nil 419 } 420 421 func (i *iam) RemoveUser(ctx context.Context, params RemoveUserRequest) error { 422 if err := params.Validate(); err != nil { 423 return fmt.Errorf("%s: %w:\n%s", ErrRemoveUser, ErrStructValidation, err) 424 } 425 426 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s", params.IdentityID)) 427 if err != nil { 428 return fmt.Errorf("%w: failed to create request: %s", ErrRemoveUser, err) 429 } 430 431 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, u.String(), nil) 432 if err != nil { 433 return fmt.Errorf("%w: failed to create request: %s", ErrRemoveUser, err) 434 } 435 436 resp, err := i.Exec(req, nil) 437 if err != nil { 438 return fmt.Errorf("%w: request failed: %s", ErrRemoveUser, err) 439 } 440 441 if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { 442 return fmt.Errorf("%s: %w", ErrRemoveUser, i.Error(resp)) 443 } 444 445 return nil 446 } 447 448 func (i *iam) UpdateUserAuthGrants(ctx context.Context, params UpdateUserAuthGrantsRequest) ([]AuthGrant, error) { 449 if err := params.Validate(); err != nil { 450 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateUserAuthGrants, ErrStructValidation, err) 451 } 452 453 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/auth-grants", params.IdentityID)) 454 if err != nil { 455 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserAuthGrants, err) 456 } 457 458 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 459 if err != nil { 460 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserAuthGrants, err) 461 } 462 463 rval := make([]AuthGrant, 0) 464 465 resp, err := i.Exec(req, &rval, params.AuthGrants) 466 if err != nil { 467 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateUserAuthGrants, err) 468 } 469 470 if resp.StatusCode != http.StatusOK { 471 return nil, fmt.Errorf("%s: %w", ErrUpdateUserAuthGrants, i.Error(resp)) 472 } 473 474 return rval, nil 475 } 476 477 func (i *iam) UpdateUserInfo(ctx context.Context, params UpdateUserInfoRequest) (*UserBasicInfo, error) { 478 if err := params.Validate(); err != nil { 479 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateUserInfo, ErrStructValidation, err) 480 } 481 482 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/basic-info", params.IdentityID)) 483 if err != nil { 484 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserInfo, err) 485 } 486 487 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 488 if err != nil { 489 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserInfo, err) 490 } 491 492 var rval UserBasicInfo 493 resp, err := i.Exec(req, &rval, params.User) 494 if err != nil { 495 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateUserInfo, err) 496 } 497 498 if resp.StatusCode != http.StatusOK { 499 return nil, fmt.Errorf("%s: %w", ErrUpdateUserInfo, i.Error(resp)) 500 } 501 502 return &rval, nil 503 } 504 505 func (i *iam) UpdateUserNotifications(ctx context.Context, params UpdateUserNotificationsRequest) (*UserNotifications, error) { 506 if err := params.Validate(); err != nil { 507 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateUserNotifications, ErrStructValidation, err) 508 } 509 510 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/notifications", params.IdentityID)) 511 if err != nil { 512 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserNotifications, err) 513 } 514 515 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 516 if err != nil { 517 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserNotifications, err) 518 } 519 520 var rval UserNotifications 521 resp, err := i.Exec(req, &rval, params.Notifications) 522 if err != nil { 523 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateUserNotifications, err) 524 } 525 526 if resp.StatusCode != http.StatusOK { 527 return nil, fmt.Errorf("%s: %w", ErrUpdateUserNotifications, i.Error(resp)) 528 } 529 530 return &rval, nil 531 } 532 533 func (i *iam) UpdateTFA(ctx context.Context, params UpdateTFARequest) error { 534 if err := params.Validate(); err != nil { 535 return fmt.Errorf("%s: %w:\n%s", ErrUpdateTFA, ErrStructValidation, err) 536 } 537 538 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/tfa", params.IdentityID)) 539 if err != nil { 540 return fmt.Errorf("%w: failed to create request: %s", ErrUpdateTFA, err) 541 } 542 543 q := u.Query() 544 q.Add("action", string(params.Action)) 545 u.RawQuery = q.Encode() 546 547 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 548 if err != nil { 549 return fmt.Errorf("%w: failed to create request: %s", ErrUpdateTFA, err) 550 } 551 552 resp, err := i.Exec(req, nil, nil) 553 if err != nil { 554 return fmt.Errorf("%w: request failed: %s", ErrUpdateTFA, err) 555 } 556 557 if resp.StatusCode != http.StatusNoContent { 558 return fmt.Errorf("%s: %w", ErrUpdateTFA, i.Error(resp)) 559 } 560 561 return nil 562 }