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