Skip to content

Commit 6817886

Browse files
authored
Merge pull request #83 from 0x416e746f6e/feat/access-token-auth
feat: implement `access_token` auth method
2 parents 3b83768 + bff1e65 commit 6817886

File tree

5 files changed

+82
-9
lines changed

5 files changed

+82
-9
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,21 @@ Depending on the API you want to use, you may set the ``endpoint`` to:
9999
This lookup mechanism makes it easy to overload credentials for a specific
100100
project or user.
101101

102+
### Access Token
103+
104+
This authentication method is useful when short-lived credentials are necessary.
105+
E.g. oauth2 [plugin](https://github.com/puppetlabs/vault-plugin-secrets-oauthapp)
106+
for HashiCorp Vault can request an access token that would be used by OVHcloud
107+
terraform provider. Although this token, requested via data-source, would end up
108+
stored in the Terraform state file, that would pose less risk since the token
109+
validity would last for only 1 hour.
110+
111+
Other applications are of course also possible.
112+
113+
In order to use the access token with this wrapper either use
114+
`ovh.NewAccessTokenClient` to create the client, or pass the token via
115+
`OVH_ACCESS_TOKEN` environment variable to `ovh.NewDefaultClient`.
116+
102117
### Application Key/Application Secret
103118

104119
If you have completed successfully the __OAuth2__ part, you can continue to
@@ -354,9 +369,10 @@ client.Get("/xdsl/xdsl-yourservice", nil)
354369

355370
### Create a client
356371

357-
- Use ``ovh.NewDefaultClient()`` to create a client unsing endpoint and credentials from config files or environment
372+
- Use ``ovh.NewDefaultClient()`` to create a client using endpoint and credentials from config files or environment
358373
- Use ``ovh.NewEndpointClient()`` to create a client for a specific API and use credentials from config files or environment
359374
- Use ``ovh.NewOAuth2Client()`` to have full control over their authentication, using OAuth2 authentication method
375+
- Use ``ovh.NewAccessTokenClient()`` to have full control over their authentication, using token that was previously issued by auth/oauth2/token endpoint
360376
- Use ``ovh.NewClient()`` to have full control over their authentication, using legacy authentication method
361377

362378
### Query

ovh/configuration.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ func (c *Client) loadConfig(endpointName string) error {
105105
endpointName = getConfigValue(cfg, "default", "endpoint", "ovh-eu")
106106
}
107107

108+
if c.AccessToken == "" {
109+
c.AccessToken = getConfigValue(cfg, endpointName, "access_token", "")
110+
}
111+
108112
if c.AppKey == "" {
109113
c.AppKey = getConfigValue(cfg, endpointName, "application_key", "")
110114
}
@@ -125,19 +129,33 @@ func (c *Client) loadConfig(endpointName string) error {
125129
c.ClientSecret = getConfigValue(cfg, endpointName, "client_secret", "")
126130
}
127131

132+
configuredAuthMethods := []string{}
133+
if c.AppKey != "" || c.AppSecret != "" || c.ConsumerKey != "" {
134+
configuredAuthMethods = append(configuredAuthMethods, "application_key/application_secret")
135+
}
136+
if c.ClientID != "" || c.ClientSecret != "" {
137+
configuredAuthMethods = append(configuredAuthMethods, "client_id/client_secret")
138+
}
139+
if c.AccessToken != "" {
140+
configuredAuthMethods = append(configuredAuthMethods, "access_token")
141+
}
142+
143+
if len(configuredAuthMethods) > 1 {
144+
return fmt.Errorf("can't use multiple authentication methods: %s", strings.Join(configuredAuthMethods, ", "))
145+
}
146+
if len(configuredAuthMethods) == 0 {
147+
return errors.New(
148+
"missing authentication information, you need to provide one of the following: application_key/application_secret, client_id/client_secret, or access_token",
149+
)
150+
}
151+
128152
if (c.ClientID != "") != (c.ClientSecret != "") {
129153
return errors.New("invalid oauth2 config, both client_id and client_secret must be given")
130154
}
131155
if (c.AppKey != "") != (c.AppSecret != "") {
132156
return errors.New("invalid authentication config, both application_key and application_secret must be given")
133157
}
134158

135-
if c.ClientID != "" && c.AppKey != "" {
136-
return errors.New("can't use both application_key/application_secret and OAuth2 client_id/client_secret")
137-
} else if c.ClientID == "" && c.AppKey == "" {
138-
return errors.New("missing authentication information, you need to provide at least an application_key/application_secret or a client_id/client_secret")
139-
}
140-
141159
// Load real endpoint URL by name. If endpoint contains a '/', consider it as a URL
142160
if strings.Contains(endpointName, "/") {
143161
c.endpoint = endpointName

ovh/configuration_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func TestConfigFromNonExistingFile(t *testing.T) {
6464

6565
client := Client{}
6666
err := client.loadConfig("ovh-eu")
67-
td.CmpString(t, err, `missing authentication information, you need to provide at least an application_key/application_secret or a client_id/client_secret`)
67+
td.CmpString(t, err, `missing authentication information, you need to provide one of the following: application_key/application_secret, client_id/client_secret, or access_token`)
6868
}
6969

7070
func TestConfigFromInvalidINIFile(t *testing.T) {
@@ -185,7 +185,7 @@ func TestConfigInvalidBoth(t *testing.T) {
185185

186186
client := Client{}
187187
err := client.loadConfig("ovh-eu")
188-
td.CmpString(t, err, "can't use both application_key/application_secret and OAuth2 client_id/client_secret")
188+
td.CmpString(t, err, "can't use multiple authentication methods: application_key/application_secret, client_id/client_secret")
189189
}
190190

191191
func TestConfigOAuth2Invalid(t *testing.T) {

ovh/ovh.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ var (
6060

6161
// Client represents a client to call the OVH API
6262
type Client struct {
63+
// AccessToken is a short-lived access token that we got from auth/oauth2/token endpoint.
64+
AccessToken string
65+
6366
// Self generated tokens. Create one by visiting
6467
// https://eu.api.ovh.com/createApp/
6568
// AppKey holds the Application key
@@ -141,6 +144,20 @@ func NewOAuth2Client(endpoint, clientID, clientSecret string) (*Client, error) {
141144
return &client, nil
142145
}
143146

147+
func NewAccessTokenClient(endpoint, accessToken string) (*Client, error) {
148+
client := Client{
149+
AccessToken: accessToken,
150+
Client: &http.Client{},
151+
Timeout: DefaultTimeout,
152+
}
153+
154+
// Get and check the configuration
155+
if err := client.loadConfig(endpoint); err != nil {
156+
return nil, err
157+
}
158+
return &client, nil
159+
}
160+
144161
func (c *Client) Endpoint() string {
145162
return c.endpoint
146163
}
@@ -351,6 +368,8 @@ func (c *Client) NewRequest(method, path string, reqBody interface{}, needAuth b
351368
}
352369

353370
req.Header.Set("Authorization", "Bearer "+token.AccessToken)
371+
} else if c.AccessToken != "" {
372+
req.Header.Set("Authorization", "Bearer "+c.AccessToken)
354373
}
355374
}
356375

ovh/ovh_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,26 @@ func TestConstructorsOAuth2(t *testing.T) {
486486
}))
487487
}
488488

489+
func TestConstructorsAccessToken(t *testing.T) {
490+
assert, require := td.AssertRequire(t)
491+
492+
// Error: missing Endpoint
493+
client, err := NewAccessTokenClient("", "aaaaaaaa")
494+
assert.Nil(client)
495+
assert.String(err, `unknown endpoint '', consider checking 'Endpoints' list or using an URL`)
496+
497+
// Next: success cases
498+
expected := td.Struct(&Client{
499+
AccessToken: "aaaaaaaa",
500+
endpoint: "https://eu.api.ovh.com/1.0",
501+
})
502+
503+
// Nominal: full constructor
504+
client, err = NewAccessTokenClient("ovh-eu", "aaaaaaaa")
505+
require.CmpNoError(err)
506+
assert.Cmp(client, expected)
507+
}
508+
489509
func (ms *MockSuite) TestVersionInURL(assert, require *td.T) {
490510
// Signature checking mocks
491511
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/call", func(req *http.Request) (*http.Response, error) {

0 commit comments

Comments
 (0)