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:
parent
8c1caea5db
commit
58c5d44f44
@ -12,4 +12,6 @@ import (
|
||||
type Auth interface {
|
||||
// IsAuthenticated checks if request is performed with all needed authorization credentials.
|
||||
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)
|
||||
}
|
||||
|
@ -178,6 +178,9 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
p("ctx, err = h.auth.IsAuthenticated(ctx, r, true, false)")
|
||||
}
|
||||
p("if err != nil {")
|
||||
if !endpoint.NoCookieAuth {
|
||||
p("h.auth.RemoveAuthCookie(w)")
|
||||
}
|
||||
p("api.ServeError(h.log, w, http.StatusUnauthorized, err)")
|
||||
p("return")
|
||||
p("}")
|
||||
|
@ -10,9 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/zeebo/errs"
|
||||
"go.uber.org/zap"
|
||||
@ -40,7 +38,6 @@ import (
|
||||
"storj.io/storj/satellite/audit"
|
||||
"storj.io/storj/satellite/compensation"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/console/consoleweb"
|
||||
"storj.io/storj/satellite/contact"
|
||||
"storj.io/storj/satellite/gc"
|
||||
@ -237,11 +234,11 @@ func (system *Satellite) AddUser(ctx context.Context, newUser console.CreateUser
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authCtx, err := system.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx, err := system.UserContext(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = system.API.Console.Service.Payments().SetupAccount(authCtx)
|
||||
_, err = system.API.Console.Service.Payments().SetupAccount(userCtx)
|
||||
if err != nil {
|
||||
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) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
authCtx, err := system.AuthenticatedContext(ctx, ownerID)
|
||||
ctx, err = system.UserContext(ctx, ownerID)
|
||||
if err != nil {
|
||||
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,
|
||||
})
|
||||
if err != nil {
|
||||
@ -266,8 +263,8 @@ func (system *Satellite) AddProject(ctx context.Context, ownerID uuid.UUID, name
|
||||
return project, nil
|
||||
}
|
||||
|
||||
// AuthenticatedContext creates context with authentication date for given user.
|
||||
func (system *Satellite) AuthenticatedContext(ctx context.Context, userID uuid.UUID) (_ context.Context, err error) {
|
||||
// UserContext creates context with user.
|
||||
func (system *Satellite) UserContext(ctx context.Context, userID uuid.UUID) (_ context.Context, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// we are using full name as a password
|
||||
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
|
||||
return console.WithUser(ctx, user), nil
|
||||
}
|
||||
|
||||
// Close closes all the subsystems in the Satellite system.
|
||||
|
@ -147,11 +147,11 @@ func (planet *Planet) newUplink(ctx context.Context, name string) (_ *Uplink, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authCtx, err := satellite.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx, err := satellite.UserContext(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, apiKey, err := consoleAPI.Service.CreateAPIKey(authCtx, project.ID, "root")
|
||||
_, apiKey, err := consoleAPI.Service.CreateAPIKey(userCtx, project.ID, "root")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -5,6 +5,7 @@ package consoleauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"time"
|
||||
|
||||
@ -17,7 +18,7 @@ var mon = monkit.Package()
|
||||
|
||||
// Config contains configuration parameters for console auth.
|
||||
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.
|
||||
@ -63,25 +64,35 @@ func (s *Service) createToken(ctx context.Context, claims *Claims) (_ string, er
|
||||
}
|
||||
|
||||
token := Token{Payload: json}
|
||||
err = s.SignToken(&token)
|
||||
signature, err := s.SignToken(token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
token.Signature = signature
|
||||
|
||||
return token.String(), nil
|
||||
}
|
||||
|
||||
// SignToken signs token.
|
||||
func (s *Service) SignToken(token *Token) error {
|
||||
// SignToken returns token signature.
|
||||
func (s *Service) SignToken(token Token) ([]byte, error) {
|
||||
encoded := base64.URLEncoding.EncodeToString(token.Payload)
|
||||
|
||||
signature, err := s.Signer.Sign([]byte(encoded))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token.Signature = signature
|
||||
return nil
|
||||
return signature, 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.
|
||||
|
@ -59,15 +59,15 @@ func (a *Analytics) EventTriggered(w http.ResponseWriter, r *http.Request) {
|
||||
a.serveJSONError(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
auth, err := console.GetAuth(ctx)
|
||||
user, err := console.GetUser(ctx)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
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 {
|
||||
a.analytics.TrackEvent(et.EventName, auth.User.ID, auth.User.Email)
|
||||
a.analytics.TrackEvent(et.EventName, user.ID, user.Email)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@ -88,13 +88,13 @@ func (a *Analytics) PageEventTriggered(w http.ResponseWriter, r *http.Request) {
|
||||
a.serveJSONError(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
auth, err := console.GetAuth(ctx)
|
||||
user, err := console.GetUser(ctx)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, http.StatusUnauthorized, err)
|
||||
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)
|
||||
}
|
||||
|
@ -165,6 +165,7 @@ func (h *ProjectManagementHandler) handleGenGetBucketUsageRollups(w http.Respons
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
|
||||
if err != nil {
|
||||
h.auth.RemoveAuthCookie(w)
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
@ -208,6 +209,7 @@ func (h *ProjectManagementHandler) handleGenCreateProject(w http.ResponseWriter,
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
|
||||
if err != nil {
|
||||
h.auth.RemoveAuthCookie(w)
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
@ -239,6 +241,7 @@ func (h *ProjectManagementHandler) handleGenUpdateProject(w http.ResponseWriter,
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
|
||||
if err != nil {
|
||||
h.auth.RemoveAuthCookie(w)
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
@ -282,6 +285,7 @@ func (h *ProjectManagementHandler) handleGenDeleteProject(w http.ResponseWriter,
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
|
||||
if err != nil {
|
||||
h.auth.RemoveAuthCookie(w)
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
@ -313,6 +317,7 @@ func (h *ProjectManagementHandler) handleGenGetUsersProjects(w http.ResponseWrit
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
|
||||
if err != nil {
|
||||
h.auth.RemoveAuthCookie(w)
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
@ -338,6 +343,7 @@ func (h *APIKeyManagementHandler) handleGenCreateAPIKey(w http.ResponseWriter, r
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
|
||||
if err != nil {
|
||||
h.auth.RemoveAuthCookie(w)
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
@ -369,6 +375,7 @@ func (h *UserManagementHandler) handleGenGetUser(w http.ResponseWriter, r *http.
|
||||
|
||||
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
|
||||
if err != nil {
|
||||
h.auth.RemoveAuthCookie(w)
|
||||
api.ServeError(h.log, w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func Test_DeleteAPIKeyByNameAndProjectID(t *testing.T) {
|
||||
cookie := http.Cookie{
|
||||
Name: "_tokenKey",
|
||||
Path: "/",
|
||||
Value: token,
|
||||
Value: token.String(),
|
||||
Expires: expire,
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,13 @@ func (a *Auth) Token(w http.ResponseWriter, r *http.Request) {
|
||||
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)
|
||||
if err != nil {
|
||||
if console.ErrMFAMissing.Has(err) {
|
||||
@ -104,7 +111,7 @@ func (a *Auth) Token(w http.ResponseWriter, r *http.Request) {
|
||||
a.cookieAuth.SetTokenCookie(w, token)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(token)
|
||||
err = json.NewEncoder(w).Encode(token.String())
|
||||
if err != nil {
|
||||
a.log.Error("token handler could not encode token response", zap.Error(ErrAuthAPI.Wrap(err)))
|
||||
return
|
||||
@ -116,9 +123,21 @@ func (a *Auth) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
defer mon.Task()(&ctx)(nil)
|
||||
|
||||
a.cookieAuth.RemoveTokenCookie(w)
|
||||
|
||||
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.
|
||||
@ -401,27 +420,27 @@ func (a *Auth) GetAccount(w http.ResponseWriter, r *http.Request) {
|
||||
MFARecoveryCodeCount int `json:"mfaRecoveryCodeCount"`
|
||||
}
|
||||
|
||||
auth, err := console.GetAuth(ctx)
|
||||
consoleUser, err := console.GetUser(ctx)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
user.ShortName = auth.User.ShortName
|
||||
user.FullName = auth.User.FullName
|
||||
user.Email = auth.User.Email
|
||||
user.ID = auth.User.ID
|
||||
user.PartnerID = auth.User.PartnerID
|
||||
user.UserAgent = auth.User.UserAgent
|
||||
user.ProjectLimit = auth.User.ProjectLimit
|
||||
user.IsProfessional = auth.User.IsProfessional
|
||||
user.CompanyName = auth.User.CompanyName
|
||||
user.Position = auth.User.Position
|
||||
user.EmployeeCount = auth.User.EmployeeCount
|
||||
user.HaveSalesContact = auth.User.HaveSalesContact
|
||||
user.PaidTier = auth.User.PaidTier
|
||||
user.MFAEnabled = auth.User.MFAEnabled
|
||||
user.MFARecoveryCodeCount = len(auth.User.MFARecoveryCodes)
|
||||
user.ShortName = consoleUser.ShortName
|
||||
user.FullName = consoleUser.FullName
|
||||
user.Email = consoleUser.Email
|
||||
user.ID = consoleUser.ID
|
||||
user.PartnerID = consoleUser.PartnerID
|
||||
user.UserAgent = consoleUser.UserAgent
|
||||
user.ProjectLimit = consoleUser.ProjectLimit
|
||||
user.IsProfessional = consoleUser.IsProfessional
|
||||
user.CompanyName = consoleUser.CompanyName
|
||||
user.Position = consoleUser.Position
|
||||
user.EmployeeCount = consoleUser.EmployeeCount
|
||||
user.HaveSalesContact = consoleUser.HaveSalesContact
|
||||
user.PaidTier = consoleUser.PaidTier
|
||||
user.MFAEnabled = consoleUser.MFAEnabled
|
||||
user.MFARecoveryCodeCount = len(consoleUser.MFARecoveryCodes)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(&user)
|
||||
|
@ -374,7 +374,7 @@ func TestMFAEndpoints(t *testing.T) {
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "_tokenKey",
|
||||
Path: "/",
|
||||
Value: token,
|
||||
Value: token.String(),
|
||||
Expires: time.Now().AddDate(0, 0, 1),
|
||||
})
|
||||
|
||||
@ -599,21 +599,19 @@ func TestResetPasswordEndpoint(t *testing.T) {
|
||||
token = getNewResetToken()
|
||||
|
||||
// Enable MFA.
|
||||
getNewAuthContext := func() context.Context {
|
||||
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
return authCtx
|
||||
}
|
||||
authCtx := getNewAuthContext()
|
||||
|
||||
key, err := service.ResetMFASecretKey(authCtx)
|
||||
userCtx, err := sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
key, err := service.ResetMFASecretKey(userCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
userCtx, err = sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
authCtx = getNewAuthContext()
|
||||
|
||||
passcode, err := console.NewMFAPasscode(key, token.CreatedAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.EnableUserMFA(authCtx, passcode, token.CreatedAt)
|
||||
err = service.EnableUserMFA(userCtx, passcode, token.CreatedAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
status, mfaError = tryPasswordReset(token.Secret.String(), newPass, "", "")
|
||||
|
@ -76,7 +76,7 @@ func Test_AllBucketNames(t *testing.T) {
|
||||
cookie := http.Cookie{
|
||||
Name: "_tokenKey",
|
||||
Path: "/",
|
||||
Value: token,
|
||||
Value: token.String(),
|
||||
Expires: expire,
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ func Test_TotalUsageLimits(t *testing.T) {
|
||||
cookie := http.Cookie{
|
||||
Name: "_tokenKey",
|
||||
Path: "/",
|
||||
Value: token,
|
||||
Value: token.String(),
|
||||
Expires: expire,
|
||||
}
|
||||
|
||||
@ -203,7 +203,7 @@ func Test_DailyUsage(t *testing.T) {
|
||||
cookie := http.Cookie{
|
||||
Name: "_tokenKey",
|
||||
Path: "/",
|
||||
Value: token,
|
||||
Value: token.String(),
|
||||
Expires: expire,
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,7 @@ func TestGraphqlMutation(t *testing.T) {
|
||||
console.Config{
|
||||
PasswordCost: console.TestPasswordCost,
|
||||
DefaultProjectLimit: 5,
|
||||
SessionDuration: time.Hour,
|
||||
},
|
||||
)
|
||||
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})
|
||||
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)
|
||||
|
||||
authCtx := console.WithAuth(ctx, sauth)
|
||||
|
||||
testQuery := func(t *testing.T, query string) (interface{}, error) {
|
||||
result := graphql.Do(graphql.Params{
|
||||
Schema: schema,
|
||||
Context: authCtx,
|
||||
Context: userCtx,
|
||||
RequestString: query,
|
||||
RootObject: rootObject,
|
||||
})
|
||||
@ -190,11 +189,9 @@ func TestGraphqlMutation(t *testing.T) {
|
||||
token, err = service.Token(ctx, console.AuthUser{Email: rootUser.Email, Password: createUser.Password})
|
||||
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)
|
||||
|
||||
authCtx = console.WithAuth(ctx, sauth)
|
||||
|
||||
var projectIDField string
|
||||
t.Run("Create project mutation", func(t *testing.T) {
|
||||
projectInfo := console.ProjectInfo{
|
||||
@ -223,14 +220,14 @@ func TestGraphqlMutation(t *testing.T) {
|
||||
projectID, err := uuid.FromString(projectIDField)
|
||||
require.NoError(t, err)
|
||||
|
||||
project, err := service.GetProject(authCtx, projectID)
|
||||
project, err := service.GetProject(userCtx, projectID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rootUser.PartnerID, project.PartnerID)
|
||||
|
||||
regTokenUser1, err := service.CreateRegToken(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
user1, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
user1, err := service.CreateUser(userCtx, console.CreateUser{
|
||||
FullName: "User1",
|
||||
Email: "u1@mail.test",
|
||||
Password: "123a123",
|
||||
@ -254,7 +251,7 @@ func TestGraphqlMutation(t *testing.T) {
|
||||
regTokenUser2, err := service.CreateRegToken(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
user2, err := service.CreateUser(userCtx, console.CreateUser{
|
||||
FullName: "User1",
|
||||
Email: "u2@mail.test",
|
||||
Password: "123a123",
|
||||
@ -353,7 +350,7 @@ func TestGraphqlMutation(t *testing.T) {
|
||||
id, err := uuid.FromString(keyID)
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err := service.GetAPIKeyInfo(authCtx, id)
|
||||
info, err := service.GetAPIKeyInfo(userCtx, id)
|
||||
require.NoError(t, err)
|
||||
|
||||
query := fmt.Sprintf(
|
||||
|
@ -135,7 +135,7 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
|
||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||
project, _ := p.Source.(*console.Project)
|
||||
|
||||
_, err := console.GetAuth(p.Context)
|
||||
_, err := console.GetUser(p.Context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -183,11 +183,6 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
|
||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||
project, _ := p.Source.(*console.Project)
|
||||
|
||||
_, err := console.GetAuth(p.Context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cursor := cursorArgsToAPIKeysCursor(p.Args[CursorArg].(map[string]interface{}))
|
||||
page, err := service.GetAPIKeys(p.Context, project.ID, cursor)
|
||||
if err != nil {
|
||||
|
@ -97,6 +97,7 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
console.Config{
|
||||
PasswordCost: console.TestPasswordCost,
|
||||
DefaultProjectLimit: 5,
|
||||
SessionDuration: time.Hour,
|
||||
},
|
||||
)
|
||||
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})
|
||||
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)
|
||||
|
||||
authCtx := console.WithAuth(ctx, sauth)
|
||||
|
||||
testQuery := func(t *testing.T, query string) interface{} {
|
||||
result := graphql.Do(graphql.Params{
|
||||
Schema: schema,
|
||||
Context: authCtx,
|
||||
Context: userCtx,
|
||||
RequestString: query,
|
||||
RootObject: rootObject,
|
||||
})
|
||||
@ -179,7 +178,7 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
return result.Data
|
||||
}
|
||||
|
||||
createdProject, err := service.CreateProject(authCtx, console.ProjectInfo{
|
||||
createdProject, err := service.CreateProject(userCtx, console.ProjectInfo{
|
||||
Name: "TestProject",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@ -210,7 +209,7 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
regTokenUser1, err := service.CreateRegToken(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
|
||||
user1, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
user1, err := service.CreateUser(userCtx, console.CreateUser{
|
||||
FullName: "Mickey Last",
|
||||
ShortName: "Last",
|
||||
Password: "123a123",
|
||||
@ -233,7 +232,7 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
regTokenUser2, err := service.CreateRegToken(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2, err := service.CreateUser(authCtx, console.CreateUser{
|
||||
user2, err := service.CreateUser(userCtx, console.CreateUser{
|
||||
FullName: "Dubas Name",
|
||||
ShortName: "Name",
|
||||
Email: "muu2@mail.test",
|
||||
@ -253,7 +252,7 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
user2.Email = "muu2@mail.test"
|
||||
})
|
||||
|
||||
users, err := service.AddProjectMembers(authCtx, createdProject.ID, []string{
|
||||
users, err := service.AddProjectMembers(userCtx, createdProject.ID, []string{
|
||||
user1.Email,
|
||||
user2.Email,
|
||||
})
|
||||
@ -316,10 +315,10 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
assert.True(t, foundU2)
|
||||
})
|
||||
|
||||
keyInfo1, _, err := service.CreateAPIKey(authCtx, createdProject.ID, "key1")
|
||||
keyInfo1, _, err := service.CreateAPIKey(userCtx, createdProject.ID, "key1")
|
||||
require.NoError(t, err)
|
||||
|
||||
keyInfo2, _, err := service.CreateAPIKey(authCtx, createdProject.ID, "key2")
|
||||
keyInfo2, _, err := service.CreateAPIKey(userCtx, createdProject.ID, "key2")
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("Project query api keys", func(t *testing.T) {
|
||||
@ -372,7 +371,7 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
assert.True(t, foundKey2)
|
||||
})
|
||||
|
||||
project2, err := service.CreateProject(authCtx, console.ProjectInfo{
|
||||
project2, err := service.CreateProject(userCtx, console.ProjectInfo{
|
||||
Name: "Project2",
|
||||
Description: "Test desc",
|
||||
})
|
||||
|
@ -6,6 +6,8 @@ package consolewebauth
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
)
|
||||
|
||||
// CookieSettings variable cookie settings.
|
||||
@ -27,20 +29,25 @@ func NewCookieAuth(settings CookieSettings) *CookieAuth {
|
||||
}
|
||||
|
||||
// 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)
|
||||
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.
|
||||
func (auth *CookieAuth) SetTokenCookie(w http.ResponseWriter, token string) {
|
||||
func (auth *CookieAuth) SetTokenCookie(w http.ResponseWriter, token consoleauth.Token) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: auth.settings.Name,
|
||||
Value: token,
|
||||
Value: token.String(),
|
||||
Path: auth.settings.Path,
|
||||
// TODO: get expiration from token
|
||||
Expires: time.Now().Add(time.Hour * 24),
|
||||
@ -60,3 +67,8 @@ func (auth *CookieAuth) RemoveTokenCookie(w http.ResponseWriter) {
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
})
|
||||
}
|
||||
|
||||
// GetTokenCookieName returns the name of the cookie storing the session token.
|
||||
func (auth *CookieAuth) GetTokenCookieName() string {
|
||||
return auth.settings.Name
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -69,10 +70,13 @@ func TestAuth(t *testing.T) {
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
var oldCookies []*http.Cookie
|
||||
|
||||
{ // Get_AccountInfo
|
||||
resp, body := test.request(http.MethodGet, "/auth/account", nil)
|
||||
require.Equal(test.t, http.StatusOK, resp.StatusCode)
|
||||
require.Contains(test.t, body, "fullName")
|
||||
oldCookies = resp.Cookies()
|
||||
|
||||
var userIdentifier struct{ ID string }
|
||||
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)
|
||||
}
|
||||
|
||||
{ // 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
|
||||
hitRateLimiter := false
|
||||
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) login(email, password string) {
|
||||
func (test *test) login(email, password string) Response {
|
||||
resp, body := test.request(
|
||||
http.MethodPost, "/auth/token",
|
||||
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.Equal(test.t, http.StatusOK, resp.StatusCode)
|
||||
require.Equal(test.t, rawToken, cookie.Value)
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (test *test) registerUser(email, password string) registeredUser {
|
||||
|
@ -35,7 +35,6 @@ import (
|
||||
"storj.io/storj/private/web"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/console/consoleweb/consoleapi"
|
||||
"storj.io/storj/satellite/console/consoleweb/consoleql"
|
||||
"storj.io/storj/satellite/console/consoleweb/consolewebauth"
|
||||
@ -180,6 +179,62 @@ type templates struct {
|
||||
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.
|
||||
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{
|
||||
@ -219,9 +274,9 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc
|
||||
router := mux.NewRouter()
|
||||
|
||||
if server.config.GeneratedAPIEnabled {
|
||||
consoleapi.NewProjectManagement(logger, server.service, router, server.service)
|
||||
consoleapi.NewAPIKeyManagement(logger, server.service, router, server.service)
|
||||
consoleapi.NewUserManagement(logger, server.service, router, server.service)
|
||||
consoleapi.NewProjectManagement(logger, server.service, router, &apiAuth{&server})
|
||||
consoleapi.NewAPIKeyManagement(logger, server.service, router, &apiAuth{&server})
|
||||
consoleapi.NewUserManagement(logger, server.service, router, &apiAuth{&server})
|
||||
}
|
||||
|
||||
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 {
|
||||
token, err := server.cookieAuth.GetToken(r)
|
||||
if err != nil {
|
||||
return console.WithAuthFailure(ctx, err)
|
||||
return console.WithUserFailure(ctx, console.ErrUnauthorized.Wrap(err))
|
||||
}
|
||||
|
||||
ctx = consoleauth.WithAPIKey(ctx, []byte(token))
|
||||
|
||||
auth, err := server.service.Authorize(ctx)
|
||||
newCtx, err := server.service.TokenAuth(ctx, token, time.Now())
|
||||
if err != nil {
|
||||
return console.WithAuthFailure(ctx, err)
|
||||
return console.WithUserFailure(ctx, err)
|
||||
}
|
||||
|
||||
return console.WithAuth(ctx, auth)
|
||||
return newCtx
|
||||
}
|
||||
|
||||
ctx = ctxWithAuth(r.Context())
|
||||
@ -524,14 +577,12 @@ func (server *Server) bucketUsageReportHandler(w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
|
||||
auth, err := server.service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token)))
|
||||
ctx, err = server.service.TokenAuth(ctx, token, time.Now())
|
||||
if err != nil {
|
||||
server.serveError(w, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = console.WithAuth(ctx, auth)
|
||||
|
||||
// parse query params
|
||||
projectID, err := uuid.FromString(r.URL.Query().Get("projectID"))
|
||||
if err != nil {
|
||||
@ -625,7 +676,7 @@ func (server *Server) accountActivationHandler(w http.ResponseWriter, r *http.Re
|
||||
defer mon.Task()(&ctx)(nil)
|
||||
activationToken := r.URL.Query().Get("token")
|
||||
|
||||
token, err := server.service.ActivateAccount(ctx, activationToken)
|
||||
user, err := server.service.ActivateAccount(ctx, activationToken)
|
||||
if err != nil {
|
||||
server.log.Error("activation: failed to activate account",
|
||||
zap.String("token", activationToken),
|
||||
@ -645,6 +696,18 @@ func (server *Server) accountActivationHandler(w http.ResponseWriter, r *http.Re
|
||||
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)
|
||||
|
||||
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.
|
||||
func NewUserIDRateLimiter(config web.RateLimiterConfig) *web.RateLimiter {
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
return auth.User.ID.String(), nil
|
||||
return user.ID.String(), nil
|
||||
})
|
||||
}
|
||||
|
@ -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})
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenStr := token.String()
|
||||
|
||||
if userNum == 1 {
|
||||
firstToken = token
|
||||
firstToken = tokenStr
|
||||
}
|
||||
|
||||
// Expect burst number of successes.
|
||||
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.
|
||||
require.Equal(t, http.StatusTooManyRequests, applyCouponStatus(token))
|
||||
require.Equal(t, http.StatusTooManyRequests, applyCouponStatus(tokenStr))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -86,12 +86,12 @@ func NewMFASecretKey() (string, error) {
|
||||
func (s *Service) EnableUserMFA(ctx context.Context, passcode string, t time.Time) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
auth, err := s.getAuthAndAuditLog(ctx, "enable MFA")
|
||||
user, err := s.getUserAndAuditLog(ctx, "enable MFA")
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
valid, err := ValidateMFAPasscode(passcode, auth.User.MFASecretKey, t)
|
||||
valid, err := ValidateMFAPasscode(passcode, user.MFASecretKey, t)
|
||||
if err != nil {
|
||||
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))
|
||||
}
|
||||
|
||||
auth.User.MFAEnabled = true
|
||||
err = s.store.Users().Update(ctx, &auth.User)
|
||||
user.MFAEnabled = true
|
||||
err = s.store.Users().Update(ctx, user)
|
||||
if err != nil {
|
||||
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) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
auth, err := s.getAuthAndAuditLog(ctx, "disable MFA")
|
||||
user, err := s.getUserAndAuditLog(ctx, "disable MFA")
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
user := &auth.User
|
||||
|
||||
if !user.MFAEnabled {
|
||||
return nil
|
||||
}
|
||||
@ -139,7 +137,7 @@ func (s *Service) DisableUserMFA(ctx context.Context, passcode string, t time.Ti
|
||||
return ErrUnauthorized.Wrap(ErrMFARecoveryCode.New(mfaRecoveryInvalidErrMsg))
|
||||
}
|
||||
} else if passcode != "" {
|
||||
valid, err := ValidateMFAPasscode(passcode, auth.User.MFASecretKey, t)
|
||||
valid, err := ValidateMFAPasscode(passcode, user.MFASecretKey, t)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
auth.User.MFAEnabled = false
|
||||
auth.User.MFASecretKey = ""
|
||||
auth.User.MFARecoveryCodes = nil
|
||||
err = s.store.Users().Update(ctx, &auth.User)
|
||||
user.MFAEnabled = false
|
||||
user.MFASecretKey = ""
|
||||
user.MFARecoveryCodes = nil
|
||||
err = s.store.Users().Update(ctx, user)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
@ -185,7 +183,7 @@ func NewMFARecoveryCode() (string, error) {
|
||||
func (s *Service) ResetMFASecretKey(ctx context.Context) (key string, err error) {
|
||||
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 {
|
||||
return "", Error.Wrap(err)
|
||||
}
|
||||
@ -195,8 +193,8 @@ func (s *Service) ResetMFASecretKey(ctx context.Context) (key string, err error)
|
||||
return "", Error.Wrap(err)
|
||||
}
|
||||
|
||||
auth.User.MFASecretKey = key
|
||||
err = s.store.Users().Update(ctx, &auth.User)
|
||||
user.MFASecretKey = key
|
||||
err = s.store.Users().Update(ctx, user)
|
||||
if err != nil {
|
||||
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) {
|
||||
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 {
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
|
||||
if !auth.User.MFAEnabled {
|
||||
if !user.MFAEnabled {
|
||||
return nil, ErrUnauthorized.New(mfaRecoveryGenerationErrMsg)
|
||||
}
|
||||
|
||||
@ -225,9 +223,9 @@ func (s *Service) ResetMFARecoveryCodes(ctx context.Context) (codes []string, er
|
||||
}
|
||||
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 {
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// requestKey is context key for Requests.
|
||||
const requestKey key = 1
|
||||
|
||||
// WithRequest creates new context with *http.Request.
|
||||
func WithRequest(ctx context.Context, req *http.Request) context.Context {
|
||||
return context.WithValue(ctx, requestKey, req)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@ package console_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"math/big"
|
||||
@ -24,7 +25,6 @@ import (
|
||||
"storj.io/storj/private/testplanet"
|
||||
"storj.io/storj/satellite"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
)
|
||||
|
||||
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.OwnerID, up2Pro1.OwnerID)
|
||||
|
||||
authCtx1, err := sat.AuthenticatedContext(ctx, up1Pro1.OwnerID)
|
||||
userCtx1, err := sat.UserContext(ctx, up1Pro1.OwnerID)
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx2, err := sat.AuthenticatedContext(ctx, up2Pro1.OwnerID)
|
||||
userCtx2, err := sat.UserContext(ctx, up2Pro1.OwnerID)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("TestGetProject", func(t *testing.T) {
|
||||
// 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.Equal(t, up1Pro1.ID, project.ID)
|
||||
|
||||
// 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.Nil(t, project)
|
||||
})
|
||||
@ -75,17 +75,17 @@ func TestService(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.False(t, user.PaidTier)
|
||||
// get context
|
||||
authCtx1, err := sat.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx1, err := sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
// 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)
|
||||
// update auth ctx
|
||||
authCtx1, err = sat.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx1, err = sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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,
|
||||
Description: updatedDescription,
|
||||
StorageLimit: updatedStorageLimit,
|
||||
@ -102,7 +102,7 @@ func TestService(t *testing.T) {
|
||||
require.Equal(t, updatedBandwidthLimit, *updatedProject.BandwidthLimit)
|
||||
|
||||
// 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",
|
||||
Description: "TestUpdate",
|
||||
StorageLimit: memory.Size(100),
|
||||
@ -127,7 +127,7 @@ func TestService(t *testing.T) {
|
||||
StorageLimit: 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.Nil(t, updatedProject)
|
||||
|
||||
@ -137,7 +137,7 @@ func TestService(t *testing.T) {
|
||||
err = sat.DB.Console().Projects().Update(ctx, up1Pro1)
|
||||
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.Nil(t, updatedProject)
|
||||
|
||||
@ -146,7 +146,7 @@ func TestService(t *testing.T) {
|
||||
err = sat.DB.Console().Projects().Update(ctx, up1Pro1)
|
||||
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.Equal(t, updateInfo.Name, updatedProject.Name)
|
||||
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.BandwidthLimit, *updatedProject.BandwidthLimit)
|
||||
|
||||
project, err := service.GetProject(authCtx1, up1Pro1.ID)
|
||||
project, err := service.GetProject(userCtx1, up1Pro1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, updateInfo.StorageLimit, *project.StorageLimit)
|
||||
require.Equal(t, updateInfo.BandwidthLimit, *project.BandwidthLimit)
|
||||
@ -163,69 +163,69 @@ func TestService(t *testing.T) {
|
||||
|
||||
t.Run("TestAddProjectMembers", func(t *testing.T) {
|
||||
// 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.Len(t, addedUsers, 1)
|
||||
require.Contains(t, addedUsers, up2User)
|
||||
|
||||
// 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.Nil(t, addedUsers)
|
||||
})
|
||||
|
||||
t.Run("TestGetProjectMembers", func(t *testing.T) {
|
||||
// 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.Len(t, userPage.ProjectMembers, 2)
|
||||
|
||||
// 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.Len(t, userPage.ProjectMembers, 2)
|
||||
|
||||
// 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.Nil(t, userPage)
|
||||
})
|
||||
|
||||
t.Run("TestDeleteProjectMembers", func(t *testing.T) {
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
})
|
||||
|
||||
t.Run("TestDeleteProject", func(t *testing.T) {
|
||||
// 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)
|
||||
|
||||
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.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)
|
||||
|
||||
// Deleting the own project should now work
|
||||
err = service.DeleteProject(authCtx1, up1Pro1.ID)
|
||||
err = service.DeleteProject(userCtx1, up1Pro1.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Deleting someone else project should not work
|
||||
err = service.DeleteProject(authCtx1, up2Pro1.ID)
|
||||
err = service.DeleteProject(userCtx1, up2Pro1.ID)
|
||||
require.Error(t, err)
|
||||
|
||||
err = planet.Uplinks[1].CreateBucket(ctx, sat, "testbucket")
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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.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) {
|
||||
const newEmail = "newEmail@example.com"
|
||||
|
||||
err = service.ChangeEmail(authCtx2, newEmail)
|
||||
err = service.ChangeEmail(userCtx2, newEmail)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, _, err := service.GetUserByEmailWithUnverified(authCtx2, newEmail)
|
||||
user, _, err := service.GetUserByEmailWithUnverified(userCtx2, newEmail)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newEmail, user.Email)
|
||||
|
||||
err = service.ChangeEmail(authCtx2, newEmail)
|
||||
err = service.ChangeEmail(userCtx2, newEmail)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
@ -257,19 +257,19 @@ func TestService(t *testing.T) {
|
||||
ProjectID: up2Pro1.ID,
|
||||
}
|
||||
|
||||
_, err := sat.API.Buckets.Service.CreateBucket(authCtx2, bucket1)
|
||||
_, err := sat.API.Buckets.Service.CreateBucket(userCtx2, bucket1)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = sat.API.Buckets.Service.CreateBucket(authCtx2, bucket2)
|
||||
_, err = sat.API.Buckets.Service.CreateBucket(userCtx2, bucket2)
|
||||
require.NoError(t, err)
|
||||
|
||||
bucketNames, err := service.GetAllBucketNames(authCtx2, up2Pro1.ID)
|
||||
bucketNames, err := service.GetAllBucketNames(userCtx2, up2Pro1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, bucket1.Name, bucketNames[0])
|
||||
require.Equal(t, bucket2.Name, bucketNames[1])
|
||||
|
||||
// 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.Nil(t, bucketsForUnauthorizedUser)
|
||||
})
|
||||
@ -295,10 +295,10 @@ func TestService(t *testing.T) {
|
||||
require.NotNil(t, info)
|
||||
|
||||
// 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)
|
||||
|
||||
err = service.DeleteAPIKeyByNameAndProjectID(authCtx2, apikey.Name, up2Pro1.ID)
|
||||
err = service.DeleteAPIKeyByNameAndProjectID(userCtx2, apikey.Name, up2Pro1.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err = sat.DB.Console().APIKeys().Get(ctx, createdKey.ID)
|
||||
@ -351,11 +351,11 @@ func TestPaidTier(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.False(t, user.PaidTier)
|
||||
|
||||
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx, err := sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// expect user to be in paid tier
|
||||
@ -365,18 +365,18 @@ func TestPaidTier(t *testing.T) {
|
||||
require.Equal(t, usageConfig.Project.Paid, user.ProjectLimit)
|
||||
|
||||
// update auth ctx
|
||||
authCtx, err = sat.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx, err = sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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.Equal(t, usageConfig.Storage.Paid, *proj1.StorageLimit)
|
||||
require.Equal(t, usageConfig.Bandwidth.Paid, *proj1.BandwidthLimit)
|
||||
require.Equal(t, usageConfig.Segment.Paid, *proj1.SegmentLimit)
|
||||
|
||||
// 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.Equal(t, usageConfig.Storage.Paid, *proj2.StorageLimit)
|
||||
})
|
||||
@ -395,22 +395,22 @@ func TestMFA(t *testing.T) {
|
||||
}, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
getNewAuthorization := func() (context.Context, console.Authorization) {
|
||||
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
|
||||
updateContext := func() (context.Context, *console.User) {
|
||||
userCtx, err := sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
auth, err := console.GetAuth(authCtx)
|
||||
user, err := console.GetUser(userCtx)
|
||||
require.NoError(t, err)
|
||||
return authCtx, auth
|
||||
return userCtx, user
|
||||
}
|
||||
authCtx, auth := getNewAuthorization()
|
||||
userCtx, user := updateContext()
|
||||
|
||||
var key string
|
||||
t.Run("TestResetMFASecretKey", func(t *testing.T) {
|
||||
key, err = service.ResetMFASecretKey(authCtx)
|
||||
key, err = service.ResetMFASecretKey(userCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.NotEmpty(t, auth.User.MFASecretKey)
|
||||
_, user := updateContext()
|
||||
require.NotEmpty(t, user.MFASecretKey)
|
||||
})
|
||||
|
||||
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))
|
||||
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))
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
_, err = service.ResetMFARecoveryCodes(authCtx)
|
||||
userCtx, _ = updateContext()
|
||||
_, err = service.ResetMFARecoveryCodes(userCtx)
|
||||
require.True(t, console.ErrUnauthorized.Has(err))
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.False(t, auth.User.MFAEnabled)
|
||||
_, user = updateContext()
|
||||
require.False(t, user.MFAEnabled)
|
||||
})
|
||||
|
||||
t.Run("TestEnableUserMFAGoodPasscode", func(t *testing.T) {
|
||||
@ -434,13 +434,13 @@ func TestMFA(t *testing.T) {
|
||||
goodCode, err := console.NewMFAPasscode(key, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
err = service.EnableUserMFA(authCtx, goodCode, time.Time{})
|
||||
userCtx, _ = updateContext()
|
||||
err = service.EnableUserMFA(userCtx, goodCode, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.True(t, auth.User.MFAEnabled)
|
||||
require.Equal(t, auth.User.MFASecretKey, key)
|
||||
_, user = updateContext()
|
||||
require.True(t, user.MFAEnabled)
|
||||
require.Equal(t, user.MFASecretKey, key)
|
||||
})
|
||||
|
||||
t.Run("TestMFAGetToken", func(t *testing.T) {
|
||||
@ -471,13 +471,13 @@ func TestMFA(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("TestMFARecoveryCodes", func(t *testing.T) {
|
||||
_, err = service.ResetMFARecoveryCodes(authCtx)
|
||||
_, err = service.ResetMFARecoveryCodes(userCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.Len(t, auth.User.MFARecoveryCodes, console.MFARecoveryCodeCount)
|
||||
_, user = updateContext()
|
||||
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.
|
||||
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.Empty(t, token)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
_, user = updateContext()
|
||||
}
|
||||
|
||||
_, err = service.ResetMFARecoveryCodes(authCtx)
|
||||
userCtx, _ = updateContext()
|
||||
_, err = service.ResetMFARecoveryCodes(userCtx)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@ -504,14 +505,14 @@ func TestMFA(t *testing.T) {
|
||||
badCode, err := console.NewMFAPasscode(key, time.Time{}.Add(time.Hour))
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
err = service.DisableUserMFA(authCtx, badCode, time.Time{}, "")
|
||||
userCtx, _ = updateContext()
|
||||
err = service.DisableUserMFA(userCtx, badCode, time.Time{}, "")
|
||||
require.True(t, console.ErrValidation.Has(err))
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.True(t, auth.User.MFAEnabled)
|
||||
require.NotEmpty(t, auth.User.MFASecretKey)
|
||||
require.NotEmpty(t, auth.User.MFARecoveryCodes)
|
||||
_, user = updateContext()
|
||||
require.True(t, user.MFAEnabled)
|
||||
require.NotEmpty(t, user.MFASecretKey)
|
||||
require.NotEmpty(t, user.MFARecoveryCodes)
|
||||
})
|
||||
|
||||
t.Run("TestDisableUserMFAConflict", func(t *testing.T) {
|
||||
@ -519,14 +520,14 @@ func TestMFA(t *testing.T) {
|
||||
goodCode, err := console.NewMFAPasscode(key, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
err = service.DisableUserMFA(authCtx, goodCode, time.Time{}, auth.User.MFARecoveryCodes[0])
|
||||
userCtx, user = updateContext()
|
||||
err = service.DisableUserMFA(userCtx, goodCode, time.Time{}, user.MFARecoveryCodes[0])
|
||||
require.True(t, console.ErrMFAConflict.Has(err))
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.True(t, auth.User.MFAEnabled)
|
||||
require.NotEmpty(t, auth.User.MFASecretKey)
|
||||
require.NotEmpty(t, auth.User.MFARecoveryCodes)
|
||||
_, user = updateContext()
|
||||
require.True(t, user.MFAEnabled)
|
||||
require.NotEmpty(t, user.MFASecretKey)
|
||||
require.NotEmpty(t, user.MFARecoveryCodes)
|
||||
})
|
||||
|
||||
t.Run("TestDisableUserMFAGoodPasscode", func(t *testing.T) {
|
||||
@ -534,46 +535,46 @@ func TestMFA(t *testing.T) {
|
||||
goodCode, err := console.NewMFAPasscode(key, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
err = service.DisableUserMFA(authCtx, goodCode, time.Time{}, "")
|
||||
userCtx, _ = updateContext()
|
||||
err = service.DisableUserMFA(userCtx, goodCode, time.Time{}, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.False(t, auth.User.MFAEnabled)
|
||||
require.Empty(t, auth.User.MFASecretKey)
|
||||
require.Empty(t, auth.User.MFARecoveryCodes)
|
||||
userCtx, user = updateContext()
|
||||
require.False(t, user.MFAEnabled)
|
||||
require.Empty(t, user.MFASecretKey)
|
||||
require.Empty(t, user.MFARecoveryCodes)
|
||||
})
|
||||
|
||||
t.Run("TestDisableUserMFAGoodRecoveryCode", func(t *testing.T) {
|
||||
// Expect MFA-disabling attempt to succeed when providing valid recovery code.
|
||||
// Enable MFA
|
||||
key, err = service.ResetMFASecretKey(authCtx)
|
||||
key, err = service.ResetMFASecretKey(userCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
goodCode, err := console.NewMFAPasscode(key, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
err = service.EnableUserMFA(authCtx, goodCode, time.Time{})
|
||||
userCtx, _ = updateContext()
|
||||
err = service.EnableUserMFA(userCtx, goodCode, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
_, err = service.ResetMFARecoveryCodes(authCtx)
|
||||
userCtx, _ = updateContext()
|
||||
_, err = service.ResetMFARecoveryCodes(userCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.True(t, auth.User.MFAEnabled)
|
||||
require.NotEmpty(t, auth.User.MFASecretKey)
|
||||
require.NotEmpty(t, auth.User.MFARecoveryCodes)
|
||||
userCtx, user = updateContext()
|
||||
require.True(t, user.MFAEnabled)
|
||||
require.NotEmpty(t, user.MFASecretKey)
|
||||
require.NotEmpty(t, user.MFARecoveryCodes)
|
||||
|
||||
// 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)
|
||||
|
||||
authCtx, auth = getNewAuthorization()
|
||||
require.False(t, auth.User.MFAEnabled)
|
||||
require.Empty(t, auth.User.MFASecretKey)
|
||||
require.Empty(t, auth.User.MFARecoveryCodes)
|
||||
_, user = updateContext()
|
||||
require.False(t, user.MFAEnabled)
|
||||
require.Empty(t, user.MFASecretKey)
|
||||
require.Empty(t, user.MFARecoveryCodes)
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -620,23 +621,18 @@ func TestResetPassword(t *testing.T) {
|
||||
token = getNewResetToken()
|
||||
|
||||
// Enable MFA.
|
||||
getNewAuthorization := func() (context.Context, console.Authorization) {
|
||||
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
auth, err := console.GetAuth(authCtx)
|
||||
require.NoError(t, err)
|
||||
return authCtx, auth
|
||||
}
|
||||
authCtx, _ := getNewAuthorization()
|
||||
|
||||
key, err := service.ResetMFASecretKey(authCtx)
|
||||
userCtx, err := sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
key, err := service.ResetMFASecretKey(userCtx)
|
||||
require.NoError(t, err)
|
||||
userCtx, err = sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
authCtx, auth := getNewAuthorization()
|
||||
|
||||
passcode, err := console.NewMFAPasscode(key, token.CreatedAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.EnableUserMFA(authCtx, passcode, token.CreatedAt)
|
||||
err = service.EnableUserMFA(userCtx, passcode, token.CreatedAt)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
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.
|
||||
err = service.ResetPassword(ctx, token.Secret.String(), newPass, badPasscode, recoveryCode, token.CreatedAt)
|
||||
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) {
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
||||
@ -709,23 +671,23 @@ func TestRESTKeys(t *testing.T) {
|
||||
user, err := service.GetUser(ctx, proj1.OwnerID)
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx, err := sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
now := time.Now()
|
||||
expires := 5 * time.Hour
|
||||
apiKey, expiresAt, err := service.CreateRESTKey(authCtx, expires)
|
||||
apiKey, expiresAt, err := service.CreateRESTKey(userCtx, expires)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, apiKey)
|
||||
require.True(t, expiresAt.After(now))
|
||||
require.True(t, expiresAt.Before(now.Add(expires+time.Hour)))
|
||||
|
||||
// test revocation
|
||||
require.NoError(t, service.RevokeRESTKey(authCtx, apiKey))
|
||||
require.NoError(t, service.RevokeRESTKey(userCtx, apiKey))
|
||||
|
||||
// test revoke non existent key
|
||||
nonexistent := testrand.UUID()
|
||||
err = service.RevokeRESTKey(authCtx, nonexistent.String())
|
||||
err = service.RevokeRESTKey(userCtx, nonexistent.String())
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
@ -748,23 +710,23 @@ func TestLockAccount(t *testing.T) {
|
||||
user, err := sat.AddUser(ctx, newUser, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
getNewAuthorization := func() (context.Context, console.Authorization) {
|
||||
authCtx, err := sat.AuthenticatedContext(ctx, user.ID)
|
||||
updateContext := func() (context.Context, *console.User) {
|
||||
userCtx, err := sat.UserContext(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
auth, err := console.GetAuth(authCtx)
|
||||
user, err := console.GetUser(userCtx)
|
||||
require.NoError(t, err)
|
||||
return authCtx, auth
|
||||
return userCtx, user
|
||||
}
|
||||
|
||||
authCtx, _ := getNewAuthorization()
|
||||
secret, err := service.ResetMFASecretKey(authCtx)
|
||||
userCtx, _ := updateContext()
|
||||
secret, err := service.ResetMFASecretKey(userCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
goodCode0, err := console.NewMFAPasscode(secret, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, _ = getNewAuthorization()
|
||||
err = service.EnableUserMFA(authCtx, goodCode0, time.Time{})
|
||||
userCtx, _ = updateContext()
|
||||
err = service.EnableUserMFA(userCtx, goodCode0, time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
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.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty)
|
||||
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.
|
||||
expDuration := time.Duration(math.Pow(consoleConfig.FailedLoginPenalty, float64(lockedUser.FailedLoginCount-1))) * time.Minute
|
||||
lockoutExpDate := now.Add(expDuration)
|
||||
err = service.UpdateUsersFailedLoginState(authCtx, lockedUser, lockoutExpDate)
|
||||
err = service.UpdateUsersFailedLoginState(userCtx, lockedUser, lockoutExpDate)
|
||||
require.NoError(t, err)
|
||||
|
||||
lockedUser, err = service.GetUser(authCtx, user.ID)
|
||||
lockedUser, err = service.GetUser(userCtx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty+1)
|
||||
|
||||
@ -815,7 +777,7 @@ func TestLockAccount(t *testing.T) {
|
||||
|
||||
// unlock account by successful login
|
||||
lockedUser.LoginLockoutExpiration = now.Add(-time.Second)
|
||||
err = usersDB.Update(authCtx, lockedUser)
|
||||
err = usersDB.Update(userCtx, lockedUser)
|
||||
require.NoError(t, err)
|
||||
|
||||
authUser.Password = newUser.FullName
|
||||
@ -823,7 +785,7 @@ func TestLockAccount(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, token)
|
||||
|
||||
unlockedUser, err := service.GetUser(authCtx, user.ID)
|
||||
unlockedUser, err := service.GetUser(userCtx, user.ID)
|
||||
require.NoError(t, err)
|
||||
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.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty)
|
||||
require.True(t, lockedUser.LoginLockoutExpiration.After(now))
|
||||
@ -847,7 +809,7 @@ func TestLockAccount(t *testing.T) {
|
||||
// unlock account
|
||||
lockedUser.LoginLockoutExpiration = time.Time{}
|
||||
lockedUser.FailedLoginCount = 0
|
||||
err = usersDB.Update(authCtx, lockedUser)
|
||||
err = usersDB.Update(userCtx, lockedUser)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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.True(t, lockedUser.FailedLoginCount == consoleConfig.LoginAttemptsWithoutPenalty)
|
||||
require.True(t, lockedUser.LoginLockoutExpiration.After(now))
|
||||
@ -882,3 +844,43 @@ func TestWalletJsonMarshall(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
@ -117,6 +117,8 @@ type AuthUser struct {
|
||||
Password string `json:"password"`
|
||||
MFAPasscode string `json:"mfaPasscode"`
|
||||
MFARecoveryCode string `json:"mfaRecoveryCode"`
|
||||
IP string `json:"-"`
|
||||
UserAgent string `json:"-"`
|
||||
}
|
||||
|
||||
// UserStatus - is used to indicate status of the users account.
|
||||
@ -193,3 +195,34 @@ type ResponseUser struct {
|
||||
MFAEnabled bool `json:"isMFAEnabled"`
|
||||
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")
|
||||
}
|
||||
|
@ -103,10 +103,10 @@ func TestBucketAttribution(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
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)
|
||||
|
||||
_, 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)
|
||||
|
||||
config := uplink.Config{
|
||||
@ -168,10 +168,10 @@ func TestQueryAttribution(t *testing.T) {
|
||||
satProject, err := satellite.AddProject(ctx, user.ID, "test")
|
||||
require.NoError(t, err)
|
||||
|
||||
authCtx, err := satellite.AuthenticatedContext(ctx, user.ID)
|
||||
userCtx, err := satellite.UserContext(ctx, user.ID)
|
||||
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)
|
||||
|
||||
access, err := uplink.RequestAccessWithPassphrase(ctx, satellite.NodeURL().String(), apiKeyInfo.Serialize(), "mypassphrase")
|
||||
|
@ -53,12 +53,12 @@ func NewEndpoint(
|
||||
svr := server.NewDefaultServer(manager)
|
||||
|
||||
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 {
|
||||
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
|
||||
|
@ -30,7 +30,6 @@ import (
|
||||
"storj.io/storj/private/testplanet"
|
||||
"storj.io/storj/satellite"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/oidc"
|
||||
"storj.io/uplink"
|
||||
)
|
||||
@ -122,18 +121,15 @@ func TestOIDC(t *testing.T) {
|
||||
activationToken, err := sat.API.Console.Service.GenerateActivationToken(ctx, user.ID, user.Email)
|
||||
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)
|
||||
|
||||
// Set up a test project and bucket
|
||||
|
||||
authed := console.WithAuth(ctx, console.Authorization{
|
||||
User: *user,
|
||||
Claims: consoleauth.Claims{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
})
|
||||
authed := console.WithUser(ctx, user)
|
||||
|
||||
project, err := sat.API.Console.Service.CreateProject(authed, console.ProjectInfo{
|
||||
Name: "test",
|
||||
@ -250,7 +246,7 @@ func TestOIDC(t *testing.T) {
|
||||
|
||||
{
|
||||
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)
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
"storj.io/common/macaroon"
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
ctx = console.WithAuth(ctx, console.Authorization{
|
||||
User: *user,
|
||||
Claims: consoleauth.Claims{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
})
|
||||
ctx = console.WithUser(ctx, user)
|
||||
|
||||
oauthClient := data.Client.(OAuthClient)
|
||||
name := oauthClient.AppName + " / " + oauthClient.ID.String()
|
||||
|
5
scripts/testdata/satellite-config.yaml.lock
vendored
5
scripts/testdata/satellite-config.yaml.lock
vendored
@ -85,7 +85,7 @@ compensation.rates.put-tb: "0"
|
||||
# comma separated monthly withheld percentage rates
|
||||
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
|
||||
|
||||
# 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
|
||||
# console.seo: "User-agent: *\nDisallow: \nDisallow: /cgi-bin/"
|
||||
|
||||
# duration a session is valid for
|
||||
# console.session-duration: 168h0m0s
|
||||
|
||||
# path to static resources
|
||||
# console.static-dir: ""
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user