From a5cc0d8c427460ecc5b544a9fbe88d8fd7396d59 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 22 Jun 2023 11:31:18 -0500 Subject: [PATCH] identity: use AuthOptionsBuilder for v2 auth Rather than creating a tokens2.AuthOptions inside the authentication flow for identity v2, allow any structure that implements the tokens2.AuthBuilderOptions interface to be used. The interface is modified to add a CanReauth function like the v3 version has but provides a default implementation. The gophercloud.AuthOptions already implements this interface so there is no change to its API. Lastly add an AuthenticateV2Ext function which will allow an out of tree identity v2 auth mechanism to be implemented. fixes #2651. ref #1330. --- openstack/client.go | 30 ++++++++++-------------- openstack/identity/v2/tokens/requests.go | 7 +++++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 81c907c35b..26470d4590 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -102,7 +102,7 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp switch chosen.ID { case v2: - return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) + return v2auth(client, endpoint, &options, gophercloud.EndpointOpts{}) case v3: return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) default: @@ -113,10 +113,15 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp // AuthenticateV2 explicitly authenticates against the identity v2 endpoint. func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { + return v2auth(client, "", &options, eo) +} + +// AuthenticateV2Ext explicitly authenticates against the identity v2 endpoint. +func AuthenticateV2Ext(client *gophercloud.ProviderClient, options tokens2.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { return v2auth(client, "", options, eo) } -func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { +func v2auth(client *gophercloud.ProviderClient, endpoint string, options tokens2.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { v2Client, err := NewIdentityV2(client, eo) if err != nil { return err @@ -126,17 +131,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc v2Client.Endpoint = endpoint } - v2Opts := tokens2.AuthOptions{ - IdentityEndpoint: options.IdentityEndpoint, - Username: options.Username, - Password: options.Password, - TenantID: options.TenantID, - TenantName: options.TenantName, - AllowReauth: options.AllowReauth, - TokenID: options.TokenID, - } - - result := tokens2.Create(v2Client, v2Opts) + result := tokens2.Create(v2Client, options) err = client.SetTokenAndAuthResult(result) if err != nil { @@ -148,7 +143,10 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc return err } - if options.AllowReauth { + // use if the client's ReauthFunc is set to avoid needing to copy the AuthOptions and mutating the AllowReauth value + // when creating the reauthentication function below. this allows breaking of the loop without v2auth having to have + // knowledge of the supplied struct, allowing for out of tree extensions + if options.CanReauth() && client.ReauthFunc != nil { // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once @@ -156,10 +154,8 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc tac.SetThrowaway(true) tac.ReauthFunc = nil tac.SetTokenAndAuthResult(nil) - tao := options - tao.AllowReauth = false client.ReauthFunc = func() error { - err := v2auth(&tac, endpoint, tao, eo) + err := v2auth(&tac, endpoint, options, eo) if err != nil { return err } diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 84f16c3fc2..1203a0e5c9 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -38,6 +38,7 @@ type AuthOptionsBuilder interface { // ToTokenCreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. ToTokenV2CreateMap() (map[string]interface{}, error) + CanReauth() bool } // AuthOptions are the valid options for Openstack Identity v2 authentication. @@ -53,7 +54,7 @@ type AuthOptions struct { } // ToTokenV2CreateMap builds a token request body from the given AuthOptions. -func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { v2Opts := AuthOptionsV2{ TenantID: opts.TenantID, TenantName: opts.TenantName, @@ -77,6 +78,10 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return b, nil } +func (opts *AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + // Create authenticates to the identity service and attempts to acquire a Token. // Generally, rather than interact with this call directly, end users should // call openstack.AuthenticatedClient(), which abstracts all of the gory details