diff --git a/cmd/api-errors.go b/cmd/api-errors.go index fdbe40af7..3ebbf42e9 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -2125,6 +2125,12 @@ func toAPIError(ctx context.Context, err error) APIError { HTTPStatusCode: e.Response().StatusCode, } // Add more Gateway SDKs here if any in future. + default: + apiErr = APIError{ + Code: apiErr.Code, + Description: fmt.Sprintf("%s: cause(%v)", apiErr.Description, err), + HTTPStatusCode: apiErr.HTTPStatusCode, + } } } diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index 1fceaad03..63d820130 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -20,7 +20,6 @@ import ( "context" "crypto/subtle" "encoding/json" - "encoding/xml" "errors" "fmt" "io" @@ -2202,54 +2201,61 @@ type LoginSTSArgs struct { Token string `json:"token" form:"token"` } +var errSTSNotInitialized = errors.New("STS API not initialized, please configure STS support") + // LoginSTS - STS user login handler. func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *LoginRep) error { ctx := newWebContext(r, args, "WebLoginSTS") - v := url.Values{} - v.Set("Action", webIdentity) - v.Set("WebIdentityToken", args.Token) - v.Set("Version", stsAPIVersion) - - scheme := "http" - if sourceScheme := handlers.GetSourceScheme(r); sourceScheme != "" { - scheme = sourceScheme - } - if globalIsTLS { - scheme = "https" + if globalOpenIDValidators == nil { + return toJSONError(ctx, errSTSNotInitialized) } - u := &url.URL{ - Scheme: scheme, - Host: r.Host, + v, err := globalOpenIDValidators.Get("jwt") + if err != nil { + logger.LogIf(ctx, err) + return toJSONError(ctx, errSTSNotInitialized) } - u.RawQuery = v.Encode() - - req, err := http.NewRequest(http.MethodPost, u.String(), nil) + m, err := v.Validate(args.Token, "") if err != nil { return toJSONError(ctx, err) } - clnt := &http.Client{ - Transport: NewGatewayHTTPTransport(), + // JWT has requested a custom claim with policy value set. + // This is a MinIO STS API specific value, this value should + // be set and configured on your identity provider as part of + // JWT custom claims. + var policyName string + policySet, ok := iampolicy.GetPoliciesFromClaims(m, iamPolicyClaimNameOpenID()) + if ok { + policyName = globalIAMSys.CurrentPolicies(strings.Join(policySet.ToSlice(), ",")) } - resp, err := clnt.Do(req) + if policyName == "" && globalPolicyOPA == nil { + return toJSONError(ctx, fmt.Errorf("%s claim missing from the JWT token, credentials will not be generated", iamPolicyClaimNameOpenID())) + } + m[iamPolicyClaimNameOpenID()] = policyName + + secret := globalActiveCred.SecretKey + cred, err := auth.GetNewCredentialsWithMetadata(m, secret) if err != nil { return toJSONError(ctx, err) } - defer xhttp.DrainBody(resp.Body) - if resp.StatusCode != http.StatusOK { - return toJSONError(ctx, errors.New(resp.Status)) + // Set the newly generated credentials. + if err = globalIAMSys.SetTempUser(cred.AccessKey, cred, policyName); err != nil { + return toJSONError(ctx, err) } - a := AssumeRoleWithWebIdentityResponse{} - if err = xml.NewDecoder(resp.Body).Decode(&a); err != nil { - return toJSONError(ctx, err) + // Notify all other MinIO peers to reload temp users + for _, nerr := range globalNotificationSys.LoadUser(cred.AccessKey, true) { + if nerr.Err != nil { + logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) + logger.LogIf(ctx, nerr.Err) + } } - reply.Token = a.Result.Credentials.SessionToken + reply.Token = cred.SessionToken reply.UIVersion = browser.UIVersion return nil } @@ -2304,6 +2310,8 @@ func toWebAPIError(ctx context.Context, err error) APIError { HTTPStatusCode: http.StatusBadRequest, Description: err.Error(), } + case errSTSNotInitialized: + return APIError(stsErrCodes.ToSTSErr(ErrSTSNotInitialized)) case errServerNotInitialized: return APIError{ Code: "XMinioServerNotInitialized",