Add AuthorizedUser middleware

pull/2132/head
Michael Desa 2017-10-18 12:35:40 -04:00
parent f0f5bc071b
commit 28fac10baa
2 changed files with 573 additions and 0 deletions

View File

@ -2,6 +2,7 @@ package server
import (
"context"
"fmt"
"net/http"
"github.com/influxdata/chronograf"
@ -45,3 +46,95 @@ func AuthorizedToken(auth oauth2.Authenticator, logger chronograf.Logger, next h
return
})
}
func AuthorizedUser(store chronograf.UsersStore, useAuth bool, role string, logger chronograf.Logger, next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !useAuth {
next(w, r)
return
}
log := logger.
WithField("component", "role_auth").
WithField("remote_addr", r.RemoteAddr).
WithField("method", r.Method).
WithField("url", r.URL)
ctx := r.Context()
username, err := getUsername(ctx)
if err != nil {
log.Error("Failed to retrieve username from context")
Error(w, http.StatusUnauthorized, fmt.Sprintf("User is not authorized"), logger)
return
}
provider, err := getProvider(ctx)
if err != nil {
log.Error("Failed to retrieve provider from context")
Error(w, http.StatusUnauthorized, fmt.Sprintf("User %s is not authorized", username), logger)
return
}
u, err := getUserBy(store, ctx, username, provider)
if err != nil {
log.Error("Error to retrieving user")
Error(w, http.StatusUnauthorized, fmt.Sprintf("User %s is not authorized", username), logger)
return
}
if u == nil {
log.Error("User not found")
Error(w, http.StatusNotFound, fmt.Sprintf("User with name %s and provider %s not found", username, provider), logger)
return
}
if hasPrivelege(u, role) {
next(w, r)
return
}
Error(w, http.StatusUnauthorized, fmt.Sprintf("User %s is not authorized", username), logger)
return
})
}
func hasPrivelege(u *chronograf.User, role string) bool {
if u == nil {
return false
}
switch role {
case ViewerRoleName:
for _, r := range u.Roles {
switch r.Name {
case ViewerRoleName, EditorRoleName, AdminRoleName:
return true
default:
return false
}
}
case EditorRoleName:
for _, r := range u.Roles {
switch r.Name {
case EditorRoleName, AdminRoleName:
return true
default:
return false
}
}
case AdminRoleName:
for _, r := range u.Roles {
switch r.Name {
case AdminRoleName:
return true
default:
return false
}
}
default:
return false
}
return false
}

View File

@ -7,7 +7,9 @@ import (
"net/http/httptest"
"testing"
"github.com/influxdata/chronograf"
clog "github.com/influxdata/chronograf/log"
"github.com/influxdata/chronograf/mocks"
"github.com/influxdata/chronograf/oauth2"
"github.com/influxdata/chronograf/server"
)
@ -94,3 +96,481 @@ func TestAuthorizedToken(t *testing.T) {
}
}
}
func TestAuthorizedUser(t *testing.T) {
type fields struct {
UsersStore chronograf.UsersStore
Logger chronograf.Logger
}
type args struct {
username string
provider string
useAuth bool
role string
}
tests := []struct {
name string
fields fields
args args
authorized bool
}{
{
name: "Not using auth",
fields: fields{
UsersStore: &mocks.UsersStore{},
Logger: clog.New(clog.DebugLevel),
},
args: args{
useAuth: false,
},
authorized: true,
},
{
name: "User with viewer role is viewer authorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.ViewerRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "viewer",
useAuth: true,
},
authorized: true,
},
{
name: "User with editor role is viewer authorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.EditorRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "viewer",
useAuth: true,
},
authorized: true,
},
{
name: "User with admin role is viewer authorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.AdminRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "viewer",
useAuth: true,
},
authorized: true,
},
{
name: "User with viewer role is editor unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.ViewerRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "editor",
useAuth: true,
},
authorized: false,
},
{
name: "User with editor role is editor authorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.EditorRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "editor",
useAuth: true,
},
authorized: true,
},
{
name: "User with admin role is editor authorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.AdminRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "editor",
useAuth: true,
},
authorized: true,
},
{
name: "User with viewer role is admin unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.ViewerRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "admin",
useAuth: true,
},
authorized: false,
},
{
name: "User with editor role is admin unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.EditorRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "admin",
useAuth: true,
},
authorized: false,
},
{
name: "User with admin role is admin authorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
server.AdminRole,
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "admin",
useAuth: true,
},
authorized: true,
},
{
name: "User with no role is viewer unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "view",
useAuth: true,
},
authorized: false,
},
{
name: "User with no role is editor unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "editor",
useAuth: true,
},
authorized: false,
},
{
name: "User with no role is admin unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "admin",
useAuth: true,
},
authorized: false,
},
{
name: "User with unknown role is viewer unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
{
Name: "sweet_role",
},
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "viewer",
useAuth: true,
},
authorized: false,
},
{
name: "User with unknown role is editor unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
{
Name: "sweet_role",
},
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "editor",
useAuth: true,
},
authorized: false,
},
{
name: "User with unknown role is admin unauthorized",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
ID: 1337,
Name: "billysteve",
Provider: "Google",
Scheme: "OAuth2",
Roles: []chronograf.Role{
{
Name: "sweet_role",
},
},
},
}, nil
},
},
Logger: clog.New(clog.DebugLevel),
},
args: args{
username: "billysteve",
provider: "Google",
role: "admin",
useAuth: true,
},
authorized: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var authorized bool
next := func(w http.ResponseWriter, r *http.Request) {
authorized = true
}
fn := server.AuthorizedUser(tt.fields.UsersStore, tt.args.useAuth, tt.args.role, tt.fields.Logger, next)
w := httptest.NewRecorder()
r := httptest.NewRequest(
"GET",
"http://any.url", // can be any valid URL as we are bypassing mux
nil,
)
r = r.WithContext(context.WithValue(r.Context(), oauth2.PrincipalKey, oauth2.Principal{
Subject: tt.args.username,
Issuer: tt.args.provider,
}))
fn(w, r)
if authorized != tt.authorized {
t.Errorf("%q. AuthorizedUser() = %v, expected %v", tt.name, tt.authorized, authorized)
}
})
}
}