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

Added Authorization Bearer token support as per RFC6750 #1

Merged
merged 3 commits into from
Aug 8, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions agent/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,8 @@ func (s *HTTPServer) parseDC(req *http.Request, dc *string) {
}
}

// parseTokenInternal is used to parse the ?token query param or the X-Consul-Token header and
// parseTokenInternal is used to parse the ?token query param or the X-Consul-Token header or
// Authorization Bearer token (RFC6750) and
// optionally resolve proxy tokens to real ACL tokens. If no token is specified it will populate
// the token with the agents UserToken (acl_token in the consul configuration)
func (s *HTTPServer) parseTokenInternal(req *http.Request, token *string, resolveProxyToken bool) {
Expand All @@ -572,6 +573,8 @@ func (s *HTTPServer) parseTokenInternal(req *http.Request, token *string, resolv
tok = other
} else if other := req.Header.Get("X-Consul-Token"); other != "" {
tok = other
} else if other := req.Header.Get("Authorization"); other != "" {
tok = strings.Split(other, "Bearer ")[1]
}

if tok != "" {
Expand All @@ -589,13 +592,14 @@ func (s *HTTPServer) parseTokenInternal(req *http.Request, token *string, resolv
*token = s.agent.tokens.UserToken()
}

// parseToken is used to parse the ?token query param or the X-Consul-Token header and
// resolve proxy tokens to real ACL tokens
// parseToken is used to parse the ?token query param or the X-Consul-Token header or
// Authorization Bearer token header (RFC6750) and resolve proxy tokens to real ACL tokens
func (s *HTTPServer) parseToken(req *http.Request, token *string) {
s.parseTokenInternal(req, token, true)
}

// parseTokenWithoutResolvingProxyToken is used to parse the ?token query param or the X-Consul-Token header
// or Authorization Bearer header token (RFC6750) and
func (s *HTTPServer) parseTokenWithoutResolvingProxyToken(req *http.Request, token *string) {
s.parseTokenInternal(req, token, false)
}
Expand Down
36 changes: 36 additions & 0 deletions agent/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,19 @@ func TestACLResolution(t *testing.T) {
reqBothTokens, _ := http.NewRequest("GET", "/v1/catalog/nodes?token=baz", nil)
reqBothTokens.Header.Add("X-Consul-Token", "zap")

// Request with Authorization token
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add on: Authorization *Bearer* token

reqAuthToken, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil)
reqAuthToken.Header.Add("Authorization", "Bearer topsecret")

// Request with Authorization and querystring token
reqAuthAndQsToken, _ := http.NewRequest("GET", "/v1/catalog/nodes?token=qstoken", nil)
reqAuthAndQsToken.Header.Add("Authorization", "Bearer bearertoken")

// Request with Authorization and X-Consul-Token header token
reqAuthAndXToken, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil)
reqAuthAndXToken.Header.Add("X-Consul-Token", "xtoken")
reqAuthAndXToken.Header.Add("Authorization", "Bearer notparsed")

a := NewTestAgent(t.Name(), "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for when Authorization: Bearer is passed (we expect the test to fail because of this line )

defer a.Shutdown()

Expand Down Expand Up @@ -770,6 +783,29 @@ func TestACLResolution(t *testing.T) {
if token != "baz" {
t.Fatalf("bad: %s", token)
}

//
// Authorization Bearer token tests
//

// Check if Authorization bearer token header is parsed correctly
a.srv.parseToken(reqAuthToken, &token)
if token != "topsecret" {
t.Fatalf("bad: %s", token)
}

// Check if explicit token has precedence over Authorization bearer token
a.srv.parseToken(reqAuthAndQsToken, &token)
if token != "qstoken" {
t.Fatalf("bad: %s", token)
}

// Check if X-Consul-Token has precedence over Authorization bearer token
a.srv.parseToken(reqAuthAndXToken, &token)
if token != "xtoken" {
t.Fatalf("bad: %s", token)
}

}

func TestEnableWebUI(t *testing.T) {
Expand Down
7 changes: 4 additions & 3 deletions website/source/api/index.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ All API routes are prefixed with `/v1/`. This documentation is only for the v1 A
Several endpoints in Consul use or require ACL tokens to operate. An agent
can be configured to use a default token in requests using the `acl_token`
configuration option. However, the token can also be specified per-request
by using the `X-Consul-Token` request header or the `token` query string
parameter. The request header takes precedence over the default token, and
the query string parameter takes precedence over everything.
by using the `X-Consul-Token` request header or Bearer header in Authorization
header or the `token` query string parameter. The request header takes
precedence over the default token, and the query string parameter takes
precedence over everything.

For more details about ACLs, please see the [ACL Guide](/docs/guides/acl.html).

Expand Down
9 changes: 5 additions & 4 deletions website/source/docs/guides/acl.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ The type is either "client" (meaning the token cannot modify ACL rules) or "mana

The token ID is passed along with each RPC request to the servers. Consul's
[HTTP endpoints](/api/index.html) can accept tokens via the `token`
query string parameter, or the `X-Consul-Token` request header. Consul's
query string parameter, or the `X-Consul-Token` request header, or Authorization Bearer
token [RFC6750](https://tools.ietf.org/html/rfc6750). Consul's
[CLI commands](/docs/commands/index.html) can accept tokens via the
`token` argument, or the `CONSUL_HTTP_TOKEN` environment variable.

Expand Down Expand Up @@ -612,9 +613,9 @@ On success, the token ID is returned:
```

This token ID can then be passed into Consul's HTTP APIs via the `token`
query string parameter, or the `X-Consul-Token` request header, or Consul's
CLI commands via the `token` argument, or the `CONSUL_HTTP_TOKEN` environment
variable.
query string parameter, or the `X-Consul-Token` request header, or Authorization
Bearer token header, or Consul's CLI commands via the `token` argument,
or the `CONSUL_HTTP_TOKEN` environment variable.

#### Agent Rules

Expand Down