github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/identity/v3/tokens/requests.go (about) 1 package tokens 2 3 import ( 4 "github.com/opentelekomcloud/gophertelekomcloud" 5 ) 6 7 // Scope allows a created token to be limited to a specific domain or project. 8 type Scope struct { 9 ProjectID string 10 ProjectName string 11 DomainID string 12 DomainName string 13 } 14 15 // AuthOptionsBuilder provides the ability for extensions to add additional 16 // parameters to AuthOptions. Extensions must satisfy all required methods. 17 type AuthOptionsBuilder interface { 18 // ToTokenV3CreateMap assembles the Create request body, returning an error 19 // if parameters are missing or inconsistent. 20 ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) 21 ToTokenV3ScopeMap() (map[string]interface{}, error) 22 CanReauth() bool 23 AuthTokenID() string 24 AuthHeaderDomainID() string 25 } 26 27 // AuthOptions represents options for authenticating a user. 28 type AuthOptions struct { 29 // IdentityEndpoint specifies the HTTP endpoint that is required to work with 30 // the Identity API of the appropriate version. While it's ultimately needed 31 // by all of the identity services, it will often be populated by a 32 // provider-level function. 33 IdentityEndpoint string `json:"-"` 34 35 // Username is required if using Identity V2 API. Consult with your provider's 36 // control panel to discover your account's username. In Identity V3, either 37 // UserID or a combination of Username and DomainID or DomainName are needed. 38 Username string `json:"username,omitempty"` 39 UserID string `json:"id,omitempty"` 40 41 Password string `json:"password,omitempty"` 42 43 // At most one of DomainID and DomainName must be provided if using Username 44 // with Identity V3. Otherwise, either are optional. 45 DomainID string `json:"-"` 46 DomainName string `json:"name,omitempty"` 47 48 // AllowReauth should be set to true if you grant permission for Gophercloud 49 // to cache your credentials in memory, and to allow Gophercloud to attempt 50 // to re-authenticate automatically if/when your token expires. If you set 51 // it to false, it will not cache these settings, but re-authentication will 52 // not be possible. This setting defaults to false. 53 AllowReauth bool `json:"-"` 54 55 // TokenID allows users to authenticate (possibly as another user) with an 56 // authentication token ID. 57 TokenID string `json:"-"` 58 59 // Passcode is a Virtual MFA device verification code, which can be obtained on the MFA app. 60 Passcode string `json:"-"` 61 62 Scope Scope `json:"-"` 63 } 64 65 // ToTokenV3CreateMap builds a request body from AuthOptions. 66 func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { 67 golangsdkAuthOpts := golangsdk.AuthOptions{ 68 Username: opts.Username, 69 UserID: opts.UserID, 70 Password: opts.Password, 71 DomainID: opts.DomainID, 72 DomainName: opts.DomainName, 73 AllowReauth: opts.AllowReauth, 74 TokenID: opts.TokenID, 75 Passcode: opts.Passcode, 76 } 77 78 return golangsdkAuthOpts.ToTokenV3CreateMap(scope) 79 } 80 81 // ToTokenV3ScopeMap builds a scope request body from AuthOptions. 82 func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { 83 if opts.Scope.ProjectName != "" { 84 // ProjectName provided: either DomainID or DomainName must also be supplied. 85 // ProjectID may not be supplied. 86 if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { 87 return nil, golangsdk.ErrScopeDomainIDOrDomainName{} 88 } 89 if opts.Scope.ProjectID != "" { 90 return nil, golangsdk.ErrScopeProjectIDOrProjectName{} 91 } 92 93 if opts.Scope.DomainID != "" { 94 // ProjectName + DomainID 95 return map[string]interface{}{ 96 "project": map[string]interface{}{ 97 "name": &opts.Scope.ProjectName, 98 "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, 99 }, 100 }, nil 101 } 102 103 if opts.Scope.DomainName != "" { 104 // ProjectName + DomainName 105 return map[string]interface{}{ 106 "project": map[string]interface{}{ 107 "name": &opts.Scope.ProjectName, 108 "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, 109 }, 110 }, nil 111 } 112 } else if opts.Scope.ProjectID != "" { 113 // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. 114 if opts.Scope.DomainID != "" { 115 return nil, golangsdk.ErrScopeProjectIDAlone{} 116 } 117 if opts.Scope.DomainName != "" { 118 return nil, golangsdk.ErrScopeProjectIDAlone{} 119 } 120 121 // ProjectID 122 return map[string]interface{}{ 123 "project": map[string]interface{}{ 124 "id": &opts.Scope.ProjectID, 125 }, 126 }, nil 127 } else if opts.Scope.DomainID != "" { 128 // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. 129 if opts.Scope.DomainName != "" { 130 return nil, golangsdk.ErrScopeDomainIDOrDomainName{} 131 } 132 133 // DomainID 134 return map[string]interface{}{ 135 "domain": map[string]interface{}{ 136 "id": &opts.Scope.DomainID, 137 }, 138 }, nil 139 } else if opts.Scope.DomainName != "" { 140 // DomainName 141 return map[string]interface{}{ 142 "domain": map[string]interface{}{ 143 "name": &opts.Scope.DomainName, 144 }, 145 }, nil 146 } 147 148 return nil, nil 149 } 150 151 func (opts *AuthOptions) CanReauth() bool { 152 return opts.AllowReauth 153 } 154 155 func (opts *AuthOptions) AuthTokenID() string { 156 return "" 157 } 158 159 func (opts *AuthOptions) AuthHeaderDomainID() string { 160 return "" 161 } 162 163 func subjectTokenHeaders(_ *golangsdk.ServiceClient, subjectToken string) map[string]string { 164 return map[string]string{ 165 "X-Subject-Token": subjectToken, 166 } 167 } 168 169 // Create authenticates and either generates a new token, or changes the Scope 170 // of an existing token. 171 func Create(c *golangsdk.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { 172 scope, err := opts.ToTokenV3ScopeMap() 173 if err != nil { 174 r.Err = err 175 return 176 } 177 178 b, err := opts.ToTokenV3CreateMap(scope) 179 if err != nil { 180 r.Err = err 181 return 182 } 183 184 resp, err := c.Post(tokenURL(c), b, &r.Body, &golangsdk.RequestOpts{ 185 MoreHeaders: map[string]string{ 186 "X-Auth-Token": opts.AuthTokenID(), 187 "X-Domain-Id": opts.AuthHeaderDomainID(), 188 }, 189 }) 190 r.Err = err 191 if resp != nil { 192 r.Header = resp.Header 193 } 194 return 195 } 196 197 // Get validates and retrieves information about another token. 198 func Get(c *golangsdk.ServiceClient, token string) (r GetResult) { 199 resp, err := c.Get(tokenURL(c), &r.Body, &golangsdk.RequestOpts{ 200 MoreHeaders: subjectTokenHeaders(c, token), 201 OkCodes: []int{200, 203}, 202 }) 203 if resp != nil { 204 r.Err = err 205 r.Header = resp.Header 206 } 207 return 208 } 209 210 // Validate determines if a specified token is valid or not. 211 func Validate(c *golangsdk.ServiceClient, token string) (bool, error) { 212 resp, err := c.Request("HEAD", tokenURL(c), &golangsdk.RequestOpts{ 213 MoreHeaders: subjectTokenHeaders(c, token), 214 OkCodes: []int{200, 204, 404}, 215 }) 216 if err != nil { 217 return false, err 218 } 219 220 return resp.StatusCode == 200 || resp.StatusCode == 204, nil 221 } 222 223 // Revoke immediately makes specified token invalid. 224 func Revoke(c *golangsdk.ServiceClient, token string) (r RevokeResult) { 225 _, r.Err = c.Delete(tokenURL(c), &golangsdk.RequestOpts{ 226 MoreHeaders: subjectTokenHeaders(c, token), 227 }) 228 return 229 }