Refactor authenticator
Signed-off-by: hongming <hongming@kubesphere.io>
This commit is contained in:
@@ -20,6 +20,15 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/form3tech-oss/jwt-go"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
|
||||
@@ -32,7 +41,6 @@ import (
|
||||
iamv1alpha2 "kubesphere.io/api/iam/v1alpha2"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||
"kubesphere.io/kubesphere/pkg/models/auth"
|
||||
@@ -41,8 +49,9 @@ import (
|
||||
|
||||
const (
|
||||
KindTokenReview = "TokenReview"
|
||||
passwordGrantType = "password"
|
||||
refreshTokenGrantType = "refresh_token"
|
||||
grantTypePassword = "password"
|
||||
grantTypeRefreshToken = "refresh_token"
|
||||
grantTypeCode = "code"
|
||||
)
|
||||
|
||||
type Spec struct {
|
||||
@@ -75,30 +84,30 @@ func (request *TokenReview) Validate() error {
|
||||
|
||||
type handler struct {
|
||||
im im.IdentityManagementInterface
|
||||
options *authoptions.AuthenticationOptions
|
||||
options *authentication.Options
|
||||
tokenOperator auth.TokenManagementInterface
|
||||
passwordAuthenticator auth.PasswordAuthenticator
|
||||
oauth2Authenticator auth.OAuthAuthenticator
|
||||
oauthAuthenticator auth.OAuthAuthenticator
|
||||
loginRecorder auth.LoginRecorder
|
||||
}
|
||||
|
||||
func newHandler(im im.IdentityManagementInterface,
|
||||
tokenOperator auth.TokenManagementInterface,
|
||||
passwordAuthenticator auth.PasswordAuthenticator,
|
||||
oauth2Authenticator auth.OAuthAuthenticator,
|
||||
oauthAuthenticator auth.OAuthAuthenticator,
|
||||
loginRecorder auth.LoginRecorder,
|
||||
options *authoptions.AuthenticationOptions) *handler {
|
||||
options *authentication.Options) *handler {
|
||||
return &handler{im: im,
|
||||
tokenOperator: tokenOperator,
|
||||
passwordAuthenticator: passwordAuthenticator,
|
||||
oauth2Authenticator: oauth2Authenticator,
|
||||
oauthAuthenticator: oauthAuthenticator,
|
||||
loginRecorder: loginRecorder,
|
||||
options: options}
|
||||
}
|
||||
|
||||
// Implement webhook authentication interface
|
||||
// tokenReview Implement webhook authentication interface
|
||||
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
|
||||
func (h *handler) TokenReview(req *restful.Request, resp *restful.Response) {
|
||||
func (h *handler) tokenReview(req *restful.Request, resp *restful.Response) {
|
||||
var tokenReview TokenReview
|
||||
|
||||
err := req.ReadEntity(&tokenReview)
|
||||
@@ -112,12 +121,13 @@ func (h *handler) TokenReview(req *restful.Request, resp *restful.Response) {
|
||||
return
|
||||
}
|
||||
|
||||
authenticated, err := h.tokenOperator.Verify(tokenReview.Spec.Token)
|
||||
verified, err := h.tokenOperator.Verify(tokenReview.Spec.Token)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
authenticated := verified.User
|
||||
success := TokenReview{APIVersion: tokenReview.APIVersion,
|
||||
Kind: KindTokenReview,
|
||||
Status: &Status{
|
||||
@@ -129,78 +139,144 @@ func (h *handler) TokenReview(req *restful.Request, resp *restful.Response) {
|
||||
resp.WriteEntity(success)
|
||||
}
|
||||
|
||||
func (h *handler) Authorize(req *restful.Request, resp *restful.Response) {
|
||||
authenticated, ok := request.UserFrom(req.Request.Context())
|
||||
clientId := req.QueryParameter("client_id")
|
||||
responseType := req.QueryParameter("response_type")
|
||||
redirectURI := req.QueryParameter("redirect_uri")
|
||||
// The Authorization Endpoint performs Authentication of the End-User.
|
||||
func (h *handler) authorize(req *restful.Request, response *restful.Response) {
|
||||
var scope, responseType, clientID, redirectURI, state, nonce string
|
||||
scope = req.QueryParameter("scope")
|
||||
clientID = req.QueryParameter("client_id")
|
||||
redirectURI = req.QueryParameter("redirect_uri")
|
||||
//prompt = req.QueryParameter("prompt")
|
||||
responseType = req.QueryParameter("response_type")
|
||||
state = req.QueryParameter("state")
|
||||
nonce = req.QueryParameter("nonce")
|
||||
|
||||
conf, err := h.options.OAuthOptions.OAuthClient(clientId)
|
||||
// Authorization Servers MUST support the use of the HTTP GET and POST methods
|
||||
// defined in RFC 2616 [RFC2616] at the Authorization Endpoint.
|
||||
if req.Request.Method == http.MethodPost {
|
||||
scope, _ = req.BodyParameter("scope")
|
||||
clientID, _ = req.BodyParameter("client_id")
|
||||
redirectURI, _ = req.BodyParameter("redirect_uri")
|
||||
responseType, _ = req.BodyParameter("response_type")
|
||||
state, _ = req.BodyParameter("state")
|
||||
nonce, _ = req.BodyParameter("nonce")
|
||||
}
|
||||
|
||||
oauthClient, err := h.options.OAuthOptions.OAuthClient(clientID)
|
||||
if err != nil {
|
||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||
api.HandleError(resp, req, err)
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidClient(err))
|
||||
return
|
||||
}
|
||||
|
||||
if responseType != "token" {
|
||||
err := apierrors.NewBadRequest(fmt.Sprintf("Response type %s is not supported", responseType))
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
err := apierrors.NewUnauthorized("Unauthorized")
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := h.tokenOperator.IssueTo(authenticated)
|
||||
redirectURL, err := oauthClient.ResolveRedirectURL(redirectURI)
|
||||
if err != nil {
|
||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||
api.HandleError(resp, req, err)
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL, err := conf.ResolveRedirectURL(redirectURI)
|
||||
authenticated, _ := request.UserFrom(req.Request.Context())
|
||||
if authenticated == nil || authenticated.GetName() == user.Anonymous {
|
||||
response.Header().Add("WWW-Authenticate", "Basic")
|
||||
response.WriteHeaderAndEntity(http.StatusUnauthorized, oauth.ErrorLoginRequired)
|
||||
return
|
||||
}
|
||||
|
||||
// If no openid scope value is present, the request may still be a valid OAuth 2.0 request,
|
||||
// but is not an OpenID Connect request.
|
||||
var scopes []string
|
||||
if scope != "" {
|
||||
scopes = strings.Split(scope, " ")
|
||||
}
|
||||
var responseTypes []string
|
||||
if responseType != "" {
|
||||
responseTypes = strings.Split(responseType, " ")
|
||||
}
|
||||
|
||||
// If the resource owner denies the access request or if the request
|
||||
// fails for reasons other than a missing or invalid redirection URI,
|
||||
// the authorization server informs the client by adding the following
|
||||
// parameters to the query component of the redirection URI using the
|
||||
// "application/x-www-form-urlencoded" format
|
||||
informsError := func(err oauth.Error) {
|
||||
values := make(url.Values, 0)
|
||||
values.Add("error", err.Type)
|
||||
if err.Description != "" {
|
||||
values.Add("error_description", err.Description)
|
||||
}
|
||||
if state != "" {
|
||||
values.Add("state", state)
|
||||
}
|
||||
redirectURL.RawQuery = values.Encode()
|
||||
http.Redirect(response.ResponseWriter, req.Request, redirectURL.String(), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Other scope values MAY be present.
|
||||
// Scope values used that are not understood by an implementation SHOULD be ignored.
|
||||
if !oauth.IsValidScopes(scopes) {
|
||||
klog.Warningf("Some requested scopes were invalid: %v", scopes)
|
||||
}
|
||||
|
||||
if !oauth.IsValidResponseTypes(responseTypes) {
|
||||
err := fmt.Errorf("Some requested response types were invalid")
|
||||
informsError(oauth.NewInvalidRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(hongming) support Hybrid Flow
|
||||
// Authorization Code Flow
|
||||
if responseType == oauth.ResponseCode {
|
||||
code, err := h.tokenOperator.IssueTo(&token.IssueRequest{
|
||||
User: authenticated,
|
||||
Claims: token.Claims{
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
Audience: []string{clientID},
|
||||
},
|
||||
TokenType: token.AuthorizationCode,
|
||||
Nonce: nonce,
|
||||
},
|
||||
// A maximum authorization code lifetime of 10 minutes is
|
||||
ExpiresIn: 10 * time.Minute,
|
||||
})
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
}
|
||||
values := redirectURL.Query()
|
||||
values.Add("code", code)
|
||||
redirectURL.RawQuery = values.Encode()
|
||||
http.Redirect(response, req.Request, redirectURL.String(), http.StatusFound)
|
||||
}
|
||||
|
||||
// Implicit Flow
|
||||
if responseType != oauth.ResponseToken {
|
||||
informsError(oauth.ErrorUnsupportedResponseType)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.issueTokenTo(authenticated)
|
||||
if err != nil {
|
||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
}
|
||||
|
||||
redirectURL = fmt.Sprintf("%s#access_token=%s&token_type=Bearer", redirectURL, token.AccessToken)
|
||||
|
||||
if token.ExpiresIn > 0 {
|
||||
redirectURL = fmt.Sprintf("%s&expires_in=%v", redirectURL, token.ExpiresIn)
|
||||
}
|
||||
resp.Header().Set("Content-Type", "text/plain")
|
||||
http.Redirect(resp, req.Request, redirectURL, http.StatusFound)
|
||||
values := make(url.Values, 0)
|
||||
values.Add("access_token", result.AccessToken)
|
||||
values.Add("refresh_token", result.RefreshToken)
|
||||
values.Add("token_type", result.TokenType)
|
||||
values.Add("expires_in", fmt.Sprint(result.ExpiresIn))
|
||||
redirectURL.Fragment = values.Encode()
|
||||
http.Redirect(response, req.Request, redirectURL.String(), http.StatusFound)
|
||||
}
|
||||
|
||||
func (h *handler) oauthCallback(req *restful.Request, resp *restful.Response) {
|
||||
func (h *handler) oauthCallback(req *restful.Request, response *restful.Response) {
|
||||
provider := req.PathParameter("callback")
|
||||
// OAuth2 callback, see also https://tools.ietf.org/html/rfc6749#section-4.1.2
|
||||
code := req.QueryParameter("code")
|
||||
// CAS callback, see also https://apereo.github.io/cas/6.3.x/protocol/CAS-Protocol-V2-Specification.html#25-servicevalidate-cas-20
|
||||
if code == "" {
|
||||
code = req.QueryParameter("ticket")
|
||||
}
|
||||
if code == "" {
|
||||
err := apierrors.NewUnauthorized("Unauthorized: missing code")
|
||||
api.HandleError(resp, req, err)
|
||||
authenticated, provider, err := h.oauthAuthenticator.Authenticate(req.Request.Context(), provider, req.Request)
|
||||
if err != nil {
|
||||
api.HandleUnauthorized(response, req, apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)))
|
||||
return
|
||||
}
|
||||
|
||||
authenticated, provider, err := h.oauth2Authenticator.Authenticate(provider, code)
|
||||
result, err := h.issueTokenTo(authenticated)
|
||||
if err != nil {
|
||||
api.HandleUnauthorized(resp, req, apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)))
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.tokenOperator.IssueTo(authenticated)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, apierrors.NewInternalError(err))
|
||||
return
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
}
|
||||
|
||||
requestInfo, _ := request.RequestInfoFrom(req.Request.Context())
|
||||
@@ -208,10 +284,10 @@ func (h *handler) oauthCallback(req *restful.Request, resp *restful.Response) {
|
||||
klog.Errorf("Failed to record successful login for user %s, error: %v", authenticated.GetName(), err)
|
||||
}
|
||||
|
||||
resp.WriteEntity(result)
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) Login(request *restful.Request, response *restful.Response) {
|
||||
func (h *handler) login(request *restful.Request, response *restful.Response) {
|
||||
var loginRequest LoginRequest
|
||||
err := request.ReadEntity(&loginRequest)
|
||||
if err != nil {
|
||||
@@ -221,29 +297,70 @@ func (h *handler) Login(request *restful.Request, response *restful.Response) {
|
||||
h.passwordGrant(loginRequest.Username, loginRequest.Password, request, response)
|
||||
}
|
||||
|
||||
func (h *handler) Token(req *restful.Request, response *restful.Response) {
|
||||
grantType, err := req.BodyParameter("grant_type")
|
||||
// To obtain an Access Token, an ID Token, and optionally a Refresh Token,
|
||||
// the RP (Client) sends a Token Request to the Token Endpoint to obtain a Token Response,
|
||||
// as described in Section 3.2 of OAuth 2.0 [RFC6749], when using the Authorization Code Flow.
|
||||
// Communication with the Token Endpoint MUST utilize TLS.
|
||||
func (h *handler) token(req *restful.Request, response *restful.Response) {
|
||||
// TODO(hongming) support basic auth
|
||||
// https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
|
||||
clientID, err := req.BodyParameter("client_id")
|
||||
if err != nil {
|
||||
api.HandleBadRequest(response, req, err)
|
||||
response.WriteHeaderAndEntity(http.StatusUnauthorized, oauth.NewInvalidClient(err))
|
||||
return
|
||||
}
|
||||
clientSecret, err := req.BodyParameter("client_secret")
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusUnauthorized, oauth.NewInvalidClient(err))
|
||||
return
|
||||
}
|
||||
|
||||
client, err := h.options.OAuthOptions.OAuthClient(clientID)
|
||||
if err != nil {
|
||||
oauthError := oauth.NewInvalidClient(err)
|
||||
response.WriteHeaderAndEntity(http.StatusUnauthorized, oauthError)
|
||||
return
|
||||
}
|
||||
|
||||
if client.Secret != clientSecret {
|
||||
oauthError := oauth.NewInvalidClient(fmt.Errorf("invalid client credential"))
|
||||
response.WriteHeaderAndEntity(http.StatusUnauthorized, oauthError)
|
||||
return
|
||||
}
|
||||
|
||||
grantType, err := req.BodyParameter("grant_type")
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
switch grantType {
|
||||
case passwordGrantType:
|
||||
case grantTypePassword:
|
||||
username, _ := req.BodyParameter("username")
|
||||
password, _ := req.BodyParameter("password")
|
||||
h.passwordGrant(username, password, req, response)
|
||||
break
|
||||
case refreshTokenGrantType:
|
||||
return
|
||||
case grantTypeRefreshToken:
|
||||
h.refreshTokenGrant(req, response)
|
||||
break
|
||||
return
|
||||
case grantTypeCode:
|
||||
h.codeGrant(req, response)
|
||||
return
|
||||
default:
|
||||
err := apierrors.NewBadRequest(fmt.Sprintf("Grant type %s is not supported", grantType))
|
||||
api.HandleBadRequest(response, req, err)
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.ErrorUnsupportedGrantType)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// passwordGrant handle Resource Owner Password Credentials Grant
|
||||
// for more details: https://datatracker.ietf.org/doc/html/rfc6749#section-4.3
|
||||
// The resource owner password credentials grant type is suitable in
|
||||
// cases where the resource owner has a trust relationship with the client,
|
||||
// such as the device operating system or a highly privileged application.
|
||||
// The authorization server should take special care when enabling this
|
||||
// grant type and only allow it when other flows are not viable.
|
||||
func (h *handler) passwordGrant(username string, password string, req *restful.Request, response *restful.Response) {
|
||||
authenticated, provider, err := h.passwordAuthenticator.Authenticate(username, password)
|
||||
authenticated, provider, err := h.passwordAuthenticator.Authenticate(req.Request.Context(), username, password)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case auth.IncorrectPasswordError:
|
||||
@@ -251,45 +368,79 @@ func (h *handler) passwordGrant(username string, password string, req *restful.R
|
||||
if err := h.loginRecorder.RecordLogin(username, iamv1alpha2.Token, provider, requestInfo.SourceIP, requestInfo.UserAgent, err); err != nil {
|
||||
klog.Errorf("Failed to record unsuccessful login attempt for user %s, error: %v", username, err)
|
||||
}
|
||||
api.HandleUnauthorized(response, req, apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)))
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidGrant(err))
|
||||
return
|
||||
case auth.RateLimitExceededError:
|
||||
api.HandleTooManyRequests(response, req, apierrors.NewTooManyRequestsError(fmt.Sprintf("Unauthorized: %s", err)))
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidGrant(err))
|
||||
return
|
||||
default:
|
||||
api.HandleInternalError(response, req, apierrors.NewInternalError(err))
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result, err := h.tokenOperator.IssueTo(authenticated)
|
||||
result, err := h.issueTokenTo(authenticated)
|
||||
if err != nil {
|
||||
api.HandleInternalError(response, req, apierrors.NewInternalError(err))
|
||||
return
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
}
|
||||
|
||||
requestInfo, _ := request.RequestInfoFrom(req.Request.Context())
|
||||
if err = h.loginRecorder.RecordLogin(authenticated.GetName(), iamv1alpha2.Token, provider, requestInfo.SourceIP, requestInfo.UserAgent, nil); err != nil {
|
||||
klog.Errorf("Failed to record successful login for user %s, error: %v", username, err)
|
||||
klog.Errorf("Failed to record successful login for user %s, error: %v", authenticated.GetName(), err)
|
||||
}
|
||||
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) issueTokenTo(authenticated user.Info) (*oauth.Token, error) {
|
||||
accessToken, err := h.tokenOperator.IssueTo(&token.IssueRequest{
|
||||
User: authenticated,
|
||||
Claims: token.Claims{TokenType: token.AccessToken},
|
||||
ExpiresIn: h.options.OAuthOptions.AccessTokenMaxAge,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refreshToken, err := h.tokenOperator.IssueTo(&token.IssueRequest{
|
||||
User: authenticated,
|
||||
Claims: token.Claims{TokenType: token.RefreshToken},
|
||||
ExpiresIn: h.options.OAuthOptions.AccessTokenMaxAge + h.options.OAuthOptions.AccessTokenInactivityTimeout,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := oauth.Token{
|
||||
AccessToken: accessToken,
|
||||
// The OAuth 2.0 token_type response parameter value MUST be Bearer,
|
||||
// as specified in OAuth 2.0 Bearer Token Usage [RFC6750]
|
||||
TokenType: "Bearer",
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: int(h.options.OAuthOptions.AccessTokenMaxAge.Seconds()),
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (h *handler) refreshTokenGrant(req *restful.Request, response *restful.Response) {
|
||||
refreshToken, err := req.BodyParameter("refresh_token")
|
||||
if err != nil {
|
||||
api.HandleBadRequest(response, req, apierrors.NewBadRequest(err.Error()))
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
authenticated, err := h.tokenOperator.Verify(refreshToken)
|
||||
verified, err := h.tokenOperator.Verify(refreshToken)
|
||||
if err != nil {
|
||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||
api.HandleUnauthorized(response, req, apierrors.NewUnauthorized(err.Error()))
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidGrant(err))
|
||||
return
|
||||
}
|
||||
|
||||
if verified.TokenType != token.RefreshToken {
|
||||
err = fmt.Errorf("ivalid token type %v want %v", verified.TokenType, token.RefreshToken)
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidGrant(err))
|
||||
return
|
||||
}
|
||||
|
||||
authenticated := verified.User
|
||||
// update token after registration
|
||||
if authenticated.GetName() == iamv1alpha2.PreRegistrationUser &&
|
||||
authenticated.GetExtra() != nil &&
|
||||
@@ -304,28 +455,71 @@ func (h *handler) refreshTokenGrant(req *restful.Request, response *restful.Resp
|
||||
iamv1alpha2.OriginUIDLabel: uid}).String()
|
||||
result, err := h.im.ListUsers(queryParam)
|
||||
if err != nil {
|
||||
api.HandleInternalError(response, req, apierrors.NewInternalError(err))
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
return
|
||||
}
|
||||
if len(result.Items) != 1 {
|
||||
err := apierrors.NewUnauthorized("authenticated user does not exist")
|
||||
api.HandleUnauthorized(response, req, apierrors.NewUnauthorized(err.Error()))
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidGrant(fmt.Errorf("authenticated user does not exist")))
|
||||
return
|
||||
}
|
||||
|
||||
authenticated = &user.DefaultInfo{Name: result.Items[0].(*iamv1alpha2.User).Name}
|
||||
}
|
||||
|
||||
result, err := h.tokenOperator.IssueTo(authenticated)
|
||||
result, err := h.issueTokenTo(authenticated)
|
||||
if err != nil {
|
||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||
api.HandleUnauthorized(response, req, apierrors.NewUnauthorized(err.Error()))
|
||||
return
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
}
|
||||
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) Logout(req *restful.Request, resp *restful.Response) {
|
||||
func (h *handler) codeGrant(req *restful.Request, response *restful.Response) {
|
||||
code, err := req.BodyParameter("code")
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidRequest(err))
|
||||
return
|
||||
}
|
||||
|
||||
authorizeContext, err := h.tokenOperator.Verify(code)
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidGrant(err))
|
||||
return
|
||||
}
|
||||
if authorizeContext.TokenType != token.AuthorizationCode {
|
||||
err = fmt.Errorf("ivalid token type %v want %v", authorizeContext.TokenType, token.AuthorizationCode)
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest, oauth.NewInvalidGrant(err))
|
||||
return
|
||||
}
|
||||
|
||||
// The client MUST NOT use the authorization code more than once.
|
||||
err = h.tokenOperator.Revoke(code)
|
||||
if err != nil {
|
||||
klog.Warningf("grant: failed to revoke authorization code: %v", err)
|
||||
}
|
||||
|
||||
authenticated := authorizeContext.User
|
||||
result, err := h.issueTokenTo(authenticated)
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, oauth.NewServerError(err))
|
||||
}
|
||||
|
||||
idToken, err := h.tokenOperator.IssueTo(&token.IssueRequest{
|
||||
User: authenticated,
|
||||
Claims: token.Claims{
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
Audience: authorizeContext.Audience,
|
||||
},
|
||||
Nonce: authorizeContext.Nonce,
|
||||
TokenType: token.IDToken,
|
||||
},
|
||||
ExpiresIn: h.options.OAuthOptions.AccessTokenMaxAge + h.options.OAuthOptions.AccessTokenInactivityTimeout,
|
||||
})
|
||||
result.IDToken = idToken
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) logout(req *restful.Request, resp *restful.Response) {
|
||||
authenticated, ok := request.UserFrom(req.Request.Context())
|
||||
if ok {
|
||||
if err := h.tokenOperator.RevokeAllUserTokens(authenticated.GetName()); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user