implement identity provider and built-in oauth server

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-03-26 06:43:20 +08:00
parent 59002cd176
commit 9b9d4021ec
25 changed files with 637 additions and 1163 deletions

View File

@@ -26,18 +26,19 @@ import (
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/api/auth"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"net/http"
)
type oauthHandler struct {
issuer token.Issuer
config oauth.Configuration
issuer token.Issuer
options *authoptions.AuthenticationOptions
}
func newOAUTHHandler(issuer token.Issuer, config oauth.Configuration) *oauthHandler {
return &oauthHandler{issuer: issuer, config: config}
func newOAUTHHandler(issuer token.Issuer, options *authoptions.AuthenticationOptions) *oauthHandler {
return &oauthHandler{issuer: issuer, options: options}
}
// Implement webhook authentication interface
@@ -59,7 +60,7 @@ func (h *oauthHandler) TokenReviewHandler(req *restful.Request, resp *restful.Re
return
}
user, _, err := h.issuer.Verify(tokenReview.Spec.Token)
user, err := h.issuer.Verify(tokenReview.Spec.Token)
if err != nil {
klog.Errorln(err)
@@ -82,8 +83,9 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
user, ok := request.UserFrom(req.Request.Context())
clientId := req.QueryParameter("client_id")
responseType := req.QueryParameter("response_type")
redirectURI := req.QueryParameter("redirect_uri")
conf, err := h.config.Load(clientId)
conf, err := h.options.OAuthOptions.GetOAuthClient(clientId)
if err != nil {
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
@@ -103,7 +105,13 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
return
}
accessToken, clm, err := h.issuer.IssueTo(user)
expiresIn := h.options.OAuthOptions.AccessTokenMaxAgeSeconds
if conf.AccessTokenMaxAgeSeconds != nil {
expiresIn = *conf.AccessTokenMaxAgeSeconds
}
accessToken, err := h.issuer.IssueTo(user, expiresIn)
if err != nil {
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
@@ -111,11 +119,71 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
return
}
redirectURL := fmt.Sprintf("%s?access_token=%s&token_type=Bearer", conf.RedirectURL, accessToken)
expiresIn := clm.ExpiresAt - clm.IssuedAt
redirectURL, err := conf.ResolveRedirectURL(redirectURI)
if err != nil {
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
resp.WriteError(http.StatusUnauthorized, err)
return
}
redirectURL = fmt.Sprintf("%s#access_token=%s&token_type=Bearer", redirectURL, accessToken)
if expiresIn > 0 {
redirectURL = fmt.Sprintf("%s&expires_in=%v", redirectURL, expiresIn)
}
resp.Header().Set("Content-Type", "text/plain")
http.Redirect(resp, req.Request, redirectURL, http.StatusFound)
}
func (h *oauthHandler) OAuthCallBackHandler(req *restful.Request, resp *restful.Response) {
code := req.QueryParameter("code")
name := req.PathParameter("callback")
if code == "" {
err := apierrors.NewUnauthorized("Unauthorized: missing code")
resp.WriteError(http.StatusUnauthorized, err)
}
idP, err := h.options.OAuthOptions.GetIdentityProvider(name)
if err != nil {
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
resp.WriteError(http.StatusUnauthorized, err)
}
oauthIdentityProvider, err := idP.GetOAuth2IdentityProviderInstance()
if err != nil {
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
resp.WriteError(http.StatusUnauthorized, err)
}
user, err := oauthIdentityProvider.IdentityExchange(code)
if err != nil {
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
resp.WriteError(http.StatusUnauthorized, err)
}
expiresIn := h.options.OAuthOptions.AccessTokenMaxAgeSeconds
accessToken, err := h.issuer.IssueTo(user, expiresIn)
if err != nil {
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
resp.WriteError(http.StatusUnauthorized, err)
return
}
result := oauth.Token{
AccessToken: accessToken,
TokenType: "Bearer",
ExpiresIn: expiresIn,
}
resp.WriteEntity(result)
return
}

View File

@@ -23,19 +23,19 @@ import (
restfulspec "github.com/emicklei/go-restful-openapi"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/api/auth"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
"kubesphere.io/kubesphere/pkg/constants"
"net/http"
)
func AddToContainer(c *restful.Container, issuer token.Issuer, configuration oauth.Configuration) error {
func AddToContainer(c *restful.Container, issuer token.Issuer, options *authoptions.AuthenticationOptions) error {
ws := &restful.WebService{}
ws.Path("/oauth").
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
handler := newOAUTHHandler(issuer, configuration)
handler := newOAUTHHandler(issuer, options)
// Implement webhook authentication interface
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
@@ -46,16 +46,14 @@ func AddToContainer(c *restful.Container, issuer token.Issuer, configuration oau
Returns(http.StatusOK, api.StatusOK, auth.TokenReview{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
// TODO Built-in oauth2 server (provider)
// web console use 'Resource Owner Password Credentials Grant' or 'Client Credentials Grant' request for an OAuth token
// https://tools.ietf.org/html/rfc6749#section-4.3
// https://tools.ietf.org/html/rfc6749#section-4.4
// Only support implicit grant flow
// https://tools.ietf.org/html/rfc6749#section-4.2
// curl -u admin:P@88w0rd 'http://ks-apiserver.kubesphere-system.svc/oauth/authorize?client_id=kubesphere-console-client&response_type=token' -v
ws.Route(ws.GET("/authorize").
To(handler.AuthorizeHandler))
//ws.Route(ws.POST("/token"))
//ws.Route(ws.POST("/callback/{callback}"))
ws.Route(ws.GET("/callback/{callback}").
To(handler.OAuthCallBackHandler))
c.Add(ws)