github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/iam/groups.go (about) 1 package iam 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 "strconv" 10 11 validation "github.com/go-ozzo/ozzo-validation/v4" 12 ) 13 14 type ( 15 // Groups is the IAM group API interface 16 Groups interface { 17 // CreateGroup creates a new group within a parent group_id specified in the request 18 // 19 // See: https://techdocs.akamai.com/iam-user-admin/reference/post-group 20 CreateGroup(context.Context, GroupRequest) (*Group, error) 21 22 // GetGroup returns a group's details 23 // 24 // See: https://techdocs.akamai.com/iam-user-admin/reference/get-group 25 GetGroup(context.Context, GetGroupRequest) (*Group, error) 26 27 // ListAffectedUsers lists users who are affected when a group is moved 28 // 29 // See: https://techdocs.akamai.com/iam-user-admin/reference/get-move-affected-users 30 ListAffectedUsers(context.Context, ListAffectedUsersRequest) ([]GroupUser, error) 31 32 // ListGroups lists all groups in which you have a scope of admin for the current account and contract type 33 // 34 // See: https://techdocs.akamai.com/iam-user-admin/reference/get-groups 35 ListGroups(context.Context, ListGroupsRequest) ([]Group, error) 36 37 // RemoveGroup removes a group based on group_id. We can only delete a sub-group, and only if that sub-group doesn't include any users 38 // 39 // See: https://techdocs.akamai.com/iam-user-admin/reference/delete-group 40 RemoveGroup(context.Context, RemoveGroupRequest) error 41 42 // UpdateGroupName changes the name of the group 43 // 44 // See: https://techdocs.akamai.com/iam-user-admin/reference/put-group 45 UpdateGroupName(context.Context, GroupRequest) (*Group, error) 46 47 // MoveGroup Move a nested group under another group within the same parent hierarchy 48 // 49 // See: https://techdocs.akamai.com/iam-user-admin/reference/post-groups-move 50 MoveGroup(context.Context, MoveGroupRequest) error 51 } 52 53 // GetGroupRequest describes the request parameters of the get group endpoint 54 GetGroupRequest struct { 55 GroupID int64 56 Actions bool 57 } 58 59 // Group describes the response of the list groups endpoint 60 Group struct { 61 Actions *GroupActions `json:"actions,omitempty"` 62 CreatedBy string `json:"createdBy"` 63 CreatedDate string `json:"createdDate"` 64 GroupID int64 `json:"groupId"` 65 GroupName string `json:"groupName"` 66 ModifiedBy string `json:"modifiedBy"` 67 ModifiedDate string `json:"modifiedDate"` 68 ParentGroupID int64 `json:"parentGroupId"` 69 SubGroups []Group `json:"subGroups,omitempty"` 70 } 71 72 // GroupActions encapsulates permissions available to the user for this group 73 GroupActions struct { 74 Delete bool `json:"delete"` 75 Edit bool `json:"edit"` 76 } 77 78 // GroupUser describes the response of the list affected users endpoint 79 GroupUser struct { 80 AccountID string `json:"accountId"` 81 Email string `json:"email"` 82 FirstName string `json:"firstName"` 83 IdentityID string `json:"uiIdentityId"` 84 LastLoginDate string `json:"lastLoginDate"` 85 LastName string `json:"lastName"` 86 UserName string `json:"uiUserName"` 87 } 88 89 // GroupRequest describes the request and body parameters for creating new group or updating a group name endpoint 90 GroupRequest struct { 91 GroupID int64 `json:"-"` 92 GroupName string `json:"groupName"` 93 } 94 95 // MoveGroupRequest describes the request body to move a group under another group 96 MoveGroupRequest struct { 97 SourceGroupID int64 `json:"sourceGroupId"` 98 DestinationGroupID int64 `json:"destinationGroupId"` 99 } 100 101 // ListAffectedUsersRequest describes the request and body parameters of the list affected users endpoint 102 ListAffectedUsersRequest struct { 103 DestinationGroupID int64 104 SourceGroupID int64 105 UserType string 106 } 107 108 // ListGroupsRequest describes the request parameters of the list groups endpoint 109 ListGroupsRequest struct { 110 Actions bool 111 } 112 113 // RemoveGroupRequest describes the request parameter for removing a group 114 RemoveGroupRequest struct { 115 GroupID int64 116 } 117 ) 118 119 const ( 120 // LostAccessUsers with a userType of lostAccess lose their access to the source group 121 LostAccessUsers = "lostAccess" 122 // GainAccessUsers with a userType of gainAccess gain their access to the source group 123 GainAccessUsers = "gainAccess" 124 ) 125 126 var ( 127 // ErrCreateGroup is returned when CreateGroup fails 128 ErrCreateGroup = errors.New("create group") 129 // ErrGetGroup is returned when GetGroup fails 130 ErrGetGroup = errors.New("get group") 131 // ErrListAffectedUsers is returned when ListAffectedUsers fails 132 ErrListAffectedUsers = errors.New("list affected users") 133 // ErrListGroups is returned when ListGroups fails 134 ErrListGroups = errors.New("list groups") 135 // ErrUpdateGroupName is returned when UpdateGroupName fails 136 ErrUpdateGroupName = errors.New("update group name") 137 // ErrRemoveGroup is returned when RemoveGroup fails 138 ErrRemoveGroup = errors.New("remove group") 139 // ErrMoveGroup is returned when MoveGroup fails 140 ErrMoveGroup = errors.New("move group") 141 ) 142 143 // Validate validates GetGroupRequest 144 func (r GetGroupRequest) Validate() error { 145 return validation.Errors{ 146 "groupID": validation.Validate(r.GroupID, validation.Required), 147 }.Filter() 148 } 149 150 // Validate validates GroupRequest 151 func (r GroupRequest) Validate() error { 152 return validation.Errors{ 153 "groupID": validation.Validate(r.GroupID, validation.Required), 154 "groupName": validation.Validate(r.GroupName, validation.Required), 155 }.Filter() 156 } 157 158 // Validate validates MoveGroupRequest 159 func (r MoveGroupRequest) Validate() error { 160 return validation.Errors{ 161 "destinationGroupID": validation.Validate(r.DestinationGroupID, validation.Required), 162 "sourceGroupID": validation.Validate(r.SourceGroupID, validation.Required), 163 }.Filter() 164 } 165 166 // Validate validates ListAffectedUsersRequest 167 func (r ListAffectedUsersRequest) Validate() error { 168 return validation.Errors{ 169 "destinationGroupID": validation.Validate(r.DestinationGroupID, validation.Required), 170 "sourceGroupID": validation.Validate(r.SourceGroupID, validation.Required), 171 "userType": validation.Validate(r.UserType, validation.In(LostAccessUsers, GainAccessUsers)), 172 }.Filter() 173 } 174 175 // Validate validates RemoveGroupRequest 176 func (r RemoveGroupRequest) Validate() error { 177 return validation.Errors{ 178 "groupID": validation.Validate(r.GroupID, validation.Required), 179 }.Filter() 180 } 181 182 func (i *iam) CreateGroup(ctx context.Context, params GroupRequest) (*Group, error) { 183 logger := i.Log(ctx) 184 logger.Debug("CreateGroup") 185 186 if err := params.Validate(); err != nil { 187 return nil, fmt.Errorf("%s: %w:\n%s", ErrCreateGroup, ErrStructValidation, err) 188 } 189 190 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/groups/%d", params.GroupID)) 191 if err != nil { 192 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateGroup, err) 193 } 194 195 req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), nil) 196 if err != nil { 197 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateGroup, err) 198 } 199 200 var result Group 201 resp, err := i.Exec(req, &result, params) 202 if err != nil { 203 return nil, fmt.Errorf("%w: request failed: %s", ErrCreateGroup, err) 204 } 205 206 if resp.StatusCode != http.StatusCreated { 207 return nil, fmt.Errorf("%s: %w", ErrCreateGroup, i.Error(resp)) 208 } 209 210 return &result, nil 211 } 212 213 func (i *iam) GetGroup(ctx context.Context, params GetGroupRequest) (*Group, error) { 214 logger := i.Log(ctx) 215 logger.Debug("GetGroup") 216 217 if err := params.Validate(); err != nil { 218 return nil, fmt.Errorf("%s: %w:\n%s", ErrGetGroup, ErrStructValidation, err) 219 } 220 221 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/groups/%d", params.GroupID)) 222 if err != nil { 223 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetGroup, err) 224 } 225 q := u.Query() 226 q.Add("actions", strconv.FormatBool(params.Actions)) 227 228 u.RawQuery = q.Encode() 229 230 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) 231 if err != nil { 232 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetGroup, err) 233 } 234 235 var result Group 236 resp, err := i.Exec(req, &result) 237 if err != nil { 238 return nil, fmt.Errorf("%w: request failed: %s", ErrGetGroup, err) 239 } 240 241 if resp.StatusCode != http.StatusOK { 242 return nil, fmt.Errorf("%s: %w", ErrGetGroup, i.Error(resp)) 243 } 244 245 return &result, nil 246 } 247 248 func (i *iam) ListAffectedUsers(ctx context.Context, params ListAffectedUsersRequest) ([]GroupUser, error) { 249 logger := i.Log(ctx) 250 logger.Debug("ListAffectedUsers") 251 252 if err := params.Validate(); err != nil { 253 return nil, fmt.Errorf("%s: %w:\n%s", ErrListAffectedUsers, ErrStructValidation, err) 254 } 255 256 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/groups/move/%d/%d/affected-users", params.SourceGroupID, params.DestinationGroupID)) 257 if err != nil { 258 return nil, fmt.Errorf("%w: failed to create request: %s", ErrListAffectedUsers, err) 259 } 260 261 if params.UserType != "" { 262 q := u.Query() 263 q.Add("userType", params.UserType) 264 u.RawQuery = q.Encode() 265 } 266 267 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) 268 if err != nil { 269 return nil, fmt.Errorf("%w: failed to create request: %s", ErrListAffectedUsers, err) 270 } 271 272 var result []GroupUser 273 resp, err := i.Exec(req, &result) 274 if err != nil { 275 return nil, fmt.Errorf("%w: request failed: %s", ErrListAffectedUsers, err) 276 } 277 278 if resp.StatusCode != http.StatusOK { 279 return nil, fmt.Errorf("%s: %w", ErrListAffectedUsers, i.Error(resp)) 280 } 281 282 return result, nil 283 } 284 285 func (i *iam) ListGroups(ctx context.Context, params ListGroupsRequest) ([]Group, error) { 286 logger := i.Log(ctx) 287 logger.Debug("ListGroups") 288 289 u, err := url.Parse("/identity-management/v2/user-admin/groups") 290 if err != nil { 291 return nil, fmt.Errorf("%w: failed to create request: %s", ErrListGroups, err) 292 } 293 q := u.Query() 294 q.Add("actions", strconv.FormatBool(params.Actions)) 295 296 u.RawQuery = q.Encode() 297 298 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) 299 if err != nil { 300 return nil, fmt.Errorf("%w: failed to create request: %s", ErrListGroups, err) 301 } 302 303 var result []Group 304 resp, err := i.Exec(req, &result) 305 if err != nil { 306 return nil, fmt.Errorf("%w: request failed: %s", ErrListGroups, err) 307 } 308 309 if resp.StatusCode != http.StatusOK { 310 return nil, fmt.Errorf("%s: %w", ErrListGroups, i.Error(resp)) 311 } 312 313 return result, nil 314 } 315 316 func (i *iam) RemoveGroup(ctx context.Context, params RemoveGroupRequest) error { 317 logger := i.Log(ctx) 318 logger.Debug("RemoveGroup") 319 320 if err := params.Validate(); err != nil { 321 return fmt.Errorf("%s: %w:\n%s", ErrRemoveGroup, ErrStructValidation, err) 322 } 323 324 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/groups/%d", params.GroupID)) 325 if err != nil { 326 return fmt.Errorf("%w: failed to create request: %s", ErrRemoveGroup, err) 327 } 328 329 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, u.String(), nil) 330 if err != nil { 331 return fmt.Errorf("%w: failed to create request: %s", ErrRemoveGroup, err) 332 } 333 334 resp, err := i.Exec(req, nil) 335 if err != nil { 336 return fmt.Errorf("%w: request failed: %s", ErrRemoveGroup, err) 337 } 338 339 if resp.StatusCode != http.StatusNoContent { 340 return fmt.Errorf("%s: %w", ErrRemoveGroup, i.Error(resp)) 341 } 342 343 return nil 344 } 345 346 func (i *iam) UpdateGroupName(ctx context.Context, params GroupRequest) (*Group, error) { 347 logger := i.Log(ctx) 348 logger.Debug("UpdateGroupName") 349 350 if err := params.Validate(); err != nil { 351 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateGroupName, ErrStructValidation, err) 352 } 353 354 u, err := url.Parse(fmt.Sprintf("/identity-management/v2/user-admin/groups/%d", params.GroupID)) 355 if err != nil { 356 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateGroupName, err) 357 } 358 359 req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) 360 if err != nil { 361 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateGroupName, err) 362 } 363 364 var result Group 365 resp, err := i.Exec(req, &result, params) 366 if err != nil { 367 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateGroupName, err) 368 } 369 370 if resp.StatusCode != http.StatusOK { 371 return nil, fmt.Errorf("%s: %w", ErrUpdateGroupName, i.Error(resp)) 372 } 373 374 return &result, nil 375 } 376 377 func (i *iam) MoveGroup(ctx context.Context, params MoveGroupRequest) error { 378 logger := i.Log(ctx) 379 logger.Debug("MoveGroup") 380 381 if err := params.Validate(); err != nil { 382 return fmt.Errorf("%s: %w:\n%s", ErrMoveGroup, ErrStructValidation, err) 383 } 384 385 u, err := url.Parse("/identity-management/v2/user-admin/groups/move") 386 if err != nil { 387 return fmt.Errorf("%w: failed to parse url: %s", ErrMoveGroup, err) 388 } 389 390 req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), nil) 391 if err != nil { 392 return fmt.Errorf("%w: failed to create request: %s", ErrMoveGroup, err) 393 } 394 395 resp, err := i.Exec(req, nil, params) 396 if err != nil { 397 return fmt.Errorf("%w: request failed: %s", ErrMoveGroup, err) 398 } 399 400 if resp.StatusCode != http.StatusNoContent { 401 return fmt.Errorf("%w: %s", ErrMoveGroup, i.Error(resp)) 402 } 403 404 return nil 405 }