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  }