diff --git a/go.mod b/go.mod index d430f14a1..920fb6630 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/go-openapi/strfmt v0.20.0 github.com/go-openapi/swag v0.19.14 github.com/go-openapi/validate v0.20.2 + github.com/golang-jwt/jwt/v4 v4.1.0 github.com/gorilla/websocket v1.4.2 github.com/jessevdk/go-flags v1.4.0 github.com/klauspost/compress v1.13.6 diff --git a/go.sum b/go.sum index 1bb37d67d..16927a0ab 100644 --- a/go.sum +++ b/go.sum @@ -487,6 +487,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0= +github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= diff --git a/restapi/user_session.go b/restapi/user_session.go index 5d3f12d66..b4354ee36 100644 --- a/restapi/user_session.go +++ b/restapi/user_session.go @@ -21,8 +21,10 @@ import ( "encoding/json" "net/http" "net/url" + "strconv" "time" + jwtgo "github.com/golang-jwt/jwt/v4" "github.com/minio/pkg/bucket/policy/condition" minioIAMPolicy "github.com/minio/pkg/iam/policy" @@ -70,6 +72,20 @@ func registerSessionHandlers(api *operations.ConsoleAPI) { }) } +func getClaimsFromToken(sessionToken string) (map[string]interface{}, error) { + jp := new(jwtgo.Parser) + jp.ValidMethods = []string{ + "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", + "RS3256", "RS3384", "RS3512", "ES3256", "ES3384", "ES3512", + } + var claims jwtgo.MapClaims + _, _, err := jp.ParseUnverified(sessionToken, &claims) + if err != nil { + return nil, err + } + return claims, nil +} + // getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI func getSessionResponse(session *models.Principal) (*models.SessionResponse, *models.Error) { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) @@ -104,12 +120,44 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo actions = acl.GetActionsStringFromPolicy(policy) } + currTime := time.Now().UTC() + // This actions will be global, meaning has to be attached to all resources conditionValues := map[string][]string{ condition.AWSUsername.Name(): {session.AccountAccessKey}, + // All calls to MinIO from console use temporary credentials. + condition.AWSPrincipalType.Name(): {"AssumeRole"}, + condition.AWSSecureTransport.Name(): {strconv.FormatBool(getMinIOEndpointIsSecure())}, + condition.AWSCurrentTime.Name(): {currTime.Format(time.RFC3339)}, + condition.AWSEpochTime.Name(): {strconv.FormatInt(currTime.Unix(), 10)}, + + // All calls from console are signature v4. + condition.S3SignatureVersion.Name(): {"AWS4-HMAC-SHA256"}, + // All calls from console are signature v4. + condition.S3AuthType.Name(): {"REST-HEADER"}, + // This is usually empty, may be set some times (rare). + condition.S3LocationConstraint.Name(): {GetMinIORegion()}, } + + claims, err := getClaimsFromToken(session.STSSessionToken) + if err != nil { + return nil, prepareError(err, errorGenericInvalidSession) + } + + // Support all LDAP, JWT variables + for k, v := range claims { + vstr, ok := v.(string) + if !ok { + // skip all non-strings + continue + } + // store all claims from sessionToken + conditionValues[k] = []string{vstr} + } + defaultActions := policy.IsAllowedActions("", "", conditionValues) consoleResourceName := "console-ui" + permissions := map[string]minioIAMPolicy.ActionSet{ consoleResourceName: defaultActions, }