satellite/console: integrate sessions into satellite UI

This change integrates the session management database functionality
with the web application. Claim-based authentication has been removed
in favor of session token-based authentication.

Change-Id: I62a4f5354a3ed8ca80272814aad2448f901eab1b
This commit is contained in:
Jeremy Wharton 2022-06-05 17:41:38 -05:00 committed by Storj Robot
parent 8c1caea5db
commit 58c5d44f44
30 changed files with 746 additions and 704 deletions

View File

@ -12,4 +12,6 @@ import (
type Auth interface { type Auth interface {
// IsAuthenticated checks if request is performed with all needed authorization credentials. // IsAuthenticated checks if request is performed with all needed authorization credentials.
IsAuthenticated(ctx context.Context, r *http.Request, isCookieAuth, isKeyAuth bool) (context.Context, error) IsAuthenticated(ctx context.Context, r *http.Request, isCookieAuth, isKeyAuth bool) (context.Context, error)
// RemoveAuthCookie indicates to the client that the authentication cookie should be removed.
RemoveAuthCookie(w http.ResponseWriter)
} }

View File

@ -178,6 +178,9 @@ func (a *API) generateGo() ([]byte, error) {
p("ctx, err = h.auth.IsAuthenticated(ctx, r, true, false)") p("ctx, err = h.auth.IsAuthenticated(ctx, r, true, false)")
} }
p("if err != nil {") p("if err != nil {")
if !endpoint.NoCookieAuth {
p("h.auth.RemoveAuthCookie(w)")
}
p("api.ServeError(h.log, w, http.StatusUnauthorized, err)") p("api.ServeError(h.log, w, http.StatusUnauthorized, err)")
p("return") p("return")
p("}") p("}")

View File

@ -10,9 +10,7 @@ import (
"path/filepath" "path/filepath"
"runtime/pprof" "runtime/pprof"
"strconv" "strconv"
"time"
"github.com/pquerna/otp/totp"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/zeebo/errs" "github.com/zeebo/errs"
"go.uber.org/zap" "go.uber.org/zap"
@ -40,7 +38,6 @@ import (
"storj.io/storj/satellite/audit" "storj.io/storj/satellite/audit"
"storj.io/storj/satellite/compensation" "storj.io/storj/satellite/compensation"
"storj.io/storj/satellite/console" "storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleauth"
"storj.io/storj/satellite/console/consoleweb" "storj.io/storj/satellite/console/consoleweb"
"storj.io/storj/satellite/contact" "storj.io/storj/satellite/contact"
"storj.io/storj/satellite/gc" "storj.io/storj/satellite/gc"
@ -237,11 +234,11 @@ func (system *Satellite) AddUser(ctx context.Context, newUser console.CreateUser
return nil, err return nil, err
} }
authCtx, err := system.AuthenticatedContext(ctx, user.ID) userCtx, err := system.UserContext(ctx, user.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = system.API.Console.Service.Payments().SetupAccount(authCtx) _, err = system.API.Console.Service.Payments().SetupAccount(userCtx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -253,11 +250,11 @@ func (system *Satellite) AddUser(ctx context.Context, newUser console.CreateUser
func (system *Satellite) AddProject(ctx context.Context, ownerID uuid.UUID, name string) (_ *console.Project, err error) { func (system *Satellite) AddProject(ctx context.Context, ownerID uuid.UUID, name string) (_ *console.Project, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
authCtx, err := system.AuthenticatedContext(ctx, ownerID) ctx, err = system.UserContext(ctx, ownerID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
project, err := system.API.Console.Service.CreateProject(authCtx, console.ProjectInfo{ project, err := system.API.Console.Service.CreateProject(ctx, console.ProjectInfo{
Name: name, Name: name,
}) })
if err != nil { if err != nil {
@ -266,8 +263,8 @@ func (system *Satellite) AddProject(ctx context.Context, ownerID uuid.UUID, name
return project, nil return project, nil
} }
// AuthenticatedContext creates context with authentication date for given user. // UserContext creates context with user.
func (system *Satellite) AuthenticatedContext(ctx context.Context, userID uuid.UUID) (_ context.Context, err error) { func (system *Satellite) UserContext(ctx context.Context, userID uuid.UUID) (_ context.Context, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
user, err := system.API.Console.Service.GetUser(ctx, userID) user, err := system.API.Console.Service.GetUser(ctx, userID)
@ -275,26 +272,7 @@ func (system *Satellite) AuthenticatedContext(ctx context.Context, userID uuid.U
return nil, err return nil, err
} }
// we are using full name as a password return console.WithUser(ctx, user), nil
request := console.AuthUser{Email: user.Email, Password: user.FullName}
if user.MFAEnabled {
code, err := totp.GenerateCode(user.MFASecretKey, time.Now())
if err != nil {
return nil, err
}
request.MFAPasscode = code
}
token, err := system.API.Console.Service.Token(ctx, request)
if err != nil {
return nil, err
}
auth, err := system.API.Console.Service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token)))
if err != nil {
return nil, err
}
return console.WithAuth(ctx, auth), nil
} }
// Close closes all the subsystems in the Satellite system. // Close closes all the subsystems in the Satellite system.

View File

@ -147,11 +147,11 @@ func (planet *Planet) newUplink(ctx context.Context, name string) (_ *Uplink, er
return nil, err return nil, err
} }
authCtx, err := satellite.AuthenticatedContext(ctx, user.ID) userCtx, err := satellite.UserContext(ctx, user.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, apiKey, err := consoleAPI.Service.CreateAPIKey(authCtx, project.ID, "root") _, apiKey, err := consoleAPI.Service.CreateAPIKey(userCtx, project.ID, "root")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,57 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package console
import (
"context"
"github.com/zeebo/errs"
"storj.io/storj/satellite/console/consoleauth"
)
// TODO: change to JWT or Macaroon based auth
// key is a context value key type.
type key int
// authKey is context key for Authorization.
const authKey key = 0
// requestKey is context key for Requests.
const requestKey key = 1
// ErrUnauthorized is error class for authorization related errors.
var ErrUnauthorized = errs.Class("unauthorized")
// Authorization contains auth info of authorized User.
type Authorization struct {
User User
Claims consoleauth.Claims
}
// WithAuth creates new context with Authorization.
func WithAuth(ctx context.Context, auth Authorization) context.Context {
return context.WithValue(ctx, authKey, auth)
}
// WithAuthFailure creates new context with authorization failure.
func WithAuthFailure(ctx context.Context, err error) context.Context {
return context.WithValue(ctx, authKey, err)
}
// GetAuth gets Authorization from context.
func GetAuth(ctx context.Context) (Authorization, error) {
value := ctx.Value(authKey)
if auth, ok := value.(Authorization); ok {
return auth, nil
}
if err, ok := value.(error); ok {
return Authorization{}, ErrUnauthorized.Wrap(err)
}
return Authorization{}, ErrUnauthorized.New(unauthorizedErrMsg)
}

View File

@ -5,6 +5,7 @@ package consoleauth
import ( import (
"context" "context"
"crypto/subtle"
"encoding/base64" "encoding/base64"
"time" "time"
@ -17,7 +18,7 @@ var mon = monkit.Package()
// Config contains configuration parameters for console auth. // Config contains configuration parameters for console auth.
type Config struct { type Config struct {
TokenExpirationTime time.Duration `help:"expiration time for auth tokens, account recovery tokens, and activation tokens" default:"24h"` TokenExpirationTime time.Duration `help:"expiration time for account recovery and activation tokens" default:"24h"`
} }
// Service handles creating, signing, and checking the expiration of auth tokens. // Service handles creating, signing, and checking the expiration of auth tokens.
@ -63,25 +64,35 @@ func (s *Service) createToken(ctx context.Context, claims *Claims) (_ string, er
} }
token := Token{Payload: json} token := Token{Payload: json}
err = s.SignToken(&token) signature, err := s.SignToken(token)
if err != nil { if err != nil {
return "", err return "", err
} }
token.Signature = signature
return token.String(), nil return token.String(), nil
} }
// SignToken signs token. // SignToken returns token signature.
func (s *Service) SignToken(token *Token) error { func (s *Service) SignToken(token Token) ([]byte, error) {
encoded := base64.URLEncoding.EncodeToString(token.Payload) encoded := base64.URLEncoding.EncodeToString(token.Payload)
signature, err := s.Signer.Sign([]byte(encoded)) signature, err := s.Signer.Sign([]byte(encoded))
if err != nil { if err != nil {
return err return nil, err
} }
token.Signature = signature return signature, nil
return nil }
// ValidateToken determines token validity using its signature.
func (s *Service) ValidateToken(token Token) (bool, error) {
signature, err := s.SignToken(token)
if err != nil {
return false, err
}
return subtle.ConstantTimeCompare(signature, token.Signature) == 1, nil
} }
// IsExpired returns whether token is expired. // IsExpired returns whether token is expired.

View File

@ -59,15 +59,15 @@ func (a *Analytics) EventTriggered(w http.ResponseWriter, r *http.Request) {
a.serveJSONError(w, http.StatusInternalServerError, err) a.serveJSONError(w, http.StatusInternalServerError, err)
} }
auth, err := console.GetAuth(ctx) user, err := console.GetUser(ctx)
if err != nil { if err != nil {
a.serveJSONError(w, http.StatusUnauthorized, err) a.serveJSONError(w, http.StatusUnauthorized, err)
return return
} }
if et.Link != "" { if et.Link != "" {
a.analytics.TrackLinkEvent(et.EventName, auth.User.ID, auth.User.Email, et.Link) a.analytics.TrackLinkEvent(et.EventName, user.ID, user.Email, et.Link)
} else { } else {
a.analytics.TrackEvent(et.EventName, auth.User.ID, auth.User.Email) a.analytics.TrackEvent(et.EventName, user.ID, user.Email)
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
@ -88,13 +88,13 @@ func (a *Analytics) PageEventTriggered(w http.ResponseWriter, r *http.Request) {
a.serveJSONError(w, http.StatusInternalServerError, err) a.serveJSONError(w, http.StatusInternalServerError, err)
} }
auth, err := console.GetAuth(ctx) user, err := console.GetUser(ctx)
if err != nil { if err != nil {
a.serveJSONError(w, http.StatusUnauthorized, err) a.serveJSONError(w, http.StatusUnauthorized, err)
return return
} }
a.analytics.PageVisitEvent(pv.PageName, auth.User.ID, auth.User.Email) a.analytics.PageVisitEvent(pv.PageName, user.ID, user.Email)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }

View File

@ -165,6 +165,7 @@ func (h *ProjectManagementHandler) handleGenGetBucketUsageRollups(w http.Respons
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true) ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil { if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err) api.ServeError(h.log, w, http.StatusUnauthorized, err)
return return
} }
@ -208,6 +209,7 @@ func (h *ProjectManagementHandler) handleGenCreateProject(w http.ResponseWriter,
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true) ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil { if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err) api.ServeError(h.log, w, http.StatusUnauthorized, err)
return return
} }
@ -239,6 +241,7 @@ func (h *ProjectManagementHandler) handleGenUpdateProject(w http.ResponseWriter,
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true) ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil { if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err) api.ServeError(h.log, w, http.StatusUnauthorized, err)
return return
} }
@ -282,6 +285,7 @@ func (h *ProjectManagementHandler) handleGenDeleteProject(w http.ResponseWriter,
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true) ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil { if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err) api.ServeError(h.log, w, http.StatusUnauthorized, err)
return return
} }
@ -313,6 +317,7 @@ func (h *ProjectManagementHandler) handleGenGetUsersProjects(w http.ResponseWrit
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true) ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil { if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err) api.ServeError(h.log, w, http.StatusUnauthorized, err)
return return
} }
@ -338,6 +343,7 @@ func (h *APIKeyManagementHandler) handleGenCreateAPIKey(w http.ResponseWriter, r
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true) ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil { if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err) api.ServeError(h.log, w, http.StatusUnauthorized, err)
return return
} }
@ -369,6 +375,7 @@ func (h *UserManagementHandler) handleGenGetUser(w http.ResponseWriter, r *http.
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true) ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil { if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err) api.ServeError(h.log, w, http.StatusUnauthorized, err)
return return
} }

