github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/identity/v3/users/requests.go (about)

     1  package users
     2  
     3  import (
     4  	"context"
     5  	"net/url"
     6  	"strings"
     7  
     8  	"github.com/vnpaycloud-console/gophercloud/v2"
     9  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/identity/v3/groups"
    10  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/identity/v3/projects"
    11  	"github.com/vnpaycloud-console/gophercloud/v2/pagination"
    12  )
    13  
    14  // Option is a specific option defined at the API to enable features
    15  // on a user account.
    16  type Option string
    17  
    18  const (
    19  	IgnoreChangePasswordUponFirstUse Option = "ignore_change_password_upon_first_use"
    20  	IgnorePasswordExpiry             Option = "ignore_password_expiry"
    21  	IgnoreLockoutFailureAttempts     Option = "ignore_lockout_failure_attempts"
    22  	MultiFactorAuthRules             Option = "multi_factor_auth_rules"
    23  	MultiFactorAuthEnabled           Option = "multi_factor_auth_enabled"
    24  )
    25  
    26  // ListOptsBuilder allows extensions to add additional parameters to
    27  // the List request
    28  type ListOptsBuilder interface {
    29  	ToUserListQuery() (string, error)
    30  }
    31  
    32  // ListOpts provides options to filter the List results.
    33  type ListOpts struct {
    34  	// DomainID filters the response by a domain ID.
    35  	DomainID string `q:"domain_id"`
    36  
    37  	// Enabled filters the response by enabled users.
    38  	Enabled *bool `q:"enabled"`
    39  
    40  	// IdpID filters the response by an Identity Provider ID.
    41  	IdPID string `q:"idp_id"`
    42  
    43  	// Name filters the response by username.
    44  	Name string `q:"name"`
    45  
    46  	// PasswordExpiresAt filters the response based on expiring passwords.
    47  	PasswordExpiresAt string `q:"password_expires_at"`
    48  
    49  	// ProtocolID filters the response by protocol ID.
    50  	ProtocolID string `q:"protocol_id"`
    51  
    52  	// UniqueID filters the response by unique ID.
    53  	UniqueID string `q:"unique_id"`
    54  
    55  	// Filters filters the response by custom filters such as
    56  	// 'name__contains=foo'
    57  	Filters map[string]string `q:"-"`
    58  }
    59  
    60  // ToUserListQuery formats a ListOpts into a query string.
    61  func (opts ListOpts) ToUserListQuery() (string, error) {
    62  	q, err := gophercloud.BuildQueryString(opts)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  
    67  	params := q.Query()
    68  	for k, v := range opts.Filters {
    69  		i := strings.Index(k, "__")
    70  		if i > 0 && i < len(k)-2 {
    71  			params.Add(k, v)
    72  		} else {
    73  			return "", InvalidListFilter{FilterName: k}
    74  		}
    75  	}
    76  
    77  	q = &url.URL{RawQuery: params.Encode()}
    78  	return q.String(), err
    79  }
    80  
    81  // List enumerates the Users to which the current token has access.
    82  func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
    83  	url := listURL(client)
    84  	if opts != nil {
    85  		query, err := opts.ToUserListQuery()
    86  		if err != nil {
    87  			return pagination.Pager{Err: err}
    88  		}
    89  		url += query
    90  	}
    91  	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
    92  		return UserPage{pagination.LinkedPageBase{PageResult: r}}
    93  	})
    94  }
    95  
    96  // Get retrieves details on a single user, by ID.
    97  func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) {
    98  	resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil)
    99  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   100  	return
   101  }
   102  
   103  // CreateOptsBuilder allows extensions to add additional parameters to
   104  // the Create request.
   105  type CreateOptsBuilder interface {
   106  	ToUserCreateMap() (map[string]any, error)
   107  }
   108  
   109  // CreateOpts provides options used to create a user.
   110  type CreateOpts struct {
   111  	// Name is the name of the new user.
   112  	Name string `json:"name" required:"true"`
   113  
   114  	// DefaultProjectID is the ID of the default project of the user.
   115  	DefaultProjectID string `json:"default_project_id,omitempty"`
   116  
   117  	// Description is a description of the user.
   118  	Description string `json:"description,omitempty"`
   119  
   120  	// DomainID is the ID of the domain the user belongs to.
   121  	DomainID string `json:"domain_id,omitempty"`
   122  
   123  	// Enabled sets the user status to enabled or disabled.
   124  	Enabled *bool `json:"enabled,omitempty"`
   125  
   126  	// Extra is free-form extra key/value pairs to describe the user.
   127  	Extra map[string]any `json:"-"`
   128  
   129  	// Options are defined options in the API to enable certain features.
   130  	Options map[Option]any `json:"options,omitempty"`
   131  
   132  	// Password is the password of the new user.
   133  	Password string `json:"password,omitempty"`
   134  }
   135  
   136  // ToUserCreateMap formats a CreateOpts into a create request.
   137  func (opts CreateOpts) ToUserCreateMap() (map[string]any, error) {
   138  	b, err := gophercloud.BuildRequestBody(opts, "user")
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	if opts.Extra != nil {
   144  		if v, ok := b["user"].(map[string]any); ok {
   145  			for key, value := range opts.Extra {
   146  				v[key] = value
   147  			}
   148  		}
   149  	}
   150  
   151  	return b, nil
   152  }
   153  
   154  // Create creates a new User.
   155  func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
   156  	b, err := opts.ToUserCreateMap()
   157  	if err != nil {
   158  		r.Err = err
   159  		return
   160  	}
   161  	resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{
   162  		OkCodes: []int{201},
   163  	})
   164  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   165  	return
   166  }
   167  
   168  // UpdateOptsBuilder allows extensions to add additional parameters to
   169  // the Update request.
   170  type UpdateOptsBuilder interface {
   171  	ToUserUpdateMap() (map[string]any, error)
   172  }
   173  
   174  // UpdateOpts provides options for updating a user account.
   175  type UpdateOpts struct {
   176  	// Name is the name of the new user.
   177  	Name string `json:"name,omitempty"`
   178  
   179  	// DefaultProjectID is the ID of the default project of the user.
   180  	DefaultProjectID string `json:"default_project_id,omitempty"`
   181  
   182  	// Description is a description of the user.
   183  	Description *string `json:"description,omitempty"`
   184  
   185  	// DomainID is the ID of the domain the user belongs to.
   186  	DomainID string `json:"domain_id,omitempty"`
   187  
   188  	// Enabled sets the user status to enabled or disabled.
   189  	Enabled *bool `json:"enabled,omitempty"`
   190  
   191  	// Extra is free-form extra key/value pairs to describe the user.
   192  	Extra map[string]any `json:"-"`
   193  
   194  	// Options are defined options in the API to enable certain features.
   195  	Options map[Option]any `json:"options,omitempty"`
   196  
   197  	// Password is the password of the new user.
   198  	Password string `json:"password,omitempty"`
   199  }
   200  
   201  // ToUserUpdateMap formats a UpdateOpts into an update request.
   202  func (opts UpdateOpts) ToUserUpdateMap() (map[string]any, error) {
   203  	b, err := gophercloud.BuildRequestBody(opts, "user")
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	if opts.Extra != nil {
   209  		if v, ok := b["user"].(map[string]any); ok {
   210  			for key, value := range opts.Extra {
   211  				v[key] = value
   212  			}
   213  		}
   214  	}
   215  
   216  	return b, nil
   217  }
   218  
   219  // Update updates an existing User.
   220  func Update(ctx context.Context, client *gophercloud.ServiceClient, userID string, opts UpdateOptsBuilder) (r UpdateResult) {
   221  	b, err := opts.ToUserUpdateMap()
   222  	if err != nil {
   223  		r.Err = err
   224  		return
   225  	}
   226  	resp, err := client.Patch(ctx, updateURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{
   227  		OkCodes: []int{200},
   228  	})
   229  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   230  	return
   231  }
   232  
   233  // ChangePasswordOptsBuilder allows extensions to add additional parameters to
   234  // the ChangePassword request.
   235  type ChangePasswordOptsBuilder interface {
   236  	ToUserChangePasswordMap() (map[string]any, error)
   237  }
   238  
   239  // ChangePasswordOpts provides options for changing password for a user.
   240  type ChangePasswordOpts struct {
   241  	// OriginalPassword is the original password of the user.
   242  	OriginalPassword string `json:"original_password"`
   243  
   244  	// Password is the new password of the user.
   245  	Password string `json:"password"`
   246  }
   247  
   248  // ToUserChangePasswordMap formats a ChangePasswordOpts into a ChangePassword request.
   249  func (opts ChangePasswordOpts) ToUserChangePasswordMap() (map[string]any, error) {
   250  	b, err := gophercloud.BuildRequestBody(opts, "user")
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	return b, nil
   256  }
   257  
   258  // ChangePassword changes password for a user.
   259  func ChangePassword(ctx context.Context, client *gophercloud.ServiceClient, userID string, opts ChangePasswordOptsBuilder) (r ChangePasswordResult) {
   260  	b, err := opts.ToUserChangePasswordMap()
   261  	if err != nil {
   262  		r.Err = err
   263  		return
   264  	}
   265  
   266  	resp, err := client.Post(ctx, changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{
   267  		OkCodes: []int{204},
   268  	})
   269  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   270  	return
   271  }
   272  
   273  // Delete deletes a user.
   274  func Delete(ctx context.Context, client *gophercloud.ServiceClient, userID string) (r DeleteResult) {
   275  	resp, err := client.Delete(ctx, deleteURL(client, userID), nil)
   276  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   277  	return
   278  }
   279  
   280  // ListGroups enumerates groups user belongs to.
   281  func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pager {
   282  	url := listGroupsURL(client, userID)
   283  	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
   284  		return groups.GroupPage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}}
   285  	})
   286  }
   287  
   288  // AddToGroup adds a user to a group.
   289  func AddToGroup(ctx context.Context, client *gophercloud.ServiceClient, groupID, userID string) (r AddToGroupResult) {
   290  	url := addToGroupURL(client, groupID, userID)
   291  	resp, err := client.Put(ctx, url, nil, nil, &gophercloud.RequestOpts{
   292  		OkCodes: []int{204},
   293  	})
   294  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   295  	return
   296  }
   297  
   298  // IsMemberOfGroup checks whether a user belongs to a group.
   299  func IsMemberOfGroup(ctx context.Context, client *gophercloud.ServiceClient, groupID, userID string) (r IsMemberOfGroupResult) {
   300  	url := isMemberOfGroupURL(client, groupID, userID)
   301  	resp, err := client.Head(ctx, url, &gophercloud.RequestOpts{
   302  		OkCodes: []int{204, 404},
   303  	})
   304  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   305  	if r.Err == nil {
   306  		if resp.StatusCode == 204 {
   307  			r.isMember = true
   308  		}
   309  	}
   310  	return
   311  }
   312  
   313  // RemoveFromGroup removes a user from a group.
   314  func RemoveFromGroup(ctx context.Context, client *gophercloud.ServiceClient, groupID, userID string) (r RemoveFromGroupResult) {
   315  	url := removeFromGroupURL(client, groupID, userID)
   316  	resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{
   317  		OkCodes: []int{204},
   318  	})
   319  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   320  	return
   321  }
   322  
   323  // ListProjects enumerates groups user belongs to.
   324  func ListProjects(client *gophercloud.ServiceClient, userID string) pagination.Pager {
   325  	url := listProjectsURL(client, userID)
   326  	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
   327  		return projects.ProjectPage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}}
   328  	})
   329  }
   330  
   331  // ListInGroup enumerates users that belong to a group.
   332  func ListInGroup(client *gophercloud.ServiceClient, groupID string, opts ListOptsBuilder) pagination.Pager {
   333  	url := listInGroupURL(client, groupID)
   334  	if opts != nil {
   335  		query, err := opts.ToUserListQuery()
   336  		if err != nil {
   337  			return pagination.Pager{Err: err}
   338  		}
   339  		url += query
   340  	}
   341  	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
   342  		return UserPage{pagination.LinkedPageBase{PageResult: r}}
   343  	})
   344  }