github.com/akamai/AkamaiOPEN-edgegrid-golang/v5@v5.0.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 "timeZone": validation.Validate(r.User.TimeZone, validation.Required), 284 "preferredLanguage": validation.Validate(r.User.PreferredLanguage, validation.Required), 285 "sessionTimeOut": validation.Validate(r.User.SessionTimeOut, validation.Required), 286 }.Filter() 287 } 288 289 // Validate validates UpdateUserNotificationsRequest 290 func (r UpdateUserNotificationsRequest) Validate() error { 291 return validation.Errors{ 292 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 293 }.Filter() 294 } 295 296 // Validate validates UpdateUserAuthGrantsRequest 297 func (r UpdateUserAuthGrantsRequest) Validate() error { 298 return validation.Errors{ 299 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 300 "authGrants": validation.Validate(r.AuthGrants, validation.Required), 301 }.Filter() 302 } 303 304 // Validate validates RemoveUserRequest 305 func (r RemoveUserRequest) Validate() error { 306 return validation.Errors{ 307 "uiIdentity": validation.Validate(r.IdentityID, validation.Required), 308 }.Filter() 309 } 310 311 // Validate validates UpdateTFARequest 312 func (r UpdateTFARequest) Validate() error { 313 return validation.Errors{ 314 "IdentityID": validation.Validate(r.IdentityID, validation.Required), 315 "Action": validation.Validate(r.Action, validation.Required, validation.In(TFAActionEnable, TFAActionDisable, TFAActionReset). 316 Error(fmt.Sprintf("value '%s' is invalid. Must be one of: 'enable', 'disable' or 'reset'", r.Action))), 317 }.Filter() 318 } 319 320 func (i *iam) CreateUser(ctx context.Context, params CreateUserRequest) (*User, error) { 321 if err := params.Validate(); err != nil { 322 return nil, fmt.Errorf("%s: %w:\n%s", ErrCreateUser, ErrStructValidation, err) 323 } 324 325 u, err := url.Parse(path.Join("/identity-management/v2/user-admin", "ui-identities")) 326 if err != nil { 327 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateUser, err) 328 } 329 330 q := u.Query() 331 q.Add("sendEmail", strconv.FormatBool(params.SendEmail)) 332 333 u.RawQuery = q.Encode() 334 335 req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), nil) 336 if err != nil { 337 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateUser, err) 338 } 339 340 var result User 341 resp, err := i.Exec(req, &result, params) 342 if err != nil { 343 return nil, fmt.Errorf("%w: request failed: %s", ErrCreateUser, err) 344 } 345 346 if resp.StatusCode != http.StatusCreated { 347 return nil, fmt.Errorf("%s: %w", ErrCreateUser, i.Error(resp)) 348 } 349 350 return &result, nil 351 } 352 353 func (i *iam) GetUser(ctx context.Context, params GetUserRequest) (*User, error) { 354 if err := params.Validate(); err != nil { 355 return nil, fmt.Errorf("%s: %w:\n%s", ErrGetUser, ErrStructValidation, err) 356 } 357 358 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s", params.IdentityID)) 359 if err != nil { 360 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetUser, err) 361 } 362 363 q := u.Query() 364 q.Add("actions", strconv.FormatBool(params.Actions)) 365 q.Add("authGrants", strconv.FormatBool(params.AuthGrants)) 366 q.Add("notifications", strconv.FormatBool(params.Notifications)) 367 368 u.RawQuery = q.Encode() 369 370 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) 371 if err != nil { 372 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetUser, err) 373 } 374 375 var rval User 376 resp, err := i.Exec(req, &rval) 377 if err != nil { 378 return nil, fmt.Errorf("%w: request failed: %s", ErrGetUser, err) 379 } 380 381 if resp.StatusCode != http.StatusOK { 382 return nil, fmt.Errorf("%s: %w", ErrGetUser, i.Error(resp)) 383 } 384 385 return &rval, nil 386 } 387 388 func (i *iam) ListUsers(ctx context.Context, params ListUsersRequest) ([]UserListItem, error) { 389 u, err := url.Parse("/identity-management/v2/user-admin/ui-identities") 390 if err != nil { 391 return nil, fmt.Errorf("%w: failed to parse the URL:\n%s", ErrListUsers, err) 392 } 393 394 q := u.Query() 395 q.Add("actions", strconv.FormatBool(params.Actions)) 396 q.Add("authGrants", strconv.FormatBool(params.AuthGrants)) 397 if params.GroupID != nil { 398 q.Add("groupId", strconv.FormatInt(int64(*params.GroupID), 10)) 399 } 400 u.RawQuery = q.Encode() 401 402 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) 403 if err != nil { 404 return nil, fmt.Errorf("%w: failed to create request:\n%s", ErrListUsers, err) 405 } 406 407 var users []UserListItem 408 resp, err := i.Exec(req, &users) 409 if err != nil { 410 return nil, fmt.Errorf("%w: request failed:\n%s", ErrListUsers, err) 411 } 412 413 if resp.StatusCode != http.StatusOK { 414 return nil, fmt.Errorf("%s: %w", ErrListUsers, i.Error(resp)) 415 } 416 417 return users, nil 418 } 419 420 func (i *iam) RemoveUser(ctx context.Context, params RemoveUserRequest) error { 421 if err := params.Validate(); err != nil { 422 return fmt.Errorf("%s: %w:\n%s", ErrRemoveUser, ErrStructValidation, err) 423 } 424 425 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s", params.IdentityID)) 426 if err != nil { 427 return fmt.Errorf("%w: failed to create request: %s", ErrRemoveUser, err) 428 } 429 430 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, u.String(), nil) 431 if err != nil { 432 return fmt.Errorf("%w: failed to create request: %s", ErrRemoveUser, err) 433 } 434 435 resp, err := i.Exec(req, nil) 436 if err != nil { 437 return fmt.Errorf("%w: request failed: %s", ErrRemoveUser, err) 438 } 439 440 if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { 441 return fmt.Errorf("%s: %w", ErrRemoveUser, i.Error(resp)) 442 } 443 444 return nil 445 } 446 447 func (i *iam) UpdateUserAuthGrants(ctx context.Context, params UpdateUserAuthGrantsRequest) ([]AuthGrant, error) { 448 if err := params.Validate(); err != nil { 449 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateUserAuthGrants, ErrStructValidation, err) 450 } 451 452 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/auth-grants", params.IdentityID)) 453 if err != nil { 454 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserAuthGrants, err) 455 } 456 457 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 458 if err != nil { 459 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserAuthGrants, err) 460 } 461 462 rval := make([]AuthGrant, 0) 463 464 resp, err := i.Exec(req, &rval, params.AuthGrants) 465 if err != nil { 466 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateUserAuthGrants, err) 467 } 468 469 if resp.StatusCode != http.StatusOK { 470 return nil, fmt.Errorf("%s: %w", ErrUpdateUserAuthGrants, i.Error(resp)) 471 } 472 473 return rval, nil 474 } 475 476 func (i *iam) UpdateUserInfo(ctx context.Context, params UpdateUserInfoRequest) (*UserBasicInfo, error) { 477 if err := params.Validate(); err != nil { 478 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateUserInfo, ErrStructValidation, err) 479 } 480 481 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/basic-info", params.IdentityID)) 482 if err != nil { 483 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserInfo, err) 484 } 485 486 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 487 if err != nil { 488 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserInfo, err) 489 } 490 491 var rval UserBasicInfo 492 resp, err := i.Exec(req, &rval, params.User) 493 if err != nil { 494 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateUserInfo, err) 495 } 496 497 if resp.StatusCode != http.StatusOK { 498 return nil, fmt.Errorf("%s: %w", ErrUpdateUserInfo, i.Error(resp)) 499 } 500 501 return &rval, nil 502 } 503 504 func (i *iam) UpdateUserNotifications(ctx context.Context, params UpdateUserNotificationsRequest) (*UserNotifications, error) { 505 if err := params.Validate(); err != nil { 506 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateUserNotifications, ErrStructValidation, err) 507 } 508 509 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/notifications", params.IdentityID)) 510 if err != nil { 511 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserNotifications, err) 512 } 513 514 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 515 if err != nil { 516 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateUserNotifications, err) 517 } 518 519 var rval UserNotifications 520 resp, err := i.Exec(req, &rval, params.Notifications) 521 if err != nil { 522 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateUserNotifications, err) 523 } 524 525 if resp.StatusCode != http.StatusOK { 526 return nil, fmt.Errorf("%s: %w", ErrUpdateUserNotifications, i.Error(resp)) 527 } 528 529 return &rval, nil 530 } 531 532 func (i *iam) UpdateTFA(ctx context.Context, params UpdateTFARequest) error { 533 if err := params.Validate(); err != nil { 534 return fmt.Errorf("%s: %w:\n%s", ErrUpdateTFA, ErrStructValidation, err) 535 } 536 537 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/ui-identities/%s/tfa", params.IdentityID)) 538 if err != nil { 539 return fmt.Errorf("%w: failed to create request: %s", ErrUpdateTFA, err) 540 } 541 542 q := u.Query() 543 q.Add("action", string(params.Action)) 544 u.RawQuery = q.Encode() 545 546 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 547 if err != nil { 548 return fmt.Errorf("%w: failed to create request: %s", ErrUpdateTFA, err) 549 } 550 551 resp, err := i.Exec(req, nil, nil) 552 if err != nil { 553 return fmt.Errorf("%w: request failed: %s", ErrUpdateTFA, err) 554 } 555 556 if resp.StatusCode != http.StatusNoContent { 557 return fmt.Errorf("%s: %w", ErrUpdateTFA, i.Error(resp)) 558 } 559 560 return nil 561 }