View File

@ -70,7 +70,7 @@ func Test_DeleteAPIKeyByNameAndProjectID(t *testing.T) {
cookie := http.Cookie{ cookie := http.Cookie{
Name: "_tokenKey", Name: "_tokenKey",
Path: "/", Path: "/",
Value: token, Value: token.String(),
Expires: expire, Expires: expire,
} }

View File

@ -90,6 +90,13 @@ func (a *Auth) Token(w http.ResponseWriter, r *http.Request) {
return return
} }
tokenRequest.UserAgent = r.UserAgent()
tokenRequest.IP, err = web.GetRequestIP(r)
if err != nil {
a.serveJSONError(w, err)
return
}
token, err := a.service.Token(ctx, tokenRequest) token, err := a.service.Token(ctx, tokenRequest)
if err != nil { if err != nil {
if console.ErrMFAMissing.Has(err) { if console.ErrMFAMissing.Has(err) {
@ -104,7 +111,7 @@ func (a *Auth) Token(w http.ResponseWriter, r *http.Request) {
a.cookieAuth.SetTokenCookie(w, token) a.cookieAuth.SetTokenCookie(w, token)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(token) err = json.NewEncoder(w).Encode(token.String())
if err != nil { if err != nil {
a.log.Error("token handler could not encode token response", zap.Error(ErrAuthAPI.Wrap(err))) a.log.Error("token handler could not encode token response", zap.Error(ErrAuthAPI.Wrap(err)))
return return
@ -116,9 +123,21 @@ func (a *Auth) Logout(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
defer mon.Task()(&ctx)(nil) defer mon.Task()(&ctx)(nil)
a.cookieAuth.RemoveTokenCookie(w)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
token, err := a.cookieAuth.GetToken(r)
if err != nil {
a.serveJSONError(w, err)
return
}
err = a.service.DeleteSessionByToken(ctx, token)
if err != nil {
a.serveJSONError(w, err)
return
}
a.cookieAuth.RemoveTokenCookie(w)
} }
// replaceURLCharacters replaces slash, colon, and dot characters in a string with a hyphen. // replaceURLCharacters replaces slash, colon, and dot characters in a string with a hyphen.
@ -401,27 +420,27 @@ func (a *Auth) GetAccount(w http.ResponseWriter, r *http.Request) {
MFARecoveryCodeCount int `json:"mfaRecoveryCodeCount"` MFARecoveryCodeCount int `json:"mfaRecoveryCodeCount"`
} }
auth, err := console.GetAuth(ctx) consoleUser, err := console.GetUser(ctx)
if err != nil { if err != nil {
a.serveJSONError(w, err) a.serveJSONError(w, err)
return return
} }
user.ShortName = auth.User.ShortName user.ShortName = consoleUser.ShortName
user.FullName = auth.User.FullName user.FullName = consoleUser.FullName
user.Email = auth.User.Email user.Email = consoleUser.Email
user.ID = auth.User.ID user.ID = consoleUser.ID
user.PartnerID = auth.User.PartnerID user.PartnerID = consoleUser.PartnerID
user.UserAgent = auth.User.UserAgent user.UserAgent = consoleUser.UserAgent
user.ProjectLimit = auth.User.ProjectLimit user.ProjectLimit = consoleUser.ProjectLimit
user.IsProfessional = auth.User.IsProfessional user.IsProfessional = consoleUser.IsProfessional
user.CompanyName = auth.User.CompanyName user.CompanyName = consoleUser.CompanyName
user.Position = auth.User.Position user.Position = consoleUser.Position
user.EmployeeCount = auth.User.EmployeeCount user.EmployeeCount = consoleUser.EmployeeCount
user.HaveSalesContact = auth.User.HaveSalesContact user.HaveSalesContact = consoleUser.HaveSalesContact
user.PaidTier = auth.User.PaidTier user.PaidTier = consoleUser.PaidTier
user.MFAEnabled = auth.User.MFAEnabled user.MFAEnabled = consoleUser.MFAEnabled
user.MFARecoveryCodeCount = len(auth.User.MFARecoveryCodes) user.MFARecoveryCodeCount = len(consoleUser.MFARecoveryCodes)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(&user) err = json.NewEncoder(w).Encode(&user)

View File

@ -374,7 +374,7 @@ func TestMFAEndpoints(t *testing.T) {
req.AddCookie(&http.Cookie{ req.AddCookie(&http.Cookie{
Name: "_tokenKey", Name: "_tokenKey",
Path: "/", Path: "/",
Value: token, Value: token.String(),
Expires: time.Now().AddDate(0, 0, 1), Expires: time.Now().AddDate(0, 0, 1),
}) })
@ -599,21 +599,19 @@ func TestResetPasswordEndpoint(t *testing.T) {
token = getNewResetToken() token = getNewResetToken()
// Enable MFA. // Enable MFA.
getNewAuthContext := func() context.Context { userCtx, err := sat.UserContext(ctx, user.ID)
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
return authCtx
}
authCtx := getNewAuthContext()
key, err := service.ResetMFASecretKey(authCtx) key, err := service.ResetMFASecretKey(userCtx)
require.NoError(t, err)
userCtx, err = sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
authCtx = getNewAuthContext()
passcode, err := console.NewMFAPasscode(key, token.CreatedAt) passcode, err := console.NewMFAPasscode(key, token.CreatedAt)
require.NoError(t, err) require.NoError(t, err)
err = service.EnableUserMFA(authCtx, passcode, token.CreatedAt) err = service.EnableUserMFA(userCtx, passcode, token.CreatedAt)
require.NoError(t, err) require.NoError(t, err)
status, mfaError = tryPasswordReset(token.Secret.String(), newPass, "", "") status, mfaError = tryPasswordReset(token.Secret.String(), newPass, "", "")

View File

@ -76,7 +76,7 @@ func Test_AllBucketNames(t *testing.T) {
cookie := http.Cookie{ cookie := http.Cookie{
Name: "_tokenKey", Name: "_tokenKey",
Path: "/", Path: "/",
Value: token, Value: token.String(),
Expires: expire, Expires: expire,
} }

View File

@ -89,7 +89,7 @@ func Test_TotalUsageLimits(t *testing.T) {
cookie := http.Cookie{ cookie := http.Cookie{
Name: "_tokenKey", Name: "_tokenKey",
Path: "/", Path: "/",
Value: token, Value: token.String(),
Expires: expire, Expires: expire,
} }
@ -203,7 +203,7 @@ func Test_DailyUsage(t *testing.T) {
cookie := http.Cookie{ cookie := http.Cookie{
Name: "_tokenKey", Name: "_tokenKey",
Path: "/", Path: "/",
Value: token, Value: token.String(),
Expires: expire, Expires: expire,
} }

View File

@ -113,6 +113,7 @@ func TestGraphqlMutation(t *testing.T) {
console.Config{ console.Config{
PasswordCost: console.TestPasswordCost, PasswordCost: console.TestPasswordCost,
DefaultProjectLimit: 5, DefaultProjectLimit: 5,
SessionDuration: time.Hour,
}, },
) )
require.NoError(t, err) require.NoError(t, err)
@ -164,15 +165,13 @@ func TestGraphqlMutation(t *testing.T) {
token, err := service.Token(ctx, console.AuthUser{Email: createUser.Email, Password: createUser.Password}) token, err := service.Token(ctx, console.AuthUser{Email: createUser.Email, Password: createUser.Password})
require.NoError(t, err) require.NoError(t, err)
sauth, err := service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token))) userCtx, err := service.TokenAuth(ctx, token, time.Now())
require.NoError(t, err) require.NoError(t, err)
authCtx := console.WithAuth(ctx, sauth)
testQuery := func(t *testing.T, query string) (interface{}, error) { testQuery := func(t *testing.T, query string) (interface{}, error) {
result := graphql.Do(graphql.Params{ result := graphql.Do(graphql.Params{
Schema: schema, Schema: schema,
Context: authCtx, Context: userCtx,
RequestString: query, RequestString: query,
RootObject: rootObject, RootObject: rootObject,
}) })
@ -190,11 +189,9 @@ func TestGraphqlMutation(t *testing.T) {
token, err = service.Token(ctx, console.AuthUser{Email: rootUser.Email, Password: createUser.Password}) token, err = service.Token(ctx, console.AuthUser{Email: rootUser.Email, Password: createUser.Password})
require.NoError(t, err) require.NoError(t, err)
sauth, err = service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token))) userCtx, err = service.TokenAuth(ctx, token, time.Now())
require.NoError(t, err) require.NoError(t, err)
authCtx = console.WithAuth(ctx, sauth)
var projectIDField string var projectIDField string
t.Run("Create project mutation", func(t *testing.T) { t.Run("Create project mutation", func(t *testing.T) {
projectInfo := console.ProjectInfo{ projectInfo := console.ProjectInfo{
@ -223,14 +220,14 @@ func TestGraphqlMutation(t *testing.T) {
projectID, err := uuid.FromString(projectIDField) projectID, err := uuid.FromString(projectIDField)
require.NoError(t, err) require.NoError(t, err)
project, err := service.GetProject(authCtx, projectID) project, err := service.GetProject(userCtx, projectID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, rootUser.PartnerID, project.PartnerID) require.Equal(t, rootUser.PartnerID, project.PartnerID)
regTokenUser1, err := service.CreateRegToken(ctx, 1) regTokenUser1, err := service.CreateRegToken(ctx, 1)
require.NoError(t, err) require.NoError(t, err)
user1, err := service.CreateUser(authCtx, console.CreateUser{ user1, err := service.CreateUser(userCtx, console.CreateUser{
FullName: "User1", FullName: "User1",
Email: "u1@mail.test", Email: "u1@mail.test",
Password: "123a123", Password: "123a123",
@ -254,7 +251,7 @@ func TestGraphqlMutation(t *testing.T) {
regTokenUser2, err := service.CreateRegToken(ctx, 1) regTokenUser2, err := service.CreateRegToken(ctx, 1)
require.NoError(t, err) require.NoError(t, err)
user2, err := service.CreateUser(authCtx, console.CreateUser{ user2, err := service.CreateUser(userCtx, console.CreateUser{
FullName: "User1", FullName: "User1",
Email: "u2@mail.test", Email: "u2@mail.test",
Password: "123a123", Password: "123a123",
@ -353,7 +350,7 @@ func TestGraphqlMutation(t *testing.T) {
id, err := uuid.FromString(keyID) id, err := uuid.FromString(keyID)
require.NoError(t, err) require.NoError(t, err)
info, err := service.GetAPIKeyInfo(authCtx, id) info, err := service.GetAPIKeyInfo(userCtx, id)
require.NoError(t, err) require.NoError(t, err)
query := fmt.Sprintf( query := fmt.Sprintf(

View File

@ -135,7 +135,7 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
project, _ := p.Source.(*console.Project) project, _ := p.Source.(*console.Project)
_, err := console.GetAuth(p.Context) _, err := console.GetUser(p.Context)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -183,11 +183,6 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
project, _ := p.Source.(*console.Project) project, _ := p.Source.(*console.Project)
_, err := console.GetAuth(p.Context)
if err != nil {
return nil, err
}
cursor := cursorArgsToAPIKeysCursor(p.Args[CursorArg].(map[string]interface{})) cursor := cursorArgsToAPIKeysCursor(p.Args[CursorArg].(map[string]interface{}))
page, err := service.GetAPIKeys(p.Context, project.ID, cursor) page, err := service.GetAPIKeys(p.Context, project.ID, cursor)
if err != nil { if err != nil {

View File

@ -97,6 +97,7 @@ func TestGraphqlQuery(t *testing.T) {
console.Config{ console.Config{
PasswordCost: console.TestPasswordCost, PasswordCost: console.TestPasswordCost,
DefaultProjectLimit: 5, DefaultProjectLimit: 5,
SessionDuration: time.Hour,
}, },
) )
require.NoError(t, err) require.NoError(t, err)
@ -158,15 +159,13 @@ func TestGraphqlQuery(t *testing.T) {
token, err := service.Token(ctx, console.AuthUser{Email: createUser.Email, Password: createUser.Password}) token, err := service.Token(ctx, console.AuthUser{Email: createUser.Email, Password: createUser.Password})
require.NoError(t, err) require.NoError(t, err)
sauth, err := service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token))) userCtx, err := service.TokenAuth(ctx, token, time.Now())
require.NoError(t, err) require.NoError(t, err)
authCtx := console.WithAuth(ctx, sauth)
testQuery := func(t *testing.T, query string) interface{} { testQuery := func(t *testing.T, query string) interface{} {
result := graphql.Do(graphql.Params{ result := graphql.Do(graphql.Params{
Schema: schema, Schema: schema,
Context: authCtx, Context: userCtx,
RequestString: query, RequestString: query,
RootObject: rootObject, RootObject: rootObject,
}) })
@ -179,7 +178,7 @@ func TestGraphqlQuery(t *testing.T) {
return result.Data return result.Data
} }
createdProject, err := service.CreateProject(authCtx, console.ProjectInfo{ createdProject, err := service.CreateProject(userCtx, console.ProjectInfo{
Name: "TestProject", Name: "TestProject",
}) })
require.NoError(t, err) require.NoError(t, err)
@ -210,7 +209,7 @@ func TestGraphqlQuery(t *testing.T) {
regTokenUser1, err := service.CreateRegToken(ctx, 2) regTokenUser1, err := service.CreateRegToken(ctx, 2)
require.NoError(t, err) require.NoError(t, err)
user1, err := service.CreateUser(authCtx, console.CreateUser{ user1, err := service.CreateUser(userCtx, console.CreateUser{
FullName: "Mickey Last", FullName: "Mickey Last",
ShortName: "Last", ShortName: "Last",
Password: "123a123", Password: "123a123",
@ -233,7 +232,7 @@ func TestGraphqlQuery(t *testing.T) {
regTokenUser2, err := service.CreateRegToken(ctx, 2) regTokenUser2, err := service.CreateRegToken(ctx, 2)
require.NoError(t, err) require.NoError(t, err)
user2, err := service.CreateUser(authCtx, console.CreateUser{ user2, err := service.CreateUser(userCtx, console.CreateUser{
FullName: "Dubas Name", FullName: "Dubas Name",
ShortName: "Name", ShortName: "Name",
Email: "muu2@mail.test", Email: "muu2@mail.test",
@ -253,7 +252,7 @@ func TestGraphqlQuery(t *testing.T) {
user2.Email = "muu2@mail.test" user2.Email = "muu2@mail.test"
}) })
users, err := service.AddProjectMembers(authCtx, createdProject.ID, []string{ users, err := service.AddProjectMembers(userCtx, createdProject.ID, []string{
user1.Email, user1.Email,
user2.Email, user2.Email,
}) })
@ -316,10 +315,10 @@ func TestGraphqlQuery(t *testing.T) {
assert.True(t, foundU2) assert.True(t, foundU2)
}) })
keyInfo1, _, err := service.CreateAPIKey(authCtx, createdProject.ID, "key1") keyInfo1, _, err := service.CreateAPIKey(userCtx, createdProject.ID, "key1")
require.NoError(t, err) require.NoError(t, err)
keyInfo2, _, err := service.CreateAPIKey(authCtx, createdProject.ID, "key2") keyInfo2, _, err := service.CreateAPIKey(userCtx, createdProject.ID, "key2")
require.NoError(t, err) require.NoError(t, err)
t.Run("Project query api keys", func(t *testing.T) { t.Run("Project query api keys", func(t *testing.T) {
@ -372,7 +371,7 @@ func TestGraphqlQuery(t *testing.T) {
assert.True(t, foundKey2) assert.True(t, foundKey2)
}) })
project2, err := service.CreateProject(authCtx, console.ProjectInfo{ project2, err := service.CreateProject(userCtx, console.ProjectInfo{
Name: "Project2", Name: "Project2",
Description: "Test desc", Description: "Test desc",
}) })

View File

@ -6,6 +6,8 @@ package consolewebauth
import ( import (
"net/http" "net/http"
"time" "time"
"storj.io/storj/satellite/console/consoleauth"
) )
// CookieSettings variable cookie settings. // CookieSettings variable cookie settings.
@ -27,20 +29,25 @@ func NewCookieAuth(settings CookieSettings) *CookieAuth {
} }
// GetToken retrieves token from request. // GetToken retrieves token from request.
func (auth *CookieAuth) GetToken(r *http.Request) (string, error) { func (auth *CookieAuth) GetToken(r *http.Request) (consoleauth.Token, error) {
cookie, err := r.Cookie(auth.settings.Name) cookie, err := r.Cookie(auth.settings.Name)
if err != nil { if err != nil {
return "", err return consoleauth.Token{}, err
} }
return cookie.Value, nil token, err := consoleauth.FromBase64URLString(cookie.Value)
if err != nil {
return consoleauth.Token{}, err
}
return token, nil
} }
// SetTokenCookie sets parametrized token cookie that is not accessible from js. // SetTokenCookie sets parametrized token cookie that is not accessible from js.
func (auth *CookieAuth) SetTokenCookie(w http.ResponseWriter, token string) { func (auth *CookieAuth) SetTokenCookie(w http.ResponseWriter, token consoleauth.Token) {
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: auth.settings.Name, Name: auth.settings.Name,
Value: token, Value: token.String(),
Path: auth.settings.Path, Path: auth.settings.Path,
// TODO: get expiration from token // TODO: get expiration from token
Expires: time.Now().Add(time.Hour * 24), Expires: time.Now().Add(time.Hour * 24),
@ -60,3 +67,8 @@ func (auth *CookieAuth) RemoveTokenCookie(w http.ResponseWriter) {
SameSite: http.SameSiteStrictMode, SameSite: http.SameSiteStrictMode,
}) })
} }
// GetTokenCookieName returns the name of the cookie storing the session token.
func (auth *CookieAuth) GetTokenCookieName() string {
return auth.settings.Name
}

View File

@ -9,6 +9,7 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"net/url"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -69,10 +70,13 @@ func TestAuth(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode) require.Equal(t, http.StatusOK, resp.StatusCode)
} }
var oldCookies []*http.Cookie
{ // Get_AccountInfo { // Get_AccountInfo
resp, body := test.request(http.MethodGet, "/auth/account", nil) resp, body := test.request(http.MethodGet, "/auth/account", nil)
require.Equal(test.t, http.StatusOK, resp.StatusCode) require.Equal(test.t, http.StatusOK, resp.StatusCode)
require.Contains(test.t, body, "fullName") require.Contains(test.t, body, "fullName")
oldCookies = resp.Cookies()
var userIdentifier struct{ ID string } var userIdentifier struct{ ID string }
require.NoError(test.t, json.Unmarshal([]byte(body), &userIdentifier)) require.NoError(test.t, json.Unmarshal([]byte(body), &userIdentifier))
@ -95,6 +99,16 @@ func TestAuth(t *testing.T) {
require.Equal(test.t, http.StatusUnauthorized, resp.StatusCode) require.Equal(test.t, http.StatusUnauthorized, resp.StatusCode)
} }
{ // Get_AccountInfo shouldn't succeed with reused session cookie
satURL, err := url.Parse(test.url(""))
require.NoError(t, err)
test.client.Jar.SetCookies(satURL, oldCookies)
resp, body := test.request(http.MethodGet, "/auth/account", nil)
require.Contains(test.t, body, "error")
require.Equal(test.t, http.StatusUnauthorized, resp.StatusCode)
}
{ // repeated login attempts should end in too many requests { // repeated login attempts should end in too many requests
hitRateLimiter := false hitRateLimiter := false
for i := 0; i < 30; i++ { for i := 0; i < 30; i++ {
@ -821,7 +835,7 @@ func (test *test) defaultUser() registeredUser {
func (test *test) defaultProjectID() string { return test.planet.Uplinks[0].Projects[0].ID.String() } func (test *test) defaultProjectID() string { return test.planet.Uplinks[0].Projects[0].ID.String() }
func (test *test) login(email, password string) { func (test *test) login(email, password string) Response {
resp, body := test.request( resp, body := test.request(
http.MethodPost, "/auth/token", http.MethodPost, "/auth/token",
test.toJSON(map[string]string{ test.toJSON(map[string]string{
@ -835,6 +849,8 @@ func (test *test) login(email, password string) {
require.NoError(test.t, json.Unmarshal([]byte(body), &rawToken)) require.NoError(test.t, json.Unmarshal([]byte(body), &rawToken))
require.Equal(test.t, http.StatusOK, resp.StatusCode) require.Equal(test.t, http.StatusOK, resp.StatusCode)
require.Equal(test.t, rawToken, cookie.Value) require.Equal(test.t, rawToken, cookie.Value)
return resp
} }
func (test *test) registerUser(email, password string) registeredUser { func (test *test) registerUser(email, password string) registeredUser {

View File

@ -35,7 +35,6 @@ import (
"storj.io/storj/private/web" "storj.io/storj/private/web"
"storj.io/storj/satellite/analytics" "storj.io/storj/satellite/analytics"
"storj.io/storj/satellite/console" "storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleauth"
"storj.io/storj/satellite/console/consoleweb/consoleapi" "storj.io/storj/satellite/console/consoleweb/consoleapi"
"storj.io/storj/satellite/console/consoleweb/consoleql" "storj.io/storj/satellite/console/consoleweb/consoleql"
"storj.io/storj/satellite/console/consoleweb/consolewebauth" "storj.io/storj/satellite/console/consoleweb/consolewebauth"
@ -180,6 +179,62 @@ type templates struct {
usageReport *template.Template usageReport *template.Template
} }
// apiAuth exposes methods to control authentication process for each generated API endpoint.
type apiAuth struct {
server *Server
}
// IsAuthenticated checks if request is performed with all needed authorization credentials.
func (a *apiAuth) IsAuthenticated(ctx context.Context, r *http.Request, isCookieAuth, isKeyAuth bool) (_ context.Context, err error) {
if isCookieAuth && isKeyAuth {
ctx, err = a.cookieAuth(ctx, r)
if err != nil {
ctx, err = a.keyAuth(ctx, r)
if err != nil {
return nil, err
}
}
} else if isCookieAuth {
ctx, err = a.cookieAuth(ctx, r)
if err != nil {
return nil, err
}
} else if isKeyAuth {
ctx, err = a.keyAuth(ctx, r)
if err != nil {
return nil, err
}
}
return ctx, nil
}
// cookieAuth returns an authenticated context by session cookie.
func (a *apiAuth) cookieAuth(ctx context.Context, r *http.Request) (context.Context, error) {
token, err := a.server.cookieAuth.GetToken(r)
if err != nil {
return nil, err
}
return a.server.service.TokenAuth(ctx, token, time.Now())
}
// cookieAuth returns an authenticated context by api key.
func (a *apiAuth) keyAuth(ctx context.Context, r *http.Request) (context.Context, error) {
authToken := r.Header.Get("Authorization")
split := strings.Split(authToken, "Bearer ")
if len(split) != 2 {
return ctx, errs.New("authorization key format is incorrect. Should be 'Bearer <key>'")
}
return a.server.service.KeyAuth(ctx, split[1], time.Now())
}
// RemoveAuthCookie indicates to the client that the authentication cookie should be removed.
func (a *apiAuth) RemoveAuthCookie(w http.ResponseWriter) {
a.server.cookieAuth.RemoveTokenCookie(w)
}
// NewServer creates new instance of console server. // NewServer creates new instance of console server.
func NewServer(logger *zap.Logger, config Config, service *console.Service, oidcService *oidc.Service, mailService *mailservice.Service, partners *rewards.PartnersService, analytics *analytics.Service, listener net.Listener, stripePublicKey string, pricing paymentsconfig.PricingValues, nodeURL storj.NodeURL) *Server { func NewServer(logger *zap.Logger, config Config, service *console.Service, oidcService *oidc.Service, mailService *mailservice.Service, partners *rewards.PartnersService, analytics *analytics.Service, listener net.Listener, stripePublicKey string, pricing paymentsconfig.PricingValues, nodeURL storj.NodeURL) *Server {
server := Server{ server := Server{
@ -219,9 +274,9 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc
router := mux.NewRouter() router := mux.NewRouter()
if server.config.GeneratedAPIEnabled { if server.config.GeneratedAPIEnabled {
consoleapi.NewProjectManagement(logger, server.service, router, server.service) consoleapi.NewProjectManagement(logger, server.service, router, &apiAuth{&server})
consoleapi.NewAPIKeyManagement(logger, server.service, router, server.service) consoleapi.NewAPIKeyManagement(logger, server.service, router, &apiAuth{&server})
consoleapi.NewUserManagement(logger, server.service, router, server.service) consoleapi.NewUserManagement(logger, server.service, router, &apiAuth{&server})
} }
router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler) router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler)
@ -486,17 +541,15 @@ func (server *Server) withAuth(handler http.Handler) http.Handler {
ctxWithAuth := func(ctx context.Context) context.Context { ctxWithAuth := func(ctx context.Context) context.Context {
token, err := server.cookieAuth.GetToken(r) token, err := server.cookieAuth.GetToken(r)
if err != nil { if err != nil {
return console.WithAuthFailure(ctx, err) return console.WithUserFailure(ctx, console.ErrUnauthorized.Wrap(err))
} }
ctx = consoleauth.WithAPIKey(ctx, []byte(token)) newCtx, err := server.service.TokenAuth(ctx, token, time.Now())
auth, err := server.service.Authorize(ctx)
if err != nil { if err != nil {
return console.WithAuthFailure(ctx, err) return console.WithUserFailure(ctx, err)
} }
return console.WithAuth(ctx, auth) return newCtx
} }
ctx = ctxWithAuth(r.Context()) ctx = ctxWithAuth(r.Context())
@ -524,14 +577,12 @@ func (server *Server) bucketUsageReportHandler(w http.ResponseWriter, r *http.Re
return return
} }
auth, err := server.service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token))) ctx, err = server.service.TokenAuth(ctx, token, time.Now())
if err != nil { if err != nil {
server.serveError(w, http.StatusUnauthorized) server.serveError(w, http.StatusUnauthorized)
return return
} }
ctx = console.WithAuth(ctx, auth)
// parse query params // parse query params
projectID, err := uuid.FromString(r.URL.Query().Get("projectID")) projectID, err := uuid.FromString(r.URL.Query().Get("projectID"))
if err != nil { if err != nil {
@ -625,7 +676,7 @@ func (server *Server) accountActivationHandler(w http.ResponseWriter, r *http.Re
defer mon.Task()(&ctx)(nil) defer mon.Task()(&ctx)(nil)
activationToken := r.URL.Query().Get("token") activationToken := r.URL.Query().Get("token")
token, err := server.service.ActivateAccount(ctx, activationToken) user, err := server.service.ActivateAccount(ctx, activationToken)
if err != nil { if err != nil {
server.log.Error("activation: failed to activate account", server.log.Error("activation: failed to activate account",
zap.String("token", activationToken), zap.String("token", activationToken),
@ -645,6 +696,18 @@ func (server *Server) accountActivationHandler(w http.ResponseWriter, r *http.Re
return return
} }
ip, err := web.GetRequestIP(r)
if err != nil {
server.serveError(w, http.StatusInternalServerError)
return
}
token, err := server.service.GenerateSessionToken(ctx, user.ID, user.Email, ip, r.UserAgent())
if err != nil {
server.serveError(w, http.StatusInternalServerError)
return
}
server.cookieAuth.SetTokenCookie(w, token) server.cookieAuth.SetTokenCookie(w, token)
http.Redirect(w, r, server.config.ExternalAddress, http.StatusTemporaryRedirect) http.Redirect(w, r, server.config.ExternalAddress, http.StatusTemporaryRedirect)
@ -897,10 +960,10 @@ func (server *Server) parseTemplates() (_ *templates, err error) {
// NewUserIDRateLimiter constructs a RateLimiter that limits based on user ID. // NewUserIDRateLimiter constructs a RateLimiter that limits based on user ID.
func NewUserIDRateLimiter(config web.RateLimiterConfig) *web.RateLimiter { func NewUserIDRateLimiter(config web.RateLimiterConfig) *web.RateLimiter {
return web.NewRateLimiter(config, func(r *http.Request) (string, error) { return web.NewRateLimiter(config, func(r *http.Request) (string, error) {
auth, err := console.GetAuth(r.Context()) user, err := console.GetUser(r.Context())
if err != nil { if err != nil {
return "", err return "", err
} }
return auth.User.ID.String(), nil return user.ID.String(), nil
}) })
} }

View File

@ -124,17 +124,19 @@ func TestUserIDRateLimiter(t *testing.T) {
token, err := sat.API.Console.Service.Token(ctx, console.AuthUser{Email: user.Email, Password: user.FullName}) token, err := sat.API.Console.Service.Token(ctx, console.AuthUser{Email: user.Email, Password: user.FullName})
require.NoError(t, err) require.NoError(t, err)
tokenStr := token.String()
if userNum == 1 { if userNum == 1 {
firstToken = token firstToken = tokenStr
} }
// Expect burst number of successes. // Expect burst number of successes.
for burstNum := 0; burstNum < sat.Config.Console.RateLimit.Burst; burstNum++ { for burstNum := 0; burstNum < sat.Config.Console.RateLimit.Burst; burstNum++ {
require.NotEqual(t, http.StatusTooManyRequests, applyCouponStatus(token)) require.NotEqual(t, http.StatusTooManyRequests, applyCouponStatus(tokenStr))
} }
// Expect failure. // Expect failure.
require.Equal(t, http.StatusTooManyRequests, applyCouponStatus(token)) require.Equal(t, http.StatusTooManyRequests, applyCouponStatus(tokenStr))
}) })
} }

View File

@ -86,12 +86,12 @@ func NewMFASecretKey() (string, error) {
func (s *Service) EnableUserMFA(ctx context.Context, passcode string, t time.Time) (err error) { func (s *Service) EnableUserMFA(ctx context.Context, passcode string, t time.Time) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
auth, err := s.getAuthAndAuditLog(ctx, "enable MFA") user, err := s.getUserAndAuditLog(ctx, "enable MFA")
if err != nil { if err != nil {
return Error.Wrap(err) return Error.Wrap(err)
} }
valid, err := ValidateMFAPasscode(passcode, auth.User.MFASecretKey, t) valid, err := ValidateMFAPasscode(passcode, user.MFASecretKey, t)
if err != nil { if err != nil {
return ErrValidation.Wrap(ErrMFAPasscode.Wrap(err)) return ErrValidation.Wrap(ErrMFAPasscode.Wrap(err))
} }
@ -99,8 +99,8 @@ func (s *Service) EnableUserMFA(ctx context.Context, passcode string, t time.Tim
return ErrValidation.Wrap(ErrMFAPasscode.New(mfaPasscodeInvalidErrMsg)) return ErrValidation.Wrap(ErrMFAPasscode.New(mfaPasscodeInvalidErrMsg))
} }
auth.User.MFAEnabled = true user.MFAEnabled = true
err = s.store.Users().Update(ctx, &auth.User) err = s.store.Users().Update(ctx, user)
if err != nil { if err != nil {
return Error.Wrap(err) return Error.Wrap(err)
} }
@ -112,13 +112,11 @@ func (s *Service) EnableUserMFA(ctx context.Context, passcode string, t time.Tim
func (s *Service) DisableUserMFA(ctx context.Context, passcode string, t time.Time, recoveryCode string) (err error) { func (s *Service) DisableUserMFA(ctx context.Context, passcode string, t time.Time, recoveryCode string) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
auth, err := s.getAuthAndAuditLog(ctx, "disable MFA") user, err := s.getUserAndAuditLog(ctx, "disable MFA")
if err != nil { if err != nil {
return Error.Wrap(err) return Error.Wrap(err)
} }
user := &auth.User
if !user.MFAEnabled { if !user.MFAEnabled {
return nil return nil
} }
@ -139,7 +137,7 @@ func (s *Service) DisableUserMFA(ctx context.Context, passcode string, t time.Ti
return ErrUnauthorized.Wrap(ErrMFARecoveryCode.New(mfaRecoveryInvalidErrMsg)) return ErrUnauthorized.Wrap(ErrMFARecoveryCode.New(mfaRecoveryInvalidErrMsg))
} }
} else if passcode != "" { } else if passcode != "" {
valid, err := ValidateMFAPasscode(passcode, auth.User.MFASecretKey, t) valid, err := ValidateMFAPasscode(passcode, user.MFASecretKey, t)
if err != nil { if err != nil {
return ErrValidation.Wrap(ErrMFAPasscode.Wrap(err)) return ErrValidation.Wrap(ErrMFAPasscode.Wrap(err))
} }
@ -150,10 +148,10 @@ func (s *Service) DisableUserMFA(ctx context.Context, passcode string, t time.Ti
return ErrMFAMissing.New(mfaRequiredErrMsg) return ErrMFAMissing.New(mfaRequiredErrMsg)
} }
auth.User.MFAEnabled = false user.MFAEnabled = false
auth.User.MFASecretKey = "" user.MFASecretKey = ""
auth.User.MFARecoveryCodes = nil user.MFARecoveryCodes = nil
err = s.store.Users().Update(ctx, &auth.User) err = s.store.Users().Update(ctx, user)
if err != nil { if err != nil {
return Error.Wrap(err) return Error.Wrap(err)
} }
@ -185,7 +183,7 @@ func NewMFARecoveryCode() (string, error) {
func (s *Service) ResetMFASecretKey(ctx context.Context) (key string, err error) { func (s *Service) ResetMFASecretKey(ctx context.Context) (key string, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
auth, err := s.getAuthAndAuditLog(ctx, "reset MFA secret key") user, err := s.getUserAndAuditLog(ctx, "reset MFA secret key")
if err != nil { if err != nil {
return "", Error.Wrap(err) return "", Error.Wrap(err)
} }
@ -195,8 +193,8 @@ func (s *Service) ResetMFASecretKey(ctx context.Context) (key string, err error)
return "", Error.Wrap(err) return "", Error.Wrap(err)
} }
auth.User.MFASecretKey = key user.MFASecretKey = key
err = s.store.Users().Update(ctx, &auth.User) err = s.store.Users().Update(ctx, user)
if err != nil { if err != nil {
return "", Error.Wrap(err) return "", Error.Wrap(err)
} }
@ -208,12 +206,12 @@ func (s *Service) ResetMFASecretKey(ctx context.Context) (key string, err error)
func (s *Service) ResetMFARecoveryCodes(ctx context.Context) (codes []string, err error) { func (s *Service) ResetMFARecoveryCodes(ctx context.Context) (codes []string, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
auth, err := s.getAuthAndAuditLog(ctx, "reset MFA recovery codes") user, err := s.getUserAndAuditLog(ctx, "reset MFA recovery codes")
if err != nil { if err != nil {
return nil, Error.Wrap(err) return nil, Error.Wrap(err)
} }
if !auth.User.MFAEnabled { if !user.MFAEnabled {
return nil, ErrUnauthorized.New(mfaRecoveryGenerationErrMsg) return nil, ErrUnauthorized.New(mfaRecoveryGenerationErrMsg)
} }
@ -225,9 +223,9 @@ func (s *Service) ResetMFARecoveryCodes(ctx context.Context) (codes []string, er
} }
codes[i] = code codes[i] = code
} }
auth.User.MFARecoveryCodes = codes user.MFARecoveryCodes = codes
err = s.store.Users().Update(ctx, &auth.User) err = s.store.Users().Update(ctx, user)
if err != nil { if err != nil {
return nil, Error.Wrap(err) return nil, Error.Wrap(err)
} }

View File

@ -8,6 +8,9 @@ import (
"net/http" "net/http"
) )
// requestKey is context key for Requests.
const requestKey key = 1
// WithRequest creates new context with *http.Request. // WithRequest creates new context with *http.Request.
func WithRequest(ctx context.Context, req *http.Request) context.Context { func WithRequest(ctx context.Context, req *http.Request) context.Context {
return context.WithValue(ctx, requestKey, req) return context.WithValue(ctx, requestKey, req)

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ package console_test
import ( import (
"context" "context"
"database/sql"
"encoding/json" "encoding/json"
"math" "math"
"math/big" "math/big"
@ -24,7 +25,6 @@ import (
"storj.io/storj/private/testplanet" "storj.io/storj/private/testplanet"
"storj.io/storj/satellite" "storj.io/storj/satellite"
"storj.io/storj/satellite/console" "storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleauth"
) )
func TestService(t *testing.T) { func TestService(t *testing.T) {
@ -46,20 +46,20 @@ func TestService(t *testing.T) {
require.NotEqual(t, up1Pro1.ID, up2Pro1.ID) require.NotEqual(t, up1Pro1.ID, up2Pro1.ID)
require.NotEqual(t, up1Pro1.OwnerID, up2Pro1.OwnerID) require.NotEqual(t, up1Pro1.OwnerID, up2Pro1.OwnerID)
authCtx1, err := sat.AuthenticatedContext(ctx, up1Pro1.OwnerID) userCtx1, err := sat.UserContext(ctx, up1Pro1.OwnerID)
require.NoError(t, err) require.NoError(t, err)
authCtx2, err := sat.AuthenticatedContext(ctx, up2Pro1.OwnerID) userCtx2, err := sat.UserContext(ctx, up2Pro1.OwnerID)
require.NoError(t, err) require.NoError(t, err)
t.Run("TestGetProject", func(t *testing.T) { t.Run("TestGetProject", func(t *testing.T) {
// Getting own project details should work // Getting own project details should work
project, err := service.GetProject(authCtx1, up1Pro1.ID) project, err := service.GetProject(userCtx1, up1Pro1.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, up1Pro1.ID, project.ID) require.Equal(t, up1Pro1.ID, project.ID)
// Getting someone else project details should not work // Getting someone else project details should not work
project, err = service.GetProject(authCtx1, up2Pro1.ID) project, err = service.GetProject(userCtx1, up2Pro1.ID)
require.Error(t, err) require.Error(t, err)
require.Nil(t, project) require.Nil(t, project)
}) })
@ -75,17 +75,17 @@ func TestService(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.False(t, user.PaidTier) require.False(t, user.PaidTier)
// get context // get context
authCtx1, err := sat.AuthenticatedContext(ctx, user.ID) userCtx1, err := sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
// add a credit card to put the user in the paid tier // add a credit card to put the user in the paid tier
err = service.Payments().AddCreditCard(authCtx1, "test-cc-token") err = service.Payments().AddCreditCard(userCtx1, "test-cc-token")
require.NoError(t, err) require.NoError(t, err)
// update auth ctx // update auth ctx
authCtx1, err = sat.AuthenticatedContext(ctx, user.ID) userCtx1, err = sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
// Updating own project should work // Updating own project should work
updatedProject, err := service.UpdateProject(authCtx1, up1Pro1.ID, console.ProjectInfo{ updatedProject, err := service.UpdateProject(userCtx1, up1Pro1.ID, console.ProjectInfo{
Name: updatedName, Name: updatedName,
Description: updatedDescription, Description: updatedDescription,
StorageLimit: updatedStorageLimit, StorageLimit: updatedStorageLimit,
@ -102,7 +102,7 @@ func TestService(t *testing.T) {
require.Equal(t, updatedBandwidthLimit, *updatedProject.BandwidthLimit) require.Equal(t, updatedBandwidthLimit, *updatedProject.BandwidthLimit)
// Updating someone else project details should not work // Updating someone else project details should not work
updatedProject, err = service.UpdateProject(authCtx1, up2Pro1.ID, console.ProjectInfo{ updatedProject, err = service.UpdateProject(userCtx1, up2Pro1.ID, console.ProjectInfo{
Name: "newName", Name: "newName",
Description: "TestUpdate", Description: "TestUpdate",
StorageLimit: memory.Size(100), StorageLimit: memory.Size(100),
@ -127,7 +127,7 @@ func TestService(t *testing.T) {
StorageLimit: memory.Size(123), StorageLimit: memory.Size(123),
BandwidthLimit: memory.Size(123), BandwidthLimit: memory.Size(123),
} }
updatedProject, err = service.UpdateProject(authCtx1, up1Pro1.ID, updateInfo) updatedProject, err = service.UpdateProject(userCtx1, up1Pro1.ID, updateInfo)
require.Error(t, err) require.Error(t, err)
require.Nil(t, updatedProject) require.Nil(t, updatedProject)
@ -137,7 +137,7 @@ func TestService(t *testing.T) {
err = sat.DB.Console().Projects().Update(ctx, up1Pro1) err = sat.DB.Console().Projects().Update(ctx, up1Pro1)
require.NoError(t, err) require.NoError(t, err)
updatedProject, err = service.UpdateProject(authCtx1, up1Pro1.ID, updateInfo) updatedProject, err = service.UpdateProject(userCtx1, up1Pro1.ID, updateInfo)
require.Error(t, err) require.Error(t, err)
require.Nil(t, updatedProject) require.Nil(t, updatedProject)
@ -146,7 +146,7 @@ func TestService(t *testing.T) {
err = sat.DB.Console().Projects().Update(ctx, up1Pro1) err = sat.DB.Console().Projects().Update(ctx, up1Pro1)
require.NoError(t, err) require.NoError(t, err)
updatedProject, err = service.UpdateProject(authCtx1, up1Pro1.ID, updateInfo) updatedProject, err = service.UpdateProject(userCtx1, up1Pro1.ID, updateInfo)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, updateInfo.Name, updatedProject.Name) require.Equal(t, updateInfo.Name, updatedProject.Name)
require.Equal(t, updateInfo.Description, updatedProject.Description) require.Equal(t, updateInfo.Description, updatedProject.Description)
@ -155,7 +155,7 @@ func TestService(t *testing.T) {
require.Equal(t, updateInfo.StorageLimit, *updatedProject.StorageLimit) require.Equal(t, updateInfo.StorageLimit, *updatedProject.StorageLimit)
require.Equal(t, updateInfo.BandwidthLimit, *updatedProject.BandwidthLimit) require.Equal(t, updateInfo.BandwidthLimit, *updatedProject.BandwidthLimit)
project, err := service.GetProject(authCtx1, up1Pro1.ID) project, err := service.GetProject(userCtx1, up1Pro1.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, updateInfo.StorageLimit, *project.StorageLimit) require.Equal(t, updateInfo.StorageLimit, *project.StorageLimit)
require.Equal(t, updateInfo.BandwidthLimit, *project.BandwidthLimit) require.Equal(t, updateInfo.BandwidthLimit, *project.BandwidthLimit)
@ -163,69 +163,69 @@ func TestService(t *testing.T) {
t.Run("TestAddProjectMembers", func(t *testing.T) { t.Run("TestAddProjectMembers", func(t *testing.T) {
// Adding members to own project should work // Adding members to own project should work
addedUsers, err := service.AddProjectMembers(authCtx1, up1Pro1.ID, []string{up2User.Email}) addedUsers, err := service.AddProjectMembers(userCtx1, up1Pro1.ID, []string{up2User.Email})
require.NoError(t, err) require.NoError(t, err)
require.Len(t, addedUsers, 1) require.Len(t, addedUsers, 1)
require.Contains(t, addedUsers, up2User) require.Contains(t, addedUsers, up2User)
// Adding members to someone else project should not work // Adding members to someone else project should not work
addedUsers, err = service.AddProjectMembers(authCtx1, up2Pro1.ID, []string{up2User.Email}) addedUsers, err = service.AddProjectMembers(userCtx1, up2Pro1.ID, []string{up2User.Email})
require.Error(t, err) require.Error(t, err)
require.Nil(t, addedUsers) require.Nil(t, addedUsers)
}) })
t.Run("TestGetProjectMembers", func(t *testing.T) { t.Run("TestGetProjectMembers", func(t *testing.T) {
// Getting the project members of an own project that one is a part of should work // Getting the project members of an own project that one is a part of should work
userPage, err := service.GetProjectMembers(authCtx1, up1Pro1.ID, console.ProjectMembersCursor{Page: 1, Limit: 10}) userPage, err := service.GetProjectMembers(userCtx1, up1Pro1.ID, console.ProjectMembersCursor{Page: 1, Limit: 10})
require.NoError(t, err) require.NoError(t, err)
require.Len(t, userPage.ProjectMembers, 2) require.Len(t, userPage.ProjectMembers, 2)
// Getting the project members of a foreign project that one is a part of should work // Getting the project members of a foreign project that one is a part of should work
userPage, err = service.GetProjectMembers(authCtx2, up1Pro1.ID, console.ProjectMembersCursor{Page: 1, Limit: 10}) userPage, err = service.GetProjectMembers(userCtx2, up1Pro1.ID, console.ProjectMembersCursor{Page: 1, Limit: 10})
require.NoError(t, err) require.NoError(t, err)
require.Len(t, userPage.ProjectMembers, 2) require.Len(t, userPage.ProjectMembers, 2)
// Getting the project members of a foreign project that one is not a part of should not work // Getting the project members of a foreign project that one is not a part of should not work
userPage, err = service.GetProjectMembers(authCtx1, up2Pro1.ID, console.ProjectMembersCursor{Page: 1, Limit: 10}) userPage, err = service.GetProjectMembers(userCtx1, up2Pro1.ID, console.ProjectMembersCursor{Page: 1, Limit: 10})
require.Error(t, err) require.Error(t, err)
require.Nil(t, userPage) require.Nil(t, userPage)
}) })
t.Run("TestDeleteProjectMembers", func(t *testing.T) { t.Run("TestDeleteProjectMembers", func(t *testing.T) {
// Deleting project members of an own project should work // Deleting project members of an own project should work
err := service.DeleteProjectMembers(authCtx1, up1Pro1.ID, []string{up2User.Email}) err := service.DeleteProjectMembers(userCtx1, up1Pro1.ID, []string{up2User.Email})
require.NoError(t, err) require.NoError(t, err)
// Deleting Project members of someone else project should not work // Deleting Project members of someone else project should not work
err = service.DeleteProjectMembers(authCtx1, up2Pro1.ID, []string{up2User.Email}) err = service.DeleteProjectMembers(userCtx1, up2Pro1.ID, []string{up2User.Email})
require.Error(t, err) require.Error(t, err)
}) })
t.Run("TestDeleteProject", func(t *testing.T) { t.Run("TestDeleteProject", func(t *testing.T) {
// Deleting the own project should not work before deleting the API-Key // Deleting the own project should not work before deleting the API-Key
err := service.DeleteProject(authCtx1, up1Pro1.ID) err := service.DeleteProject(userCtx1, up1Pro1.ID)
require.Error(t, err) require.Error(t, err)
keys, err := service.GetAPIKeys(authCtx1, up1Pro1.ID, console.APIKeyCursor{Page: 1, Limit: 10}) keys, err := service.GetAPIKeys(userCtx1, up1Pro1.ID, console.APIKeyCursor{Page: 1, Limit: 10})
require.NoError(t, err) require.NoError(t, err)
require.Len(t, keys.APIKeys, 1) require.Len(t, keys.APIKeys, 1)
err = service.DeleteAPIKeys(authCtx1, []uuid.UUID{keys.APIKeys[0].ID}) err = service.DeleteAPIKeys(userCtx1, []uuid.UUID{keys.APIKeys[0].ID})
require.NoError(t, err) require.NoError(t, err)
// Deleting the own project should now work // Deleting the own project should now work
err = service.DeleteProject(authCtx1, up1Pro1.ID) err = service.DeleteProject(userCtx1, up1Pro1.ID)
require.NoError(t, err) require.NoError(t, err)
// Deleting someone else project should not work // Deleting someone else project should not work
err = service.DeleteProject(authCtx1, up2Pro1.ID) err = service.DeleteProject(userCtx1, up2Pro1.ID)
require.Error(t, err) require.Error(t, err)
err = planet.Uplinks[1].CreateBucket(ctx, sat, "testbucket") err = planet.Uplinks[1].CreateBucket(ctx, sat, "testbucket")
require.NoError(t, err) require.NoError(t, err)
// deleting a project with a bucket should fail // deleting a project with a bucket should fail
err = service.DeleteProject(authCtx2, up2Pro1.ID) err = service.DeleteProject(userCtx2, up2Pro1.ID)
require.Error(t, err) require.Error(t, err)
require.Equal(t, "console service: project usage: some buckets still exist", err.Error()) require.Equal(t, "console service: project usage: some buckets still exist", err.Error())
}) })
@ -233,14 +233,14 @@ func TestService(t *testing.T) {
t.Run("TestChangeEmail", func(t *testing.T) { t.Run("TestChangeEmail", func(t *testing.T) {
const newEmail = "newEmail@example.com" const newEmail = "newEmail@example.com"
err = service.ChangeEmail(authCtx2, newEmail) err = service.ChangeEmail(userCtx2, newEmail)
require.NoError(t, err) require.NoError(t, err)
user, _, err := service.GetUserByEmailWithUnverified(authCtx2, newEmail) user, _, err := service.GetUserByEmailWithUnverified(userCtx2, newEmail)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, newEmail, user.Email) require.Equal(t, newEmail, user.Email)
err = service.ChangeEmail(authCtx2, newEmail) err = service.ChangeEmail(userCtx2, newEmail)
require.Error(t, err) require.Error(t, err)
}) })
@ -257,19 +257,19 @@ func TestService(t *testing.T) {
ProjectID: up2Pro1.ID, ProjectID: up2Pro1.ID,
} }
_, err := sat.API.Buckets.Service.CreateBucket(authCtx2, bucket1) _, err := sat.API.Buckets.Service.CreateBucket(userCtx2, bucket1)
require.NoError(t, err) require.NoError(t, err)
_, err = sat.API.Buckets.Service.CreateBucket(authCtx2, bucket2) _, err = sat.API.Buckets.Service.CreateBucket(userCtx2, bucket2)
require.NoError(t, err) require.NoError(t, err)
bucketNames, err := service.GetAllBucketNames(authCtx2, up2Pro1.ID) bucketNames, err := service.GetAllBucketNames(userCtx2, up2Pro1.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, bucket1.Name, bucketNames[0]) require.Equal(t, bucket1.Name, bucketNames[0])
require.Equal(t, bucket2.Name, bucketNames[1]) require.Equal(t, bucket2.Name, bucketNames[1])
// Getting someone else buckets should not work // Getting someone else buckets should not work
bucketsForUnauthorizedUser, err := service.GetAllBucketNames(authCtx1, up2Pro1.ID) bucketsForUnauthorizedUser, err := service.GetAllBucketNames(userCtx1, up2Pro1.ID)
require.Error(t, err) require.Error(t, err)
require.Nil(t, bucketsForUnauthorizedUser) require.Nil(t, bucketsForUnauthorizedUser)
}) })
@ -295,10 +295,10 @@ func TestService(t *testing.T) {
require.NotNil(t, info) require.NotNil(t, info)
// Deleting someone else api keys should not work // Deleting someone else api keys should not work
err = service.DeleteAPIKeyByNameAndProjectID(authCtx1, apikey.Name, up2Pro1.ID) err = service.DeleteAPIKeyByNameAndProjectID(userCtx1, apikey.Name, up2Pro1.ID)
require.Error(t, err) require.Error(t, err)
err = service.DeleteAPIKeyByNameAndProjectID(authCtx2, apikey.Name, up2Pro1.ID) err = service.DeleteAPIKeyByNameAndProjectID(userCtx2, apikey.Name, up2Pro1.ID)
require.NoError(t, err) require.NoError(t, err)
info, err = sat.DB.Console().APIKeys().Get(ctx, createdKey.ID) info, err = sat.DB.Console().APIKeys().Get(ctx, createdKey.ID)
@ -351,11 +351,11 @@ func TestPaidTier(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.False(t, user.PaidTier) require.False(t, user.PaidTier)
authCtx, err := sat.AuthenticatedContext(ctx, user.ID) userCtx, err := sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
// add a credit card to the user // add a credit card to the user
err = service.Payments().AddCreditCard(authCtx, "test-cc-token") err = service.Payments().AddCreditCard(userCtx, "test-cc-token")
require.NoError(t, err) require.NoError(t, err)
// expect user to be in paid tier // expect user to be in paid tier
@ -365,18 +365,18 @@ func TestPaidTier(t *testing.T) {
require.Equal(t, usageConfig.Project.Paid, user.ProjectLimit) require.Equal(t, usageConfig.Project.Paid, user.ProjectLimit)
// update auth ctx // update auth ctx
authCtx, err = sat.AuthenticatedContext(ctx, user.ID) userCtx, err = sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
// expect project to be migrated to paid tier usage limits // expect project to be migrated to paid tier usage limits
proj1, err = service.GetProject(authCtx, proj1.ID) proj1, err = service.GetProject(userCtx, proj1.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, usageConfig.Storage.Paid, *proj1.StorageLimit) require.Equal(t, usageConfig.Storage.Paid, *proj1.StorageLimit)
require.Equal(t, usageConfig.Bandwidth.Paid, *proj1.BandwidthLimit) require.Equal(t, usageConfig.Bandwidth.Paid, *proj1.BandwidthLimit)
require.Equal(t, usageConfig.Segment.Paid, *proj1.SegmentLimit) require.Equal(t, usageConfig.Segment.Paid, *proj1.SegmentLimit)
// expect new project to be created with paid tier usage limits // expect new project to be created with paid tier usage limits
proj2, err := service.CreateProject(authCtx, console.ProjectInfo{Name: "Project 2"}) proj2, err := service.CreateProject(userCtx, console.ProjectInfo{Name: "Project 2"})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, usageConfig.Storage.Paid, *proj2.StorageLimit) require.Equal(t, usageConfig.Storage.Paid, *proj2.StorageLimit)
}) })
@ -395,22 +395,22 @@ func TestMFA(t *testing.T) {
}, 1) }, 1)
require.NoError(t, err) require.NoError(t, err)
getNewAuthorization := func() (context.Context, console.Authorization) { updateContext := func() (context.Context, *console.User) {
authCtx, err := sat.AuthenticatedContext(ctx, user.ID) userCtx, err := sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
auth, err := console.GetAuth(authCtx) user, err := console.GetUser(userCtx)
require.NoError(t, err) require.NoError(t, err)
return authCtx, auth return userCtx, user
} }
authCtx, auth := getNewAuthorization() userCtx, user := updateContext()
var key string var key string
t.Run("TestResetMFASecretKey", func(t *testing.T) { t.Run("TestResetMFASecretKey", func(t *testing.T) {
key, err = service.ResetMFASecretKey(authCtx) key, err = service.ResetMFASecretKey(userCtx)
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() _, user := updateContext()
require.NotEmpty(t, auth.User.MFASecretKey) require.NotEmpty(t, user.MFASecretKey)
}) })
t.Run("TestEnableUserMFABadPasscode", func(t *testing.T) { t.Run("TestEnableUserMFABadPasscode", func(t *testing.T) {
@ -418,15 +418,15 @@ func TestMFA(t *testing.T) {
badCode, err := console.NewMFAPasscode(key, time.Time{}.Add(time.Hour)) badCode, err := console.NewMFAPasscode(key, time.Time{}.Add(time.Hour))
require.NoError(t, err) require.NoError(t, err)
err = service.EnableUserMFA(authCtx, badCode, time.Time{}) err = service.EnableUserMFA(userCtx, badCode, time.Time{})
require.True(t, console.ErrValidation.Has(err)) require.True(t, console.ErrValidation.Has(err))
authCtx, auth = getNewAuthorization() userCtx, _ = updateContext()
_, err = service.ResetMFARecoveryCodes(authCtx) _, err = service.ResetMFARecoveryCodes(userCtx)
require.True(t, console.ErrUnauthorized.Has(err)) require.True(t, console.ErrUnauthorized.Has(err))
authCtx, auth = getNewAuthorization() _, user = updateContext()
require.False(t, auth.User.MFAEnabled) require.False(t, user.MFAEnabled)
}) })
t.Run("TestEnableUserMFAGoodPasscode", func(t *testing.T) { t.Run("TestEnableUserMFAGoodPasscode", func(t *testing.T) {
@ -434,13 +434,13 @@ func TestMFA(t *testing.T) {
goodCode, err := console.NewMFAPasscode(key, time.Time{}) goodCode, err := console.NewMFAPasscode(key, time.Time{})
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, _ = updateContext()
err = service.EnableUserMFA(authCtx, goodCode, time.Time{}) err = service.EnableUserMFA(userCtx, goodCode, time.Time{})
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() _, user = updateContext()
require.True(t, auth.User.MFAEnabled) require.True(t, user.MFAEnabled)
require.Equal(t, auth.User.MFASecretKey, key) require.Equal(t, user.MFASecretKey, key)
}) })
t.Run("TestMFAGetToken", func(t *testing.T) { t.Run("TestMFAGetToken", func(t *testing.T) {
@ -471,13 +471,13 @@ func TestMFA(t *testing.T) {
}) })
t.Run("TestMFARecoveryCodes", func(t *testing.T) { t.Run("TestMFARecoveryCodes", func(t *testing.T) {
_, err = service.ResetMFARecoveryCodes(authCtx) _, err = service.ResetMFARecoveryCodes(userCtx)
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() _, user = updateContext()
require.Len(t, auth.User.MFARecoveryCodes, console.MFARecoveryCodeCount) require.Len(t, user.MFARecoveryCodes, console.MFARecoveryCodeCount)
for _, code := range auth.User.MFARecoveryCodes { for _, code := range user.MFARecoveryCodes {
// Ensure code is of the form XXXX-XXXX-XXXX where X is A-Z or 0-9. // Ensure code is of the form XXXX-XXXX-XXXX where X is A-Z or 0-9.
require.Regexp(t, "^([A-Z0-9]{4})((-[A-Z0-9]{4})){2}$", code) require.Regexp(t, "^([A-Z0-9]{4})((-[A-Z0-9]{4})){2}$", code)
@ -492,10 +492,11 @@ func TestMFA(t *testing.T) {
require.True(t, console.ErrMFARecoveryCode.Has(err)) require.True(t, console.ErrMFARecoveryCode.Has(err))
require.Empty(t, token) require.Empty(t, token)
authCtx, auth = getNewAuthorization() _, user = updateContext()
} }
_, err = service.ResetMFARecoveryCodes(authCtx) userCtx, _ = updateContext()
_, err = service.ResetMFARecoveryCodes(userCtx)
require.NoError(t, err) require.NoError(t, err)
}) })
@ -504,14 +505,14 @@ func TestMFA(t *testing.T) {
badCode, err := console.NewMFAPasscode(key, time.Time{}.Add(time.Hour)) badCode, err := console.NewMFAPasscode(key, time.Time{}.Add(time.Hour))
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, _ = updateContext()
err = service.DisableUserMFA(authCtx, badCode, time.Time{}, "") err = service.DisableUserMFA(userCtx, badCode, time.Time{}, "")
require.True(t, console.ErrValidation.Has(err)) require.True(t, console.ErrValidation.Has(err))
authCtx, auth = getNewAuthorization() _, user = updateContext()
require.True(t, auth.User.MFAEnabled) require.True(t, user.MFAEnabled)
require.NotEmpty(t, auth.User.MFASecretKey) require.NotEmpty(t, user.MFASecretKey)
require.NotEmpty(t, auth.User.MFARecoveryCodes) require.NotEmpty(t, user.MFARecoveryCodes)
}) })
t.Run("TestDisableUserMFAConflict", func(t *testing.T) { t.Run("TestDisableUserMFAConflict", func(t *testing.T) {
@ -519,14 +520,14 @@ func TestMFA(t *testing.T) {
goodCode, err := console.NewMFAPasscode(key, time.Time{}) goodCode, err := console.NewMFAPasscode(key, time.Time{})
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, user = updateContext()
err = service.DisableUserMFA(authCtx, goodCode, time.Time{}, auth.User.MFARecoveryCodes[0]) err = service.DisableUserMFA(userCtx, goodCode, time.Time{}, user.MFARecoveryCodes[0])
require.True(t, console.ErrMFAConflict.Has(err)) require.True(t, console.ErrMFAConflict.Has(err))
authCtx, auth = getNewAuthorization() _, user = updateContext()
require.True(t, auth.User.MFAEnabled) require.True(t, user.MFAEnabled)
require.NotEmpty(t, auth.User.MFASecretKey) require.NotEmpty(t, user.MFASecretKey)
require.NotEmpty(t, auth.User.MFARecoveryCodes) require.NotEmpty(t, user.MFARecoveryCodes)
}) })
t.Run("TestDisableUserMFAGoodPasscode", func(t *testing.T) { t.Run("TestDisableUserMFAGoodPasscode", func(t *testing.T) {
@ -534,46 +535,46 @@ func TestMFA(t *testing.T) {
goodCode, err := console.NewMFAPasscode(key, time.Time{}) goodCode, err := console.NewMFAPasscode(key, time.Time{})
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, _ = updateContext()
err = service.DisableUserMFA(authCtx, goodCode, time.Time{}, "") err = service.DisableUserMFA(userCtx, goodCode, time.Time{}, "")
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, user = updateContext()
require.False(t, auth.User.MFAEnabled) require.False(t, user.MFAEnabled)
require.Empty(t, auth.User.MFASecretKey) require.Empty(t, user.MFASecretKey)
require.Empty(t, auth.User.MFARecoveryCodes) require.Empty(t, user.MFARecoveryCodes)
}) })
t.Run("TestDisableUserMFAGoodRecoveryCode", func(t *testing.T) { t.Run("TestDisableUserMFAGoodRecoveryCode", func(t *testing.T) {
// Expect MFA-disabling attempt to succeed when providing valid recovery code. // Expect MFA-disabling attempt to succeed when providing valid recovery code.
// Enable MFA // Enable MFA
key, err = service.ResetMFASecretKey(authCtx) key, err = service.ResetMFASecretKey(userCtx)
require.NoError(t, err) require.NoError(t, err)
goodCode, err := console.NewMFAPasscode(key, time.Time{}) goodCode, err := console.NewMFAPasscode(key, time.Time{})
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, _ = updateContext()
err = service.EnableUserMFA(authCtx, goodCode, time.Time{}) err = service.EnableUserMFA(userCtx, goodCode, time.Time{})
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, _ = updateContext()
_, err = service.ResetMFARecoveryCodes(authCtx) _, err = service.ResetMFARecoveryCodes(userCtx)
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() userCtx, user = updateContext()
require.True(t, auth.User.MFAEnabled) require.True(t, user.MFAEnabled)
require.NotEmpty(t, auth.User.MFASecretKey) require.NotEmpty(t, user.MFASecretKey)
require.NotEmpty(t, auth.User.MFARecoveryCodes) require.NotEmpty(t, user.MFARecoveryCodes)
// Disable MFA // Disable MFA
err = service.DisableUserMFA(authCtx, "", time.Time{}, auth.User.MFARecoveryCodes[0]) err = service.DisableUserMFA(userCtx, "", time.Time{}, user.MFARecoveryCodes[0])
require.NoError(t, err) require.NoError(t, err)
authCtx, auth = getNewAuthorization() _, user = updateContext()
require.False(t, auth.User.MFAEnabled) require.False(t, user.MFAEnabled)
require.Empty(t, auth.User.MFASecretKey) require.Empty(t, user.MFASecretKey)
require.Empty(t, auth.User.MFARecoveryCodes) require.Empty(t, user.MFARecoveryCodes)
}) })
}) })
} }
@ -620,23 +621,18 @@ func TestResetPassword(t *testing.T) {
token = getNewResetToken() token = getNewResetToken()
// Enable MFA. // Enable MFA.
getNewAuthorization := func() (context.Context, console.Authorization) { userCtx, err := sat.UserContext(ctx, user.ID)
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
auth, err := console.GetAuth(authCtx)
require.NoError(t, err)
return authCtx, auth
}
authCtx, _ := getNewAuthorization()
key, err := service.ResetMFASecretKey(authCtx) key, err := service.ResetMFASecretKey(userCtx)
require.NoError(t, err)
userCtx, err = sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
authCtx, auth := getNewAuthorization()
passcode, err := console.NewMFAPasscode(key, token.CreatedAt) passcode, err := console.NewMFAPasscode(key, token.CreatedAt)
require.NoError(t, err) require.NoError(t, err)
err = service.EnableUserMFA(authCtx, passcode, token.CreatedAt) err = service.EnableUserMFA(userCtx, passcode, token.CreatedAt)
require.NoError(t, err) require.NoError(t, err)
// Expect error when providing bad passcode. // Expect error when providing bad passcode.
@ -645,7 +641,7 @@ func TestResetPassword(t *testing.T) {
err = service.ResetPassword(ctx, token.Secret.String(), newPass, badPasscode, "", token.CreatedAt) err = service.ResetPassword(ctx, token.Secret.String(), newPass, badPasscode, "", token.CreatedAt)
require.True(t, console.ErrMFAPasscode.Has(err)) require.True(t, console.ErrMFAPasscode.Has(err))
for _, recoveryCode := range auth.User.MFARecoveryCodes { for _, recoveryCode := range user.MFARecoveryCodes {
// Expect success when providing bad passcode and good recovery code. // Expect success when providing bad passcode and good recovery code.
err = service.ResetPassword(ctx, token.Secret.String(), newPass, badPasscode, recoveryCode, token.CreatedAt) err = service.ResetPassword(ctx, token.Secret.String(), newPass, badPasscode, recoveryCode, token.CreatedAt)
require.NoError(t, err) require.NoError(t, err)
@ -662,40 +658,6 @@ func TestResetPassword(t *testing.T) {
}) })
} }
// TestActivateAccountToken ensures that the token returned after activating an account can be used to authorize user activity.
// i.e. a user does not need to acquire an authorization separate from the account activation step.
func TestActivateAccountToken(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
sat := planet.Satellites[0]
service := sat.API.Console.Service
createUser := console.CreateUser{
FullName: "Alice",
ShortName: "Alice",
Email: "alice@mail.test",
Password: "123a123",
}
regToken, err := service.CreateRegToken(ctx, 1)
require.NoError(t, err)
rootUser, err := service.CreateUser(ctx, createUser, regToken.Secret)
require.NoError(t, err)
activationToken, err := service.GenerateActivationToken(ctx, rootUser.ID, rootUser.Email)
require.NoError(t, err)
authToken, err := service.ActivateAccount(ctx, activationToken)
require.NoError(t, err)
_, err = service.Authorize(consoleauth.WithAPIKey(ctx, []byte(authToken)))
require.NoError(t, err)
})
}
func TestRESTKeys(t *testing.T) { func TestRESTKeys(t *testing.T) {
testplanet.Run(t, testplanet.Config{ testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1, SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
@ -709,23 +671,23 @@ func TestRESTKeys(t *testing.T) {
user, err := service.GetUser(ctx, proj1.OwnerID) user, err := service.GetUser(ctx, proj1.OwnerID)
require.NoError(t, err) require.NoError(t, err)
authCtx, err := sat.AuthenticatedContext(ctx, user.ID) userCtx, err := sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
now := time.Now() now := time.Now()
expires := 5 * time.Hour expires := 5 * time.Hour
apiKey, expiresAt, err := service.CreateRESTKey(authCtx, expires) apiKey, expiresAt, err := service.CreateRESTKey(userCtx, expires)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, apiKey) require.NotEmpty(t, apiKey)
require.True(t, expiresAt.After(now)) require.True(t, expiresAt.After(now))
require.True(t, expiresAt.Before(now.Add(expires+time.Hour))) require.True(t, expiresAt.Before(now.Add(expires+time.Hour)))
// test revocation // test revocation
require.NoError(t, service.RevokeRESTKey(authCtx, apiKey)) require.NoError(t, service.RevokeRESTKey(userCtx, apiKey))
// test revoke non existent key // test revoke non existent key
nonexistent := testrand.UUID() nonexistent := testrand.UUID()
err = service.RevokeRESTKey(authCtx, nonexistent.String()) err = service.RevokeRESTKey(userCtx, nonexistent.String())
require.Error(t, err) require.Error(t, err)
}) })
} }
@ -748,23 +710,23 @@ func TestLockAccount(t *testing.T) {
user, err := sat.AddUser(ctx, newUser, 1) user, err := sat.AddUser(ctx, newUser, 1)
require.NoError(t, err) require.NoError(t, err)
getNewAuthorization := func() (context.Context, console.Authorization) { updateContext := func() (context.Context, *console.User) {
authCtx, err := sat.AuthenticatedContext(ctx, user.ID) userCtx, err := sat.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
auth, err := console.GetAuth(authCtx) user, err := console.GetUser(userCtx)
require.NoError(t, err) require.NoError(t, err)
return authCtx, auth return userCtx, user
} }
authCtx, _ := getNewAuthorization() userCtx, _ := updateContext()
secret, err := service.ResetMFASecretKey(authCtx) secret, err := service.ResetMFASecretKey(userCtx)
require.NoError(t, err) require.NoError(t, err)
goodCode0, err := console.NewMFAPasscode(secret, time.Time{}) goodCode0, err := console.NewMFAPasscode(secret, time.Time{})
require.NoError(t, err) require.NoError(t, err)
authCtx, _ = getNewAuthorization() userCtx, _ = updateContext()
err = service.EnableUserMFA(authCtx, goodCode0, time.Time{}) err = service.EnableUserMFA(userCtx, goodCode0, time.Time{})
require.NoError(t, err) require.NoError(t, err)
now := time.Now() now := time.Now()
@ -795,7 +757,7 @@ func TestLockAccount(t *testing.T) {
} }
} }
lockedUser, err := service.GetUser(authCtx, user.ID) lockedUser, err := service.GetUser(userCtx, user.ID)
require.NoError(t, err) require.NoError(t, err)
require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty) require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty)
require.True(t, lockedUser.LoginLockoutExpiration.After(now)) require.True(t, lockedUser.LoginLockoutExpiration.After(now))
@ -803,10 +765,10 @@ func TestLockAccount(t *testing.T) {
// lock account once again and check if lockout expiration time increased. // lock account once again and check if lockout expiration time increased.
expDuration := time.Duration(math.Pow(consoleConfig.FailedLoginPenalty, float64(lockedUser.FailedLoginCount-1))) * time.Minute expDuration := time.Duration(math.Pow(consoleConfig.FailedLoginPenalty, float64(lockedUser.FailedLoginCount-1))) * time.Minute
lockoutExpDate := now.Add(expDuration) lockoutExpDate := now.Add(expDuration)
err = service.UpdateUsersFailedLoginState(authCtx, lockedUser, lockoutExpDate) err = service.UpdateUsersFailedLoginState(userCtx, lockedUser, lockoutExpDate)
require.NoError(t, err) require.NoError(t, err)
lockedUser, err = service.GetUser(authCtx, user.ID) lockedUser, err = service.GetUser(userCtx, user.ID)
require.NoError(t, err) require.NoError(t, err)
require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty+1) require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty+1)
@ -815,7 +777,7 @@ func TestLockAccount(t *testing.T) {
// unlock account by successful login // unlock account by successful login
lockedUser.LoginLockoutExpiration = now.Add(-time.Second) lockedUser.LoginLockoutExpiration = now.Add(-time.Second)
err = usersDB.Update(authCtx, lockedUser) err = usersDB.Update(userCtx, lockedUser)
require.NoError(t, err) require.NoError(t, err)
authUser.Password = newUser.FullName authUser.Password = newUser.FullName
@ -823,7 +785,7 @@ func TestLockAccount(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, token) require.NotEmpty(t, token)
unlockedUser, err := service.GetUser(authCtx, user.ID) unlockedUser, err := service.GetUser(userCtx, user.ID)
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, unlockedUser.FailedLoginCount) require.Zero(t, unlockedUser.FailedLoginCount)
@ -839,7 +801,7 @@ func TestLockAccount(t *testing.T) {
} }
} }
lockedUser, err = service.GetUser(authCtx, user.ID) lockedUser, err = service.GetUser(userCtx, user.ID)
require.NoError(t, err) require.NoError(t, err)
require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty) require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty)
require.True(t, lockedUser.LoginLockoutExpiration.After(now)) require.True(t, lockedUser.LoginLockoutExpiration.After(now))
@ -847,7 +809,7 @@ func TestLockAccount(t *testing.T) {
// unlock account // unlock account
lockedUser.LoginLockoutExpiration = time.Time{} lockedUser.LoginLockoutExpiration = time.Time{}
lockedUser.FailedLoginCount = 0 lockedUser.FailedLoginCount = 0
err = usersDB.Update(authCtx, lockedUser) err = usersDB.Update(userCtx, lockedUser)
require.NoError(t, err) require.NoError(t, err)
// check if user's account gets locked because of providing wrong mfa recovery code. // check if user's account gets locked because of providing wrong mfa recovery code.
@ -863,7 +825,7 @@ func TestLockAccount(t *testing.T) {
} }
} }
lockedUser, err = service.GetUser(authCtx, user.ID) lockedUser, err = service.GetUser(userCtx, user.ID)
require.NoError(t, err) require.NoError(t, err)
require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty) require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty)
require.True(t, lockedUser.LoginLockoutExpiration.After(now)) require.True(t, lockedUser.LoginLockoutExpiration.After(now))
@ -882,3 +844,43 @@ func TestWalletJsonMarshall(t *testing.T) {
require.Contains(t, string(out), "\"balance\":100") require.Contains(t, string(out), "\"balance\":100")
} }
func TestSessionExpiration(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0,
Reconfigure: testplanet.Reconfigure{
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
config.Console.SessionDuration = time.Hour
},
},
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
sat := planet.Satellites[0]
service := sat.API.Console.Service
user, err := sat.AddUser(ctx, console.CreateUser{
FullName: "Test User",
Email: "test@mail.test",
}, 1)
require.NoError(t, err)
// Session should be added to DB after token request
token, err := service.Token(ctx, console.AuthUser{Email: user.Email, Password: user.FullName})
require.NoError(t, err)
_, err = service.TokenAuth(ctx, token, time.Now())
require.NoError(t, err)
sessionID, err := uuid.FromBytes(token.Payload)
require.NoError(t, err)
_, err = sat.DB.Console().WebappSessions().GetBySessionID(ctx, sessionID)
require.NoError(t, err)
// Session should be removed from DB after it has expired
_, err = service.TokenAuth(ctx, token, time.Now().Add(2*time.Hour))
require.True(t, console.ErrTokenExpiration.Has(err))
_, err = sat.DB.Console().WebappSessions().GetBySessionID(ctx, sessionID)
require.ErrorIs(t, sql.ErrNoRows, err)
})
}

View File

@ -117,6 +117,8 @@ type AuthUser struct {
Password string `json:"password"` Password string `json:"password"`
MFAPasscode string `json:"mfaPasscode"` MFAPasscode string `json:"mfaPasscode"`
MFARecoveryCode string `json:"mfaRecoveryCode"` MFARecoveryCode string `json:"mfaRecoveryCode"`
IP string `json:"-"`
UserAgent string `json:"-"`
} }
// UserStatus - is used to indicate status of the users account. // UserStatus - is used to indicate status of the users account.
@ -193,3 +195,34 @@ type ResponseUser struct {
MFAEnabled bool `json:"isMFAEnabled"` MFAEnabled bool `json:"isMFAEnabled"`
MFARecoveryCodeCount int `json:"mfaRecoveryCodeCount"` MFARecoveryCodeCount int `json:"mfaRecoveryCodeCount"`
} }
// key is a context value key type.
type key int
// userKey is context key for User.
const userKey key = 0
// WithUser creates new context with User.
func WithUser(ctx context.Context, user *User) context.Context {
return context.WithValue(ctx, userKey, user)
}
// WithUserFailure creates new context with User failure.
func WithUserFailure(ctx context.Context, err error) context.Context {
return context.WithValue(ctx, userKey, err)
}
// GetUser gets User from context.
func GetUser(ctx context.Context) (*User, error) {
value := ctx.Value(userKey)
if user, ok := value.(*User); ok {
return user, nil
}
if err, ok := value.(error); ok {
return nil, Error.Wrap(err)
}
return nil, Error.New("user is not in context")
}

View File

@ -103,10 +103,10 @@ func TestBucketAttribution(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
createBucketAndCheckAttribution := func(userID uuid.UUID, apiKeyName, bucketName string) { createBucketAndCheckAttribution := func(userID uuid.UUID, apiKeyName, bucketName string) {
authCtx, err := satellite.AuthenticatedContext(ctx, userID) userCtx, err := satellite.UserContext(ctx, userID)
require.NoError(t, err, errTag) require.NoError(t, err, errTag)
_, apiKeyInfo, err := satellite.API.Console.Service.CreateAPIKey(authCtx, satProject.ID, apiKeyName) _, apiKeyInfo, err := satellite.API.Console.Service.CreateAPIKey(userCtx, satProject.ID, apiKeyName)
require.NoError(t, err, errTag) require.NoError(t, err, errTag)
config := uplink.Config{ config := uplink.Config{
@ -168,10 +168,10 @@ func TestQueryAttribution(t *testing.T) {
satProject, err := satellite.AddProject(ctx, user.ID, "test") satProject, err := satellite.AddProject(ctx, user.ID, "test")
require.NoError(t, err) require.NoError(t, err)
authCtx, err := satellite.AuthenticatedContext(ctx, user.ID) userCtx, err := satellite.UserContext(ctx, user.ID)
require.NoError(t, err) require.NoError(t, err)
_, apiKeyInfo, err := satellite.API.Console.Service.CreateAPIKey(authCtx, satProject.ID, "root") _, apiKeyInfo, err := satellite.API.Console.Service.CreateAPIKey(userCtx, satProject.ID, "root")
require.NoError(t, err) require.NoError(t, err)
access, err := uplink.RequestAccessWithPassphrase(ctx, satellite.NodeURL().String(), apiKeyInfo.Serialize(), "mypassphrase") access, err := uplink.RequestAccessWithPassphrase(ctx, satellite.NodeURL().String(), apiKeyInfo.Serialize(), "mypassphrase")

View File

@ -53,12 +53,12 @@ func NewEndpoint(
svr := server.NewDefaultServer(manager) svr := server.NewDefaultServer(manager)
svr.SetUserAuthorizationHandler(func(w http.ResponseWriter, r *http.Request) (userID string, err error) { svr.SetUserAuthorizationHandler(func(w http.ResponseWriter, r *http.Request) (userID string, err error) {
auth, err := console.GetAuth(r.Context()) user, err := console.GetUser(r.Context())
if err != nil { if err != nil {
return "", console.ErrUnauthorized.Wrap(err) return "", console.ErrUnauthorized.Wrap(err)
} }
return auth.User.ID.String(), nil return user.ID.String(), nil
}) })
// externalAddress _should_ end with a '/' suffix based on the calling path // externalAddress _should_ end with a '/' suffix based on the calling path

View File

@ -30,7 +30,6 @@ import (
"storj.io/storj/private/testplanet" "storj.io/storj/private/testplanet"
"storj.io/storj/satellite" "storj.io/storj/satellite"
"storj.io/storj/satellite/console" "storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleauth"
"storj.io/storj/satellite/oidc" "storj.io/storj/satellite/oidc"
"storj.io/uplink" "storj.io/uplink"
) )
@ -122,18 +121,15 @@ func TestOIDC(t *testing.T) {
activationToken, err := sat.API.Console.Service.GenerateActivationToken(ctx, user.ID, user.Email) activationToken, err := sat.API.Console.Service.GenerateActivationToken(ctx, user.ID, user.Email)
require.NoError(t, err) require.NoError(t, err)
consoleToken, err := sat.API.Console.Service.ActivateAccount(ctx, activationToken) user, err = sat.API.Console.Service.ActivateAccount(ctx, activationToken)
require.NoError(t, err)
sessionToken, err := sat.API.Console.Service.GenerateSessionToken(ctx, user.ID, user.Email, "", "")
require.NoError(t, err) require.NoError(t, err)
// Set up a test project and bucket // Set up a test project and bucket
authed := console.WithAuth(ctx, console.Authorization{ authed := console.WithUser(ctx, user)
User: *user,
Claims: consoleauth.Claims{
ID: user.ID,
Email: user.Email,
},
})
project, err := sat.API.Console.Service.CreateProject(authed, console.ProjectInfo{ project, err := sat.API.Console.Service.CreateProject(authed, console.ProjectInfo{
Name: "test", Name: "test",
@ -250,7 +246,7 @@ func TestOIDC(t *testing.T) {
{ {
body := strings.NewReader(consent.Encode()) body := strings.NewReader(consent.Encode())
send(t, body, &token, http.StatusOK, authEndpoint, http.MethodPost, consoleToken, "application/x-www-form-urlencoded") send(t, body, &token, http.StatusOK, authEndpoint, http.MethodPost, sessionToken.String(), "application/x-www-form-urlencoded")
} }
require.Equal(t, "Bearer", token.TokenType) require.Equal(t, "Bearer", token.TokenType)

View File

@ -15,7 +15,6 @@ import (
"storj.io/common/macaroon" "storj.io/common/macaroon"
"storj.io/common/uuid" "storj.io/common/uuid"
"storj.io/storj/satellite/console" "storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleauth"
) )
// UUIDAuthorizeGenerate generates an auth code using Storj's uuid. // UUIDAuthorizeGenerate generates an auth code using Storj's uuid.
@ -65,13 +64,7 @@ func (a *MacaroonAccessGenerate) apiKeyForProject(ctx context.Context, data *oau
return nil, err return nil, err
} }
ctx = console.WithAuth(ctx, console.Authorization{ ctx = console.WithUser(ctx, user)
User: *user,
Claims: consoleauth.Claims{
ID: user.ID,
Email: user.Email,
},
})
oauthClient := data.Client.(OAuthClient) oauthClient := data.Client.(OAuthClient)
name := oauthClient.AppName + " / " + oauthClient.ID.String() name := oauthClient.AppName + " / " + oauthClient.ID.String()

View File

@ -85,7 +85,7 @@ compensation.rates.put-tb: "0"
# comma separated monthly withheld percentage rates # comma separated monthly withheld percentage rates
compensation.withheld-percents: 75,75,75,50,50,50,25,25,25,0,0,0,0,0,0 compensation.withheld-percents: 75,75,75,50,50,50,25,25,25,0,0,0,0,0,0
# expiration time for auth tokens, account recovery tokens, and activation tokens # expiration time for account recovery and activation tokens
# console-auth.token-expiration-time: 24h0m0s # console-auth.token-expiration-time: 24h0m0s
# url link for account activation redirect # url link for account activation redirect
@ -244,6 +244,9 @@ compensation.withheld-percents: 75,75,75,50,50,50,25,25,25,0,0,0,0,0,0
# used to communicate with web crawlers and other web robots # used to communicate with web crawlers and other web robots
# console.seo: "User-agent: *\nDisallow: \nDisallow: /cgi-bin/" # console.seo: "User-agent: *\nDisallow: \nDisallow: /cgi-bin/"
# duration a session is valid for
# console.session-duration: 168h0m0s
# path to static resources # path to static resources
# console.static-dir: "" # console.static-dir: ""