Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit 04ac06f

Browse files
author
Markus Suonto
committed
Improved NewClient endpoint handling
Made regular expression very strict. Gives false positives only for paths like "/v20./". Return approppriate errors for invalid version or if endpoint has a suffix. Path must not continue after matched version.
1 parent 4c17040 commit 04ac06f

File tree

3 files changed

+81
-8
lines changed

3 files changed

+81
-8
lines changed

openstack/client.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const (
1717
v30 = "v3.0"
1818
)
1919

20-
var VERSION_PATTERN = regexp.MustCompile("/v[1-9]{1}[0-9.]*")
20+
var VERSION_PATTERN = regexp.MustCompile("/v[1-9]{1}[0-9\\.]{0,2}/")
2121

2222
// NewClient prepares an unauthenticated ProviderClient instance.
2323
// Most users will probably prefer using the AuthenticatedClient function instead.
@@ -34,16 +34,24 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
3434
// Base is url with path
3535
endpoint = gophercloud.NormalizeURL(endpoint)
3636
base := gophercloud.NormalizeURL(u.String())
37-
3837
var location = VERSION_PATTERN.FindStringIndex(base)
3938

40-
// If version found remove it from base
4139
if location != nil {
42-
base = base[0:location[0]+1]
43-
return &gophercloud.ProviderClient{
44-
IdentityBase: base,
45-
IdentityEndpoint: endpoint,
46-
}, nil
40+
var version = base[location[0]+1:location[1]-1]
41+
switch version {
42+
case "v2.0", "v3":
43+
// post version suffixes in path are not supported
44+
if len(base) > location[1] {
45+
return nil, fmt.Errorf("Path suffixes (after version) are not supported.")
46+
}
47+
// valid version found, strip from base
48+
return &gophercloud.ProviderClient{
49+
IdentityBase: base[0:location[0]+1],
50+
IdentityEndpoint: endpoint,
51+
}, nil
52+
default:
53+
return nil, fmt.Errorf("Invalid identity endpoint version %v. Supported versions: v2.0, v3", version)
54+
}
4755
}
4856

4957
return &gophercloud.ProviderClient{

openstack/client_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,54 @@ func TestNewClient(t *testing.T) {
200200
th.AssertNoErr(t, err)
201201
th.AssertEquals(t, "https://example.com/foo/bar/v2.0/", client.IdentityEndpoint)
202202
th.AssertEquals(t, "https://example.com/foo/bar/", client.IdentityBase)
203+
204+
client, err = NewClient("https://example.com/foo/bar/v3/")
205+
th.AssertNoErr(t, err)
206+
th.AssertEquals(t, "https://example.com/foo/bar/v3/", client.IdentityEndpoint)
207+
th.AssertEquals(t, "https://example.com/foo/bar/", client.IdentityBase)
208+
209+
client, err = NewClient("https://example.com/v3")
210+
th.AssertNoErr(t, err)
211+
th.AssertEquals(t, "https://example.com/v3/", client.IdentityEndpoint)
212+
th.AssertEquals(t, "https://example.com/", client.IdentityBase)
213+
214+
client, err = NewClient("https://example.com/v3/")
215+
th.AssertNoErr(t, err)
216+
th.AssertEquals(t, "https://example.com/v3/", client.IdentityEndpoint)
217+
th.AssertEquals(t, "https://example.com/", client.IdentityBase)
218+
219+
_, err = NewClient("https://example.com/v2.3/")
220+
th.AssertErr(t, err)
221+
expected := fmt.Errorf("Invalid identity endpoint version %v. Supported versions: v2.0, v3", "v2.3")
222+
th.AssertEquals(t, expected.Error(), err.Error())
223+
224+
_, err = NewClient("https://example.com/v2.0/foo")
225+
th.AssertErr(t, err)
226+
expected = fmt.Errorf("Path suffixes (after version) are not supported.")
227+
th.AssertEquals(t, expected.Error(), err.Error())
228+
229+
// Does not match regexp, include to base
230+
client, err = NewClient("https://example.com/v2a0/")
231+
th.AssertNoErr(t, err)
232+
th.AssertEquals(t, "", client.IdentityEndpoint)
233+
th.AssertEquals(t, "https://example.com/v2a0/", client.IdentityBase)
234+
235+
// Does not match regexp, include to base
236+
client, err = NewClient("https://example.com/v3api")
237+
th.AssertNoErr(t, err)
238+
th.AssertEquals(t, "", client.IdentityEndpoint)
239+
th.AssertEquals(t, "https://example.com/v3api/", client.IdentityBase)
240+
241+
// Matches regexp, invalid version v20.
242+
_, err = NewClient("https://example.com/v20./")
243+
th.AssertErr(t, err)
244+
expected = fmt.Errorf("Invalid identity endpoint version %v. Supported versions: v2.0, v3", "v20.")
245+
th.AssertEquals(t, expected.Error(), err.Error())
246+
247+
// domain contains version
248+
client, err = NewClient("https://v3.v2.0example.com/v3/")
249+
th.AssertNoErr(t, err)
250+
th.AssertEquals(t, "https://v3.v2.0example.com/v3/", client.IdentityEndpoint)
251+
th.AssertEquals(t, "https://v3.v2.0example.com/", client.IdentityBase)
252+
203253
}

testhelper/convenience.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,21 @@ func CheckEquals(t *testing.T, expected, actual interface{}) {
224224
}
225225
}
226226

227+
// AssertErr is a convenience function for checking that an error occurred
228+
func AssertErr(t *testing.T, e error) {
229+
if e == nil {
230+
logFatal(t, fmt.Sprintf("expected an error but none occurred"))
231+
}
232+
}
233+
234+
// CheckErr is a convenience function for checking that an error occurred,
235+
// except with a non-fatal error
236+
func CheckErr(t *testing.T, e error) {
237+
if e == nil {
238+
logError(t, fmt.Sprintf("expected an error but none occurred"))
239+
}
240+
}
241+
227242
// AssertDeepEquals - like Equals - performs a comparison - but on more complex
228243
// structures that requires deeper inspection
229244
func AssertDeepEquals(t *testing.T, expected, actual interface{}) {

0 commit comments

Comments
 (0)