Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth expire handling for govcd.VCDClient #377

Open
lelvisl opened this issue May 13, 2021 · 1 comment
Open

Auth expire handling for govcd.VCDClient #377

lelvisl opened this issue May 13, 2021 · 1 comment

Comments

@lelvisl
Copy link
Contributor

lelvisl commented May 13, 2021

As long as you using *govcd.VCDClient for a long time, its auth expires in accordance with auth timeout settings in vcd.

State now: handle 401 error and reauth, after this repeat request. And you need have this logic near of every govcd call.

Example of solution with http transport:

package vcdreauth

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/vmware/go-vcloud-director/v2/govcd"
)

type Transport struct {
	cli         *govcd.VCDClient
	user        string
	password    string
	org         string
	next        http.RoundTripper
	maxAttempts uint
}

func NewReAuthTransport(cli *govcd.VCDClient, user, password, org string, maxAttempts uint) *Transport {
	if maxAttempts < 1 {
		maxAttempts = DefaultReAuthMaxAttempts
	}
	return &Transport{
		cli:         cli,
		user:        user,
		password:    password,
		org:         org,
		next:        cli.Client.Http.Transport,
		maxAttempts: maxAttempts,
	}
}

const DefaultReAuthMaxAttempts = 1

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
	for count := 0; count < DefaultReAuthMaxAttempts+1; count++ {
		// Perform request
		resp, err := t.next.RoundTrip(req)

		// don't try to retry auth enpoints
		if req.URL.Path == "/api/sessions" || strings.Contains(req.URL.Path, "/cloudapi/1.0.0/sessions/provider") {
			return resp, err
		}

		// if status not 401 - don't try to retry
		if resp.StatusCode != http.StatusUnauthorized {
			return resp, err
		}

		// close the response body when we wont use it anymore
		if resp != nil {
			_ = resp.Body.Close()
		}
		authResp, err := t.cli.GetAuthResponse(t.user, t.password, t.org)
		if err != nil {
			return nil, fmt.Errorf("unable to authenticate: %w", err)
		}

		// re-setup auth headers
		if t.cli.Client.VCDAuthHeader != "" && t.cli.Client.VCDToken != "" {
			// Add the authorization header
			req.Header.Set(t.cli.Client.VCDAuthHeader, t.cli.Client.VCDToken)
		}
		if len(t.cli.Client.VCDToken) > 32 {
			req.Header.Set("X-Vmware-Vcloud-Token-Type", "Bearer")
			req.Header.Set("Authorization", "bearer "+t.cli.Client.VCDToken)
		}

		if authResp != nil {
			_ = authResp.Body.Close()
		}
	}
	return nil, fmt.Errorf("unauthorized - max attempts (%d)", t.maxAttempts)
}

Example of usage:

	vcdclient := govcd.NewVCDClient(*u, Insecure, govcd.WithHttpUserAgent(userAgent))
	vcdclient.Client.Http.Transport = vcdreauth.NewReAuthTransport(vcdclient, User, Password, systemOrg, 1)
	_, err := vcdclient.GetAuthResponse(User, Password, systemOrg)

if you ok with this solution, i can add it as PR.

P.S. I'm already using this code in my project and its working well.

@lelvisl
Copy link
Contributor Author

lelvisl commented Aug 8, 2021

@dataclouder is it correct flow for reauth?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant