Skip to content

Commit

Permalink
Get auth token from cookie
Browse files Browse the repository at this point in the history
```
$ curl --cookie "kbase_session=$KBASE_CI_TOKEN" http://localhost:8080/node/06cc2b59-b33c-460a-8332-151a509b6d20/acl
{
  "data": {
    "delete": [
      "057abd01-95db-4500-9541-8ce2a925c3e3"
    ],
    "owner": "057abd01-95db-4500-9541-8ce2a925c3e3",
    "public": {
      "delete": false,
      "read": false,
      "write": false
    },
    "read": [
      "057abd01-95db-4500-9541-8ce2a925c3e3"
    ],
    "write": [
      "057abd01-95db-4500-9541-8ce2a925c3e3"
    ]
  },
  "error": null,
  "status": 200
}
$ curl --cookie "kbase_sessionx=$KBASE_CI_TOKEN" http://localhost:8080/node/06cc2b59-b33c-460a-8332-151a509b6d20/acl
{
  "data": null,
  "error": [
    "User Unauthorized"
  ],
  "status": 401
}
```
  • Loading branch information
MrCreosote committed May 16, 2024
1 parent 44bf972 commit 60864d4
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 59 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,17 @@ This data structure is identical to Shock's error data structure.

# API

Requests are authenticated by including the header `Authorization: OAuth <kbase token>` in the
request.
## Authentication

Requests are authenticated by including the header `Authorization: OAuth <kbase token>` or
including a cookie with the value of `<kbase token>` in the request.

The names of cookies that the server will check are set in the deployment configuration file.

The header takes precedence, then each cookie in the list in the configuration file in order.

Note that for backwards compatibility, incorrect or invalid authentication headers respond with a
400 HTTP code. Invalid cookies respond with the appropriate 401 code.

## Root

Expand Down
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 0.1.4

* Added the `del` param when downloading the file from a node.
* The Blobstore will now look for auth tokens in cookies specified in the deployment configuration.

# 0.1.3

Expand Down
2 changes: 1 addition & 1 deletion app/blobstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

const (
name = "blobstore"
version = "0.1.3"
version = "0.1.4"
shockname = "Shock"
shockver = "0.9.6" // do not increment
deprecation = "The id and version fields are deprecated."
Expand Down
8 changes: 8 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const (
// KeyAuthAdminRoles is the configuration key where the value is comma-delimited auth server
// roles that denote that a user is a blobstore admin
KeyAuthAdminRoles = "kbase-auth-admin-roles"
// KeyAuthTokenCookies is the configuartion key where the value is comma-delimited cookie
// names where the serivce should look for authentication tokens.
KeyAuthTokenCookies = "kbase-auth-token-cookies"
// KeyDontTrustXIPHeaders is the configuration key where the value determines whether to
// distrust the X-Forwarded-For and X-Real-IP headers (true) or not (anything else).
KeyDontTrustXIPHeaders = "dont-trust-x-ip-headers"
Expand Down Expand Up @@ -82,6 +85,9 @@ type Config struct {
// AuthAdminRoles are the auth server roles that denote that a user is a blobstore admin.
// It is never nil but may be empty.
AuthAdminRoles *[]string
// AuthTokenCookies are the cookie names to check for auth tokens.
// It is never nil but may be empty.
AuthTokenCookies *[]string
// DontTrustXIPHeaders determines whether to distrust the X-Forwarded-For and X-Real-IP
// headers.
DontTrustXIPHeaders bool
Expand Down Expand Up @@ -112,6 +118,7 @@ func New(configFilePath string) (*Config, error) {
s3region, err := getString(err, configFilePath, sec, KeyS3Region, true)
authurl, err := getURL(err, configFilePath, sec, KeyAuthURL)
roles, err := getStringList(err, configFilePath, sec, KeyAuthAdminRoles)
cookies, err := getStringList(err, configFilePath, sec, KeyAuthTokenCookies)
xip, err := getString(err, configFilePath, sec, KeyDontTrustXIPHeaders, false)
if err != nil {
return nil, err
Expand All @@ -137,6 +144,7 @@ func New(configFilePath string) (*Config, error) {
S3Region: s3region,
AuthURL: authurl,
AuthAdminRoles: roles,
AuthTokenCookies: cookies,
DontTrustXIPHeaders: "true" == xip,
},
nil
Expand Down
5 changes: 5 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func (t *TestSuite) TestMinimalConfig() {
S3DisableSSLVerify: false,
AuthURL: u,
AuthAdminRoles: &[]string{},
AuthTokenCookies: &[]string{},
DontTrustXIPHeaders: false,
}
t.Equal(&expected, cfg, "incorrect config")
Expand All @@ -114,6 +115,7 @@ func (t *TestSuite) TestMinimalConfigWhitespaceFields() {
"s3-region = us-west-1 \t ",
"kbase-auth-url = https://kbase.us/authyauth",
"kbase-auth-admin-roles = \t ",
"kbase-auth-token-cookies = \t ",
"dont-trust-x-ip-headers = \t ",
)
cfg, err := New(filePath)
Expand All @@ -132,6 +134,7 @@ func (t *TestSuite) TestMinimalConfigWhitespaceFields() {
S3DisableSSLVerify: false,
AuthURL: u,
AuthAdminRoles: &[]string{},
AuthTokenCookies: &[]string{},
DontTrustXIPHeaders: false,
}
t.Equal(&expected, cfg, "incorrect config")
Expand All @@ -153,6 +156,7 @@ func (t *TestSuite) TestMaximalConfig() {
"s3-disable-ssl-verify= true ",
"kbase-auth-url = https://kbase.us/authyauth",
"kbase-auth-admin-roles = \t , foo , \tbar\t , , baz ,,",
"kbase-auth-token-cookies = \t , chokkiechip , \toreoinmilk\t , , earwaxNsnot ,,",
"dont-trust-x-ip-headers = true \t ",
)
cfg, err := New(filePath)
Expand All @@ -173,6 +177,7 @@ func (t *TestSuite) TestMaximalConfig() {
S3Region: "us-west-1",
AuthURL: u,
AuthAdminRoles: &[]string{"foo", "bar", "baz"},
AuthTokenCookies: &[]string{"chokkiechip", "oreoinmilk", "earwaxNsnot"},
DontTrustXIPHeaders: true,
}
t.Equal(&expected, cfg, "incorrect config")
Expand Down
3 changes: 3 additions & 0 deletions deploy.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ s3-region = us-west-1
kbase-auth-url = https://kbase.us/services/auth
# KBase auth server custom roles that denote the user is a blobstore admin. Comma delimited.
kbase-auth-admin-roles = KBASE_ADMIN, BLOBSTORE_ADMIN
# A list of comma separated cookie names to check for authentication tokens.
# The authentication header is checked first, then each cookie in the list in order.
# kbase-auth-token-cookies =

# If "true", make the server ignore the X-Forwarded-For and X-Real-IP headers. Otherwise
# (the default behavior), the logged IP address for a request, in order of precedence, is
Expand Down
3 changes: 3 additions & 0 deletions deployment/conf/deployment.cfg.templ
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ s3-disable-ssl-verify = {{ default .Env.s3_disable_ssl_verify "false" }}
kbase-auth-url = {{ default .Env.kbase_auth_url "https://ci.kbase.us/services/auth" }}
# KBase auth server custom roles that denote the user is a blobstore admin. Comma delimited.
kbase-auth-admin-roles = {{ default .Env.kbase_auth_admin_roles "KBASE_ADMIN, BLOBSTORE_ADMIN" }}
# A list of comma separated cookie names to check for authentication tokens.
# The authentication header is checked first, then each cookie in the list in order.
kbase-auth-token-cookies = {{ default .Env.kbase_auth_token_cookies "kbase_session, kbase_session_backup" }}

# If "true", make the server ignore the X-Forwarded-For and X-Real-IP headers. Otherwise
# (the default behavior), the logged IP address for a request, in order of precedence, is
Expand Down
43 changes: 7 additions & 36 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ version: "3.1"
# that is started up and polled
services:
kbase_blobstore:
image: kbase/blobstore:latest
build: .
ports:
- "8080:8080"
environment:
blobstore_host: 0.0.0.0:8080
mongo_host: localhost:27017
mongo_database: dc_blobstore_test
mongodb_host: mongo:27017
mongodb_database: dc_blobstore_test
kbase_auth_url: https://ci.kbase.us/services/auth
kbase_auth_admin_roles: KBASE_ADMIN,BLOBSTORE_ADMIN
s3_host: minio:9000
Expand All @@ -24,9 +24,7 @@ services:
command:
- "-multiline"
- "-wait"
- "tcp://ci-mongo:27017"
- "-wait"
- "tcp://mongoinit:8080"
- "tcp://mongo:27017"
- "-wait"
- "tcp://minio:9000"
- "-timeout"
Expand All @@ -36,37 +34,10 @@ services:
- "/kb/deployment/blobstore/blobstore"
- "--conf"
- "/kb/deployment/conf/deployment.cfg"
# If you needed to pass in context for template evaluation you would put something like
# these lines that tell dockerize to hit github for an INI style file for the context
# - "-env"
# - "https://raw.githubusercontent.com/kbase/mini_kb/master/deployment/conf/tauth2-minikb.yml"
# If the -env URL needs authentication you would use an -env-header directive that specified
# either the hard coded string for the header, or a path to a file that contains the header
# string ( used for working with docker secrets files)
# - "-env-header"
# - "AUTHORIZATION:authtokenvalue"
# or for a path to a secrets file:
# - "env-header"
# - "/run/secrets/authheader"
# If your server is using self-signed certs, or otherwise problematic for cert validation
# you can add the following flag:
# - "-validateCert=false"
depends_on: ["ci-mongo", "mongoinit", "minio"]

mongoinit:
image: kbase/db_initialize:latest
entrypoint:
- "/kb/deployment/bin/dockerize.sh"
- "-wait"
- "tcp://ci-mongo:27017"
- "-timeout"
- "120s"
depends_on: [ "ci-mongo" ]
depends_on: ["mongo", "minio"]

ci-mongo:
image: mongo:2
command:
- "--smallfiles"
mongo:
image: mongo:3.6
ports:
- "27017:27017"

Expand Down
20 changes: 18 additions & 2 deletions service/errortypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (
// special explanation in the error string.
type UnauthorizedCustomError string

// UnauthorizedCustomError creates a new UnauthorizedCustomError.
// NewUnauthorizedCustomError creates a new UnauthorizedCustomError.
func NewUnauthorizedCustomError(err string) *UnauthorizedCustomError {
e := UnauthorizedCustomError(err)
return &e
Expand All @@ -28,13 +28,29 @@ func (e *UnauthorizedCustomError) Error() string {
return string(*e)
}

// InvalidTokenCustomError denotes that an invalid token was submitted and special explanation
// is needed in the error string.
type InvalidTokenCustomError string

// NewInvalidTokenCustomError creates a new InvalidTokenCustomError.
func NewInvalidTokenCustomError(err string) *InvalidTokenCustomError {
e := InvalidTokenCustomError(err)
return &e
}

func (e *InvalidTokenCustomError) Error() string {
return string(*e)
}

func translateError(err error) (code int, errstr string) {
// not sure about this approach. Alternative is to add some state to every error that
// can be mapped to a code, and I'm not super thrilled about that either.
switch t := err.(type) {
case *auth.InvalidTokenError:
// Shock compatibility, should be 401
return http.StatusBadRequest, invalidAuthHeader
case *InvalidTokenCustomError:
return http.StatusUnauthorized, t.Error()
case *core.NoBlobError:
return http.StatusNotFound, "Node not found"
case *core.UnauthorizedError:
Expand All @@ -45,7 +61,7 @@ func translateError(err error) (code int, errstr string) {
return http.StatusBadRequest, "Users that are not node owners can only delete " +
"themselves from ACLs."
case *UnauthorizedCustomError:
return http.StatusUnauthorized, t.Error()
return http.StatusForbidden, t.Error()
case *auth.InvalidUserError:
// no equivalent shock error, it accepts any string as a username
return http.StatusBadRequest, t.Error()
Expand Down
Loading

0 comments on commit 60864d4

Please sign in to comment.