Fixed broken oauth2 login for operator (#1217)

This PR includes many fixes and refactors for oauth2 authentication and
login endpoints, ie:

- Invalid login returns `403` instead of `500` error
- Removed the session token from console/operator `user credentials
  login`, `oauth flow login` and `change-password` api responses
- Removed session token from localStorage
- Added styles for oauth_callback page and display more descriptive
  errors for debugging
- Success logins returns `204` instead of `200`
- Removed unused swagger apis and code from both, operator and console
  projects
- Operator `Oauth2` login flow was not validating anything, now it does

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
Lenin Alevski
2021-11-11 14:46:14 -08:00
committed by GitHub
parent 0086aa8f64
commit 34dc51a579
33 changed files with 439 additions and 1462 deletions

View File

@@ -272,38 +272,6 @@ func init() {
}
}
}
},
"post": {
"security": [],
"tags": [
"UserAPI"
],
"summary": "Login to Console",
"operationId": "Login",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/loginRequest"
}
}
],
"responses": {
"201": {
"description": "A successful login.",
"schema": {
"$ref": "#/definitions/loginResponse"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/login/oauth2/auth": {
@@ -325,11 +293,8 @@ func init() {
}
],
"responses": {
"201": {
"description": "A successful login.",
"schema": {
"$ref": "#/definitions/loginResponse"
}
"204": {
"description": "A successful login."
},
"default": {
"description": "Generic error response.",
@@ -359,11 +324,8 @@ func init() {
}
],
"responses": {
"201": {
"description": "A successful login.",
"schema": {
"$ref": "#/definitions/loginResponse"
}
"204": {
"description": "A successful login."
},
"default": {
"description": "Generic error response.",
@@ -2215,14 +2177,6 @@ func init() {
}
}
},
"loginResponse": {
"type": "object",
"properties": {
"sessionId": {
"type": "string"
}
}
},
"maxAllocatableMemResponse": {
"type": "object",
"properties": {
@@ -3456,38 +3410,6 @@ func init() {
}
}
}
},
"post": {
"security": [],
"tags": [
"UserAPI"
],
"summary": "Login to Console",
"operationId": "Login",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/loginRequest"
}
}
],
"responses": {
"201": {
"description": "A successful login.",
"schema": {
"$ref": "#/definitions/loginResponse"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/login/oauth2/auth": {
@@ -3509,11 +3431,8 @@ func init() {
}
],
"responses": {
"201": {
"description": "A successful login.",
"schema": {
"$ref": "#/definitions/loginResponse"
}
"204": {
"description": "A successful login."
},
"default": {
"description": "Generic error response.",
@@ -3543,11 +3462,8 @@ func init() {
}
],
"responses": {
"201": {
"description": "A successful login.",
"schema": {
"$ref": "#/definitions/loginResponse"
}
"204": {
"description": "A successful login."
},
"default": {
"description": "Generic error response.",
@@ -6112,14 +6028,6 @@ func init() {
}
}
},
"loginResponse": {
"type": "object",
"properties": {
"sessionId": {
"type": "string"
}
}
},
"maxAllocatableMemResponse": {
"type": "object",
"properties": {

View File

@@ -123,9 +123,6 @@ func NewOperatorAPI(spec *loads.Document) *OperatorAPI {
OperatorAPIListTenantsHandler: operator_api.ListTenantsHandlerFunc(func(params operator_api.ListTenantsParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation operator_api.ListTenants has not yet been implemented")
}),
UserAPILoginHandler: user_api.LoginHandlerFunc(func(params user_api.LoginParams) middleware.Responder {
return middleware.NotImplemented("operation user_api.Login has not yet been implemented")
}),
UserAPILoginDetailHandler: user_api.LoginDetailHandlerFunc(func(params user_api.LoginDetailParams) middleware.Responder {
return middleware.NotImplemented("operation user_api.LoginDetail has not yet been implemented")
}),
@@ -269,8 +266,6 @@ type OperatorAPI struct {
OperatorAPIListPVCsForTenantHandler operator_api.ListPVCsForTenantHandler
// OperatorAPIListTenantsHandler sets the operation handler for the list tenants operation
OperatorAPIListTenantsHandler operator_api.ListTenantsHandler
// UserAPILoginHandler sets the operation handler for the login operation
UserAPILoginHandler user_api.LoginHandler
// UserAPILoginDetailHandler sets the operation handler for the login detail operation
UserAPILoginDetailHandler user_api.LoginDetailHandler
// UserAPILoginOauth2AuthHandler sets the operation handler for the login oauth2 auth operation
@@ -448,9 +443,6 @@ func (o *OperatorAPI) Validate() error {
if o.OperatorAPIListTenantsHandler == nil {
unregistered = append(unregistered, "operator_api.ListTenantsHandler")
}
if o.UserAPILoginHandler == nil {
unregistered = append(unregistered, "user_api.LoginHandler")
}
if o.UserAPILoginDetailHandler == nil {
unregistered = append(unregistered, "user_api.LoginDetailHandler")
}
@@ -683,10 +675,6 @@ func (o *OperatorAPI) initHandlerCache() {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/namespaces/{namespace}/tenants"] = operator_api.NewListTenants(o.context, o.OperatorAPIListTenantsHandler)
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)
}
o.handlers["POST"]["/login"] = user_api.NewLogin(o.context, o.UserAPILoginHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}

View File

@@ -1,73 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// LoginHandlerFunc turns a function with the right signature into a login handler
type LoginHandlerFunc func(LoginParams) middleware.Responder
// Handle executing the request and returning a response
func (fn LoginHandlerFunc) Handle(params LoginParams) middleware.Responder {
return fn(params)
}
// LoginHandler interface for that can handle valid login params
type LoginHandler interface {
Handle(LoginParams) middleware.Responder
}
// NewLogin creates a new http.Handler for the login operation
func NewLogin(ctx *middleware.Context, handler LoginHandler) *Login {
return &Login{Context: ctx, Handler: handler}
}
/* Login swagger:route POST /login UserAPI login
Login to Console
*/
type Login struct {
Context *middleware.Context
Handler LoginHandler
}
func (o *Login) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewLoginParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -30,48 +30,28 @@ import (
"github.com/minio/console/models"
)
// LoginOauth2AuthCreatedCode is the HTTP code returned for type LoginOauth2AuthCreated
const LoginOauth2AuthCreatedCode int = 201
// LoginOauth2AuthNoContentCode is the HTTP code returned for type LoginOauth2AuthNoContent
const LoginOauth2AuthNoContentCode int = 204
/*LoginOauth2AuthCreated A successful login.
/*LoginOauth2AuthNoContent A successful login.
swagger:response loginOauth2AuthCreated
swagger:response loginOauth2AuthNoContent
*/
type LoginOauth2AuthCreated struct {
/*
In: Body
*/
Payload *models.LoginResponse `json:"body,omitempty"`
type LoginOauth2AuthNoContent struct {
}
// NewLoginOauth2AuthCreated creates LoginOauth2AuthCreated with default headers values
func NewLoginOauth2AuthCreated() *LoginOauth2AuthCreated {
// NewLoginOauth2AuthNoContent creates LoginOauth2AuthNoContent with default headers values
func NewLoginOauth2AuthNoContent() *LoginOauth2AuthNoContent {
return &LoginOauth2AuthCreated{}
}
// WithPayload adds the payload to the login oauth2 auth created response
func (o *LoginOauth2AuthCreated) WithPayload(payload *models.LoginResponse) *LoginOauth2AuthCreated {
o.Payload = payload
return o
}
// SetPayload sets the payload to the login oauth2 auth created response
func (o *LoginOauth2AuthCreated) SetPayload(payload *models.LoginResponse) {
o.Payload = payload
return &LoginOauth2AuthNoContent{}
}
// WriteResponse to the client
func (o *LoginOauth2AuthCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
func (o *LoginOauth2AuthNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(201)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(204)
}
/*LoginOauth2AuthDefault Generic error response.

View File

@@ -30,48 +30,28 @@ import (
"github.com/minio/console/models"
)
// LoginOperatorCreatedCode is the HTTP code returned for type LoginOperatorCreated
const LoginOperatorCreatedCode int = 201
// LoginOperatorNoContentCode is the HTTP code returned for type LoginOperatorNoContent
const LoginOperatorNoContentCode int = 204
/*LoginOperatorCreated A successful login.
/*LoginOperatorNoContent A successful login.
swagger:response loginOperatorCreated
swagger:response loginOperatorNoContent
*/
type LoginOperatorCreated struct {
/*
In: Body
*/
Payload *models.LoginResponse `json:"body,omitempty"`
type LoginOperatorNoContent struct {
}
// NewLoginOperatorCreated creates LoginOperatorCreated with default headers values
func NewLoginOperatorCreated() *LoginOperatorCreated {
// NewLoginOperatorNoContent creates LoginOperatorNoContent with default headers values
func NewLoginOperatorNoContent() *LoginOperatorNoContent {
return &LoginOperatorCreated{}
}
// WithPayload adds the payload to the login operator created response
func (o *LoginOperatorCreated) WithPayload(payload *models.LoginResponse) *LoginOperatorCreated {
o.Payload = payload
return o
}
// SetPayload sets the payload to the login operator created response
func (o *LoginOperatorCreated) SetPayload(payload *models.LoginResponse) {
o.Payload = payload
return &LoginOperatorNoContent{}
}
// WriteResponse to the client
func (o *LoginOperatorCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
func (o *LoginOperatorNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(201)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(204)
}
/*LoginOperatorDefault Generic error response.

View File

@@ -1,102 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/validate"
"github.com/minio/console/models"
)
// NewLoginParams creates a new LoginParams object
//
// There are no default values defined in the spec.
func NewLoginParams() LoginParams {
return LoginParams{}
}
// LoginParams contains all the bound params for the login operation
// typically these are obtained from a http.Request
//
// swagger:parameters Login
type LoginParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.LoginRequest
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewLoginParams() beforehand.
func (o *LoginParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.LoginRequest
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body", ""))
} else {
res = append(res, errors.NewParseError("body", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
ctx := validate.WithOperationRequest(context.Background())
if err := body.ContextValidate(ctx, route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "body", ""))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -1,133 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/console/models"
)
// LoginCreatedCode is the HTTP code returned for type LoginCreated
const LoginCreatedCode int = 201
/*LoginCreated A successful login.
swagger:response loginCreated
*/
type LoginCreated struct {
/*
In: Body
*/
Payload *models.LoginResponse `json:"body,omitempty"`
}
// NewLoginCreated creates LoginCreated with default headers values
func NewLoginCreated() *LoginCreated {
return &LoginCreated{}
}
// WithPayload adds the payload to the login created response
func (o *LoginCreated) WithPayload(payload *models.LoginResponse) *LoginCreated {
o.Payload = payload
return o
}
// SetPayload sets the payload to the login created response
func (o *LoginCreated) SetPayload(payload *models.LoginResponse) {
o.Payload = payload
}
// WriteResponse to the client
func (o *LoginCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(201)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
/*LoginDefault Generic error response.
swagger:response loginDefault
*/
type LoginDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewLoginDefault creates LoginDefault with default headers values
func NewLoginDefault(code int) *LoginDefault {
if code <= 0 {
code = 500
}
return &LoginDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the login default response
func (o *LoginDefault) WithStatusCode(code int) *LoginDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the login default response
func (o *LoginDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the login default response
func (o *LoginDefault) WithPayload(payload *models.Error) *LoginDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the login default response
func (o *LoginDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *LoginDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -1,104 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// LoginURL generates an URL for the login operation
type LoginURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *LoginURL) WithBasePath(bp string) *LoginURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *LoginURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *LoginURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/login"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *LoginURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *LoginURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *LoginURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on LoginURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on LoginURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *LoginURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -17,7 +17,11 @@
package operatorapi
import (
"context"
"net/http"
"time"
xoauth2 "golang.org/x/oauth2"
"github.com/minio/minio-go/v7/pkg/credentials"
@@ -33,7 +37,7 @@ import (
)
func registerLoginHandlers(api *operations.OperatorAPI) {
// get login strategy
// GET login strategy
api.UserAPILoginDetailHandler = user_api.LoginDetailHandlerFunc(func(params user_api.LoginDetailParams) middleware.Responder {
loginDetails, err := getLoginDetailsResponse()
if err != nil {
@@ -41,31 +45,7 @@ func registerLoginHandlers(api *operations.OperatorAPI) {
}
return user_api.NewLoginDetailOK().WithPayload(loginDetails)
})
// post login
api.UserAPILoginHandler = user_api.LoginHandlerFunc(func(params user_api.LoginParams) middleware.Responder {
loginResponse, err := getLoginResponse(params.Body)
if err != nil {
return user_api.NewLoginDefault(int(err.Code)).WithPayload(err)
}
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
api.UserAPILoginOauth2AuthHandler = user_api.LoginOauth2AuthHandlerFunc(func(params user_api.LoginOauth2AuthParams) middleware.Responder {
loginResponse, err := getLoginOauth2AuthResponse()
if err != nil {
return user_api.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
}
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
// POST login using k8s service account token
api.UserAPILoginOperatorHandler = user_api.LoginOperatorHandlerFunc(func(params user_api.LoginOperatorParams) middleware.Responder {
loginResponse, err := getLoginOperatorResponse(params.Body)
if err != nil {
@@ -75,7 +55,20 @@ func registerLoginHandlers(api *operations.OperatorAPI) {
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginOperatorCreated().WithPayload(loginResponse).WriteResponse(w, p)
user_api.NewLoginOperatorNoContent().WriteResponse(w, p)
})
})
// POST login using external IDP
api.UserAPILoginOauth2AuthHandler = user_api.LoginOauth2AuthHandlerFunc(func(params user_api.LoginOauth2AuthParams) middleware.Responder {
loginResponse, err := getLoginOauth2AuthResponse(params.Body)
if err != nil {
return user_api.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
}
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginOauth2AuthNoContent().WriteResponse(w, p)
})
})
}
@@ -97,36 +90,6 @@ func login(credentials restapi.ConsoleCredentialsI) (*string, error) {
return &token, nil
}
// getConsoleCredentials will return consoleCredentials interface including the associated policy of the current account
func getConsoleCredentials(accessKey, secretKey string) (*restapi.ConsoleCredentials, error) {
creds, err := newConsoleCredentials(secretKey)
if err != nil {
return nil, err
}
return &restapi.ConsoleCredentials{
ConsoleCredentials: creds,
AccountAccessKey: accessKey,
}, nil
}
// getLoginResponse performs login() and serializes it to the handler's output
func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, *models.Error) {
// prepare console credentials
consolCreds, err := getConsoleCredentials(*lr.AccessKey, *lr.SecretKey)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
}
sessionID, err := login(consolCreds)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
}
// serialize output
loginResponse := &models.LoginResponse{
SessionID: *sessionID,
}
return loginResponse, nil
}
// getLoginDetailsResponse returns information regarding the Console authentication mechanism.
func getLoginDetailsResponse() (*models.LoginDetails, *models.Error) {
loginStrategy := models.LoginDetailsLoginStrategyServiceDashAccount
@@ -151,22 +114,48 @@ func getLoginDetailsResponse() (*models.LoginDetails, *models.Error) {
return loginDetails, nil
}
func getLoginOauth2AuthResponse() (*models.LoginResponse, *models.Error) {
// verifyUserAgainstIDP will verify user identity against the configured IDP and return MinIO credentials
func verifyUserAgainstIDP(ctx context.Context, provider auth.IdentityProviderI, code, state string) (*xoauth2.Token, error) {
oauth2Token, err := provider.VerifyIdentityForOperator(ctx, code, state)
if err != nil {
return nil, err
}
return oauth2Token, nil
}
creds, err := newConsoleCredentials(getK8sSAToken())
if err != nil {
return nil, prepareError(err)
func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.LoginResponse, *models.Error) {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
if oauth2.IsIDPEnabled() {
// initialize new oauth2 client
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, restapi.GetConsoleHTTPClient())
if err != nil {
return nil, prepareError(err)
}
// initialize new identity provider
identityProvider := auth.IdentityProvider{Client: oauth2Client}
// Validate user against IDP
_, err = verifyUserAgainstIDP(ctx, identityProvider, *lr.Code, *lr.State)
if err != nil {
return nil, prepareError(err)
}
// If we pass here that means the IDP correctly authenticate the user with the operator resource
// we proceed to use the service account token configured in the operator-console pod
creds, err := newConsoleCredentials(getK8sSAToken())
if err != nil {
return nil, prepareError(err)
}
token, err := login(restapi.ConsoleCredentials{ConsoleCredentials: creds})
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
}
// serialize output
loginResponse := &models.LoginResponse{
SessionID: *token,
}
return loginResponse, nil
}
consoleCredentials := restapi.ConsoleCredentials{ConsoleCredentials: creds}
token, err := login(consoleCredentials)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
}
// serialize output
loginResponse := &models.LoginResponse{
SessionID: *token,
}
return loginResponse, nil
return nil, prepareError(errorGeneric)
}
func newConsoleCredentials(secretKey string) (*credentials.Credentials, error) {