refactor authentication (#1950)

This commit is contained in:
zryfish
2020-03-15 17:55:55 +08:00
committed by GitHub
parent abf0d66b22
commit eb8a3c0dc6
32 changed files with 522 additions and 381 deletions

View File

@@ -19,17 +19,16 @@ package iam
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/pkg/errors"
"golang.org/x/oauth2"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/iam"
"kubesphere.io/kubesphere/pkg/api/iam/token"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
"time"
)
@@ -42,12 +41,14 @@ type IdentityManagementInterface interface {
ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
GetUserRoles(username string) ([]*rbacv1.Role, error)
GetUserRole(namespace string, username string) (*rbacv1.Role, error)
VerifyToken(token string) (*iam.User, error)
}
type imOperator struct {
authenticateOptions *iam.AuthenticationOptions
ldapClient ldap.Interface
cacheClient cache.Interface
issuer token.Issuer
}
var (
@@ -57,7 +58,12 @@ var (
)
func NewIMOperator(ldapClient ldap.Interface, cacheClient cache.Interface, options *iam.AuthenticationOptions) *imOperator {
return &imOperator{ldapClient: ldapClient, cacheClient: cacheClient, authenticateOptions: options}
return &imOperator{
ldapClient: ldapClient,
cacheClient: cacheClient,
authenticateOptions: options,
issuer: token.NewJwtTokenIssuer(token.DefaultIssuerName, []byte(options.JwtSecret)),
}
}
@@ -78,18 +84,6 @@ func (im *imOperator) ModifyUser(user *iam.User) (*iam.User, error) {
return im.ldapClient.Get(user.Username)
}
func authenticationFailedKeyForUsername(username, failedTimestamp string) string {
return fmt.Sprintf("kubesphere:authfailed:%s:%s", username, failedTimestamp)
}
func tokenKeyForUsername(username, token string) string {
return fmt.Sprintf("kubesphere:users:%s:token:%s", username, token)
}
func loginKeyForUsername(username, loginTimestamp, ip string) string {
return fmt.Sprintf("kubesphere:users:%s:login-log:%s:%s", username, loginTimestamp, ip)
}
func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error) {
records, err := im.cacheClient.Keys(authenticationFailedKeyForUsername(username, "*"))
@@ -97,7 +91,7 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
return nil, err
}
if len(records) >= im.authenticateOptions.MaxAuthenticateRetries {
if len(records) > im.authenticateOptions.MaxAuthenticateRetries {
return nil, AuthRateLimitExceeded
}
@@ -114,15 +108,12 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
return nil, err
}
loginTime := time.Now()
// token without expiration time will auto sliding
claims := jwt.MapClaims{
"iat": loginTime.Unix(),
"username": user.Username,
"email": user.Email,
issuedToken, err := im.issuer.IssueTo(user)
if err != nil {
return nil, err
}
token := jwtutil.MustSigned(claims)
// TODO: I think we should come up with a better strategy to prevent multiple login.
tokenKey := tokenKeyForUsername(user.Username, "*")
if !im.authenticateOptions.MultipleLogin {
// multi login not allowed, remove the previous token
@@ -140,17 +131,17 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
}
}
// cache token with expiration time
if err = im.cacheClient.Set(tokenKey, token, im.authenticateOptions.TokenExpiration); err != nil {
// save token with expiration time
if err = im.cacheClient.Set(tokenKey, issuedToken, im.authenticateOptions.TokenExpiration); err != nil {
return nil, err
}
im.loginRecord(user.Username, ip, loginTime)
im.logLogin(user.Username, ip, time.Now())
return &oauth2.Token{AccessToken: token}, nil
return &oauth2.Token{AccessToken: issuedToken}, nil
}
func (im *imOperator) loginRecord(username, ip string, loginTime time.Time) {
func (im *imOperator) logLogin(username, ip string, loginTime time.Time) {
if ip != "" {
_ = im.cacheClient.Set(loginKeyForUsername(username, loginTime.UTC().Format("2006-01-02T15:04:05Z"), ip), "", 30*24*time.Hour)
}
@@ -174,7 +165,6 @@ func (im *imOperator) DescribeUser(username string) (*iam.User, error) {
}
func (im *imOperator) getLastLoginTime(username string) string {
return ""
}
@@ -191,6 +181,20 @@ func (im *imOperator) CreateUser(user *iam.User) (*iam.User, error) {
return user, nil
}
func (im *imOperator) VerifyToken(tokenString string) (*iam.User, error) {
providedUser, err := im.issuer.Verify(tokenString)
if err != nil {
return nil, err
}
user, err := im.ldapClient.Get(providedUser.Name())
if err != nil {
return nil, err
}
return user, nil
}
func (im *imOperator) uidNumberNext() int {
// TODO fix me
return 0
@@ -202,3 +206,15 @@ func (im *imOperator) GetUserRoles(username string) ([]*rbacv1.Role, error) {
func (im *imOperator) GetUserRole(namespace string, username string) (*rbacv1.Role, error) {
panic("implement me")
}
func authenticationFailedKeyForUsername(username, failedTimestamp string) string {
return fmt.Sprintf("kubesphere:authfailed:%s:%s", username, failedTimestamp)
}
func tokenKeyForUsername(username, token string) string {
return fmt.Sprintf("kubesphere:users:%s:token:%s", username, token)
}
func loginKeyForUsername(username, loginTimestamp, ip string) string {
return fmt.Sprintf("kubesphere:users:%s:login-log:%s:%s", username, loginTimestamp, ip)
}