Refactor session token (#1109)

- Update operator dependency
- Don't store policy on session token, instead obtain it during session
  validation

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
Lenin Alevski
2021-10-12 21:25:02 -07:00
committed by GitHub
parent 2cae87aaed
commit 410b4555e3
18 changed files with 108 additions and 290 deletions

View File

@@ -65,7 +65,6 @@ func configureAPI(api *operations.OperatorAPI) http.Handler {
}
return &models.Principal{
STSAccessKeyID: claims.STSAccessKeyID,
Actions: claims.Actions,
STSSecretAccessKey: claims.STSSecretAccessKey,
STSSessionToken: claims.STSSessionToken,
AccountAccessKey: claims.AccountAccessKey,

View File

@@ -17,23 +17,17 @@
package operatorapi
import (
"bytes"
"context"
"net/http"
"time"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/minio/console/restapi"
iampolicy "github.com/minio/pkg/iam/policy"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
"github.com/minio/console/operatorapi/operations"
"github.com/minio/console/operatorapi/operations/user_api"
"github.com/minio/console/pkg/acl"
"github.com/minio/console/pkg/auth"
"github.com/minio/console/pkg/auth/idp/oauth2"
)
@@ -55,10 +49,8 @@ func registerLoginHandlers(api *operations.OperatorAPI) {
}
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookies := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
for _, cookie := range cookies {
http.SetCookie(w, &cookie)
}
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
@@ -69,10 +61,8 @@ func registerLoginHandlers(api *operations.OperatorAPI) {
}
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookies := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
for _, cookie := range cookies {
http.SetCookie(w, &cookie)
}
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
@@ -83,10 +73,8 @@ func registerLoginHandlers(api *operations.OperatorAPI) {
}
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookies := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
for _, cookie := range cookies {
http.SetCookie(w, &cookie)
}
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginOperatorCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
@@ -101,7 +89,7 @@ func login(credentials restapi.ConsoleCredentialsI) (*string, error) {
return nil, err
}
// if we made it here, the consoleCredentials work, generate a jwt with claims
token, err := auth.NewEncryptedTokenForClient(&tokens, credentials.GetAccountAccessKey(), credentials.GetActions())
token, err := auth.NewEncryptedTokenForClient(&tokens, credentials.GetAccountAccessKey())
if err != nil {
LogError("error authenticating user: %v", err)
return nil, errInvalidCredentials
@@ -109,67 +97,22 @@ func login(credentials restapi.ConsoleCredentialsI) (*string, error) {
return &token, nil
}
// getAccountPolicy will return the associated policy of the current account
func getAccountPolicy(ctx context.Context, client restapi.MinioAdmin) (*iampolicy.Policy, error) {
// Obtain the current policy assigned to this user
// necessary for generating the list of allowed endpoints
accountInfo, err := client.AccountInfo(ctx)
if err != nil {
return nil, err
}
return iampolicy.ParseConfig(bytes.NewReader(accountInfo.Policy))
}
// getConsoleCredentials will return consoleCredentials interface including the associated policy of the current account
func getConsoleCredentials(ctx context.Context, accessKey, secretKey string) (*restapi.ConsoleCredentials, error) {
func getConsoleCredentials(accessKey, secretKey string) (*restapi.ConsoleCredentials, error) {
creds, err := newConsoleCredentials(secretKey)
if err != nil {
return nil, err
}
// cCredentials will be sts credentials, account credentials will be need it in the scenario the user wish
// to change its password
cCredentials := &restapi.ConsoleCredentials{
return &restapi.ConsoleCredentials{
ConsoleCredentials: creds,
AccountAccessKey: accessKey,
}
tokens, err := cCredentials.Get()
if err != nil {
return nil, err
}
// initialize admin client
mAdminClient, err := restapi.NewMinioAdminClient(&models.Principal{
STSAccessKeyID: tokens.AccessKeyID,
STSSecretAccessKey: tokens.SecretAccessKey,
STSSessionToken: tokens.SessionToken,
})
if err != nil {
return nil, err
}
userAdminClient := restapi.AdminClient{Client: mAdminClient}
// Obtain the current policy assigned to this user
// necessary for generating the list of allowed endpoints
policy, err := getAccountPolicy(ctx, userAdminClient)
if err != nil {
return nil, err
}
// by default every user starts with an empty array of available actions
// therefore we would have access only to pages that doesn't require any privilege
// ie: service-account page
var actions []string
// if a policy is assigned to this user we parse the actions from there
if policy != nil {
actions = acl.GetActionsStringFromPolicy(policy)
}
cCredentials.Actions = actions
return cCredentials, nil
}, nil
}
// getLoginResponse performs login() and serializes it to the handler's output
func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, *models.Error) {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
// prepare console credentials
consolCreds, err := getConsoleCredentials(ctx, *lr.AccessKey, *lr.SecretKey)
consolCreds, err := getConsoleCredentials(*lr.AccessKey, *lr.SecretKey)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
}
@@ -214,7 +157,7 @@ func getLoginOauth2AuthResponse() (*models.LoginResponse, *models.Error) {
if err != nil {
return nil, prepareError(err)
}
consoleCredentials := restapi.ConsoleCredentials{ConsoleCredentials: creds, Actions: []string{}}
consoleCredentials := restapi.ConsoleCredentials{ConsoleCredentials: creds}
token, err := login(consoleCredentials)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
@@ -240,7 +183,7 @@ func getLoginOperatorResponse(lmr *models.LoginOperatorRequest) (*models.LoginRe
if err != nil {
return nil, prepareError(err)
}
consoleCreds := restapi.ConsoleCredentials{ConsoleCredentials: creds, Actions: []string{}}
consoleCreds := restapi.ConsoleCredentials{ConsoleCredentials: creds}
token, err := login(consoleCreds)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)

View File

@@ -42,7 +42,7 @@ func getSessionResponse(session *models.Principal) (*models.OperatorSessionRespo
return nil, prepareError(errorGenericInvalidSession)
}
sessionResp := &models.OperatorSessionResponse{
Pages: acl.GetAuthorizedEndpoints(session.Actions),
Pages: acl.GetAuthorizedEndpoints([]string{}),
Features: getListOfEnabledFeatures(),
Status: models.OperatorSessionResponseStatusOk,
Operator: true,