-
Notifications
You must be signed in to change notification settings - Fork 166
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
Fix storing URLs without scheme #72
Conversation
{"http://foobar.docker.io:2376", "https://foobar.docker.io:2376"}, | ||
|
||
// same: stored as http, retrieved without a scheme specified (hence, using the default https://) | ||
// TODO is this desired behavior? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't sure about this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one looks good to me
{"https://foobar.docker.io:1234", "https://foobar.docker.io:5678"}, | ||
|
||
// non-matching ports | ||
// TODO is this desired behavior? The other way round does work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure here either, I'd drop this test.
osxkeychain/osxkeychain_darwin.go
Outdated
// parseURL parses and validates a given serverURL to an url.URL, and | ||
// returns an error if validation failed. Querystring parameters are | ||
// omitted in the resulting URL, because they are not used in the helper | ||
func parseURL(serverURL string) (*url.URL, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Splitted this to a separate function, so that we can test independently 😄
osxkeychain/osxkeychain_darwin.go
Outdated
return nil, errors.New("no hostname in URL") | ||
} | ||
|
||
u.RawQuery = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly strip query-string, because we're not storing it, but let me know if you don't want this 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good catch!
3e8a573
to
de83797
Compare
{"https://foobar.docker.io:2376", "foobar.docker.io"}, | ||
|
||
// stored with path, retrieved without | ||
// TODO is this the desired behavior? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't sure about this either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Path is not kept in the keychain, this is the desired behavior AAIK 👍
osxkeychain/osxkeychain_darwin.go
Outdated
// returns an error if validation failed. Querystring parameters are | ||
// omitted in the resulting URL, because they are not used in the helper | ||
func parseURL(serverURL string) (*url.URL, error) { | ||
if !regexp.MustCompile(`^([a-zA-Z][-+.a-zA-Z0-9]+:)?//`).MatchString(serverURL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we add a comment to know what this regex checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, makes sense
osxkeychain/osxkeychain_darwin.go
Outdated
return nil, errors.New("no hostname in URL") | ||
} | ||
|
||
u.RawQuery = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good catch!
osxkeychain/osxkeychain_darwin.go
Outdated
} | ||
|
||
return &C.struct_Server{ | ||
proto: C.SecProtocolType(proto), | ||
host: C.CString(host), | ||
host: C.CString(u.Hostname()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is u.Hostname()
different from u.Host
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
u.Hostname()
strips the port; https://golang.org/src/net/url/url.go?s=28154:28185#L997
{"https://foobar.docker.io:2376", "foobar.docker.io"}, | ||
|
||
// stored with path, retrieved without | ||
// TODO is this the desired behavior? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Path is not kept in the keychain, this is the desired behavior AAIK 👍
continue | ||
} | ||
if te.err != nil && err == nil { | ||
t.Errorf("Error: expected parsing to fail for URL %q", te.url) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we include the expected error message of te.err
?
{"http://foobar.docker.io:2376", "https://foobar.docker.io:2376"}, | ||
|
||
// same: stored as http, retrieved without a scheme specified (hence, using the default https://) | ||
// TODO is this desired behavior? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one looks good to me
{"https://foobar.docker.io:1234", "https://foobar.docker.io:5678"}, | ||
|
||
// non-matching ports | ||
// TODO is this desired behavior? The other way round does work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure here either, I'd drop this test.
Secret: fmt.Sprintf("secret-%d", i), | ||
} | ||
|
||
if err := helper.Add(c); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
helper.Delete
before testing and defer helper.Delete
in order to remove all the added credentials, otherwise it generates false positives/negatives with creds from previous tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did comment backward for this one sorry, added references to other spots where it should be done too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future reference; this was addressed through this part above;
// Clean store before testing.
for _, te := range tests {
helper.Delete(te.url)
}
helper := Osxkeychain{} | ||
for _, te := range tests { | ||
c := &credentials.Credentials{ServerURL: te.storeURL, Username: "hello", Secret: "world"} | ||
if err := helper.Add(c); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing here for the Delete -> Add -> defer Delete
helper := Osxkeychain{} | ||
for _, te := range tests { | ||
c := &credentials.Credentials{ServerURL: te.storeURL, Username: "hello", Secret: "world"} | ||
if err := helper.Add(c); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing here for the Delete -> Add -> defer Delete
8d249de
to
42d4782
Compare
LGTM! travisCI is failing because it compiles with Golang 1.6, I don't have the rights to update I think. @vdemeester can you review this please? ^_^ |
Follow up issue on this on the CLI side: docker/cli#190 We now need to fix the behavior of stripping the protocol when a user adds it to the URL. Since we'll default to HTTPS, they'll have explicitly specify protocol: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
42d4782
to
9fc60eb
Compare
Hm, looks like it fails on Go 1.7 as well; were these options added in 1.8?
|
Ugh looks like it was added in Go 1.8 https://golang.org/doc/go1.8 |
9fc60eb
to
181227e
Compare
} | ||
|
||
u.RawQuery = "" | ||
return u, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of striping the query, and checking the scheme above what do you think about returning a different struct here instead of url.Url
. It seems we're not really using much of the url.
type url struct {
proto string
host string
port string
path string
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're using the Port() and Hostname() methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but those values could be written to this new struct before returning. Either way works fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of that first, but decided to keep it simple
osxkeychain/osxkeychain_darwin.go
Outdated
// Without a scheme, the hostname is seen as "path" by `url.Parse()` | ||
if !regexp.MustCompile(`^([a-zA-Z][-+.a-zA-Z0-9]+:)?//`).MatchString(serverURL) { | ||
serverURL = "//" + serverURL | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could call url.Parse()
and if u.Scheme == ""
, then prepend //
and call it again on the new string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, funny, that was my original proposal; #55 (comment) / https://play.golang.org/p/uSIHiJ55ir
osxkeychain/osxkeychain_darwin.go
Outdated
func parseURL(serverURL string) (*url.URL, error) { | ||
// Check if serverURL has a valid scheme, otherwise add `//` as scheme. | ||
// Without a scheme, the hostname is seen as "path" by `url.Parse()` | ||
if !regexp.MustCompile(`^([a-zA-Z][-+.a-zA-Z0-9]+:)?//`).MatchString(serverURL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no biggie, but if you update the PR, you could put this mustcompile in a package level var
osxkeychain/osxkeychain_darwin.go
Outdated
var port int | ||
if len(hostAndPort) == 2 { | ||
p, err := strconv.Atoi(hostAndPort[1]) | ||
if u.Port() != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These go1.8 Port() and Hostname() methods are only useful for supporting ipv6, based on what I've read from their code. We could argue it's out of scope for this PR. Or we should provide pre-1.8 functions with !go1.8
buildtags
If secrets are stored without specifying a scheme (https://), the keychain-helper would interpret the hostname as _path_, causing lookup of secrets to fail. This patch makes sure that a scheme is added (if missing). If no scheme is specified, https:// is used as a default. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Tibor Vass <teabee89@gmail.com>
934f22e
to
7cfc1fc
Compare
ping @dnephin @thaJeztah |
osxkeychain/osxkeychain_darwin.go
Outdated
} | ||
// Check if serverURL has a valid scheme, otherwise add `//` as scheme. | ||
// Without a scheme, the hostname is seen as "path" by `url.Parse()` | ||
if u.Scheme == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may not actually work; localhost:443
will be parsed as scheme: localhost, host: 443
; https://play.golang.org/p/QRE7pqxjx1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To simplify, we can use
if !strings.Contains(serverURL, "://") && !strings.HasPrefix(serverURL, "//") {
serverURL = "//" + serverURL
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good
Another note: this should be a fix for all OSes and not specific to OSX. Fixing. |
@tiborvass at the wrapper level? Then, we'll have to warn every folk implementing a credential helper. |
Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
LGTM |
osxkeychain/osxkeychain_darwin.go
Outdated
"unsafe" | ||
|
||
"github.com/docker/docker-credential-helpers/credentials" | ||
"strings" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to gofmt
it is :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I guess it's because docker/docker follows the goimport
conventions, in which case they are grouped, and sorted, but gofmt
won't complain if they were not grouped.
gofmt follows a simple rule, which is to sort each group of imports,
where different groups are separated by a blank line.goimports also follows a simple rule, which is to put stdlib imports
in one group and non-stdlib imports in a different group.
osxkeychain/osxkeychain_darwin.go
Outdated
// before parsing. This prevents the hostname being used as path, | ||
// and the credentials being stored without host. | ||
func parseURL(serverURL string) (*url.URL, error) { | ||
// Check if serverURL has a valid scheme, otherwise add `//` as scheme. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remove "valid" here in the comment? This is just a detection is a "possible" scheme is present
Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
@@ -1,6 +1,8 @@ | |||
package osxkeychain | |||
|
|||
import ( | |||
"errors" | |||
"fmt" | |||
"github.com/docker/docker-credential-helpers/credentials" | |||
"testing" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, not sure what formatting is used in this repo, but ideally would be;
import (
"errors"
"fmt"
"testing"
"github.com/docker/docker-credential-helpers/credentials"
)
Just a nit, so not a blocker
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll investigate that for a next PR :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one tiny nit, but LGTM
Someone just linked me this: https://github.com/goware/urlx I think this is exactly what we want for the url parsing code. |
@dnephin that looks neat indeed. Only issue I see is that it defaults to http, but perhaps we can make a proposal to make it configurable, or have a function that allows passing in defaults for missing components |
If secrets are stored without specifying a scheme
(https://), the keychain-helper would interpret the
hostname as path, causing lookup of secrets to fail.
This patch makes sure that a scheme is added (if missing).
If no scheme is specified, https:// is used as a default.