From 77a3722b4e4b91766b66da831a5eda7a8002287c Mon Sep 17 00:00:00 2001 From: hongming Date: Sun, 28 Jun 2020 11:18:25 +0800 Subject: [PATCH] fix: password modify Signed-off-by: hongming --- pkg/api/auth/types.go | 5 + .../im/fake_operator.go => api/iam/types.go} | 9 +- pkg/apiserver/apiserver.go | 9 +- .../options/authenticate_options.go | 9 +- pkg/apiserver/authentication/token/jwt.go | 8 +- pkg/apiserver/config/config_test.go | 1 - pkg/apiserver/filters/authentication.go | 3 +- .../namespace/namespace_controller.go | 3 +- pkg/kapis/iam/v1alpha2/handler.go | 185 +++++++++--------- pkg/kapis/iam/v1alpha2/register.go | 8 + pkg/kapis/oauth/handler.go | 63 ++++-- pkg/kapis/oauth/register.go | 15 ++ pkg/models/iam/im/im.go | 95 +++++++-- pkg/models/iam/im/ldap_operator.go | 91 --------- pkg/models/types.go | 7 - 15 files changed, 257 insertions(+), 254 deletions(-) rename pkg/{models/iam/im/fake_operator.go => api/iam/types.go} (77%) delete mode 100644 pkg/models/iam/im/ldap_operator.go diff --git a/pkg/api/auth/types.go b/pkg/api/auth/types.go index a5bfccf2..258125ef 100644 --- a/pkg/api/auth/types.go +++ b/pkg/api/auth/types.go @@ -38,6 +38,11 @@ type TokenReview struct { Status *Status `json:"status,omitempty" description:"token review status"` } +type LoginRequest struct { + Username string `json:"username" description:"username"` + Password string `json:"password" description:"password"` +} + func (request *TokenReview) Validate() error { if request.Spec == nil || request.Spec.Token == "" { return fmt.Errorf("token must not be null") diff --git a/pkg/models/iam/im/fake_operator.go b/pkg/api/iam/types.go similarity index 77% rename from pkg/models/iam/im/fake_operator.go rename to pkg/api/iam/types.go index ea79b299..e331c065 100644 --- a/pkg/models/iam/im/fake_operator.go +++ b/pkg/api/iam/types.go @@ -14,10 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -package im +package iam -import "kubesphere.io/kubesphere/pkg/simple/client/ldap" - -func NewFakeOperator() IdentityManagementInterface { - return NewLDAPOperator(ldap.NewSimpleLdap()) +type PasswordReset struct { + CurrentPassword string `json:"currentPassword"` + Password string `json:"password"` } diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index a4651849..89f25f5a 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -183,12 +183,11 @@ func (s *APIServer) installKubeSphereAPIs() { s.Config.MultiClusterOptions.ProxyPublishService, s.Config.MultiClusterOptions.ProxyPublishAddress, s.Config.MultiClusterOptions.AgentImage)) - urlruntime.Must(iamapi.AddToContainer(s.container, - im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory), + imOperator := im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory, s.Config.AuthenticationOptions) + urlruntime.Must(iamapi.AddToContainer(s.container, imOperator, am.NewOperator(s.InformerFactory, s.KubernetesClient.KubeSphere(), s.KubernetesClient.Kubernetes()), s.Config.AuthenticationOptions)) - urlruntime.Must(oauth.AddToContainer(s.container, - im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory), + urlruntime.Must(oauth.AddToContainer(s.container, imOperator, token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient), s.Config.AuthenticationOptions)) urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container)) @@ -275,7 +274,7 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) { // authenticators are unordered authn := unionauth.New(anonymous.NewAuthenticator(), - basictoken.New(basic.NewBasicAuthenticator(im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory))), + basictoken.New(basic.NewBasicAuthenticator(im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory, s.Config.AuthenticationOptions))), bearertoken.New(jwttoken.NewTokenAuthenticator(token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient)))) handler = filters.WithAuthentication(handler, authn) handler = filters.WithRequestInfo(handler, requestInfoResolver) diff --git a/pkg/apiserver/authentication/options/authenticate_options.go b/pkg/apiserver/authentication/options/authenticate_options.go index 91b6f845..3b275779 100644 --- a/pkg/apiserver/authentication/options/authenticate_options.go +++ b/pkg/apiserver/authentication/options/authenticate_options.go @@ -28,16 +28,11 @@ type AuthenticationOptions struct { // authenticate rate limit will AuthenticateRateLimiterMaxTries int `json:"authenticateRateLimiterMaxTries" yaml:"authenticateRateLimiterMaxTries"` AuthenticateRateLimiterDuration time.Duration `json:"authenticationRateLimiterDuration" yaml:"authenticationRateLimiterDuration"` - - // maximum retries when authenticate failed - MaxAuthenticateRetries int `json:"maxAuthenticateRetries" yaml:"maxAuthenticateRetries"` - // allow multiple users login at the same time MultipleLogin bool `json:"multipleLogin" yaml:"multipleLogin"` - // secret to signed jwt token JwtSecret string `json:"-" yaml:"jwtSecret"` - + // oauth options OAuthOptions *oauth.Options `json:"oauthOptions" yaml:"oauthOptions"` } @@ -45,7 +40,6 @@ func NewAuthenticateOptions() *AuthenticationOptions { return &AuthenticationOptions{ AuthenticateRateLimiterMaxTries: 5, AuthenticateRateLimiterDuration: time.Minute * 30, - MaxAuthenticateRetries: 0, OAuthOptions: oauth.NewOptions(), MultipleLogin: false, JwtSecret: "", @@ -64,7 +58,6 @@ func (options *AuthenticationOptions) Validate() []error { func (options *AuthenticationOptions) AddFlags(fs *pflag.FlagSet, s *AuthenticationOptions) { fs.IntVar(&options.AuthenticateRateLimiterMaxTries, "authenticate-rate-limiter-max-retries", s.AuthenticateRateLimiterMaxTries, "") fs.DurationVar(&options.AuthenticateRateLimiterDuration, "authenticate-rate-limiter-duration", s.AuthenticateRateLimiterDuration, "") - fs.IntVar(&options.MaxAuthenticateRetries, "authenticate-max-retries", s.MaxAuthenticateRetries, "") fs.BoolVar(&options.MultipleLogin, "multiple-login", s.MultipleLogin, "Allow multiple login with the same account, disable means only one user can login at the same time.") fs.StringVar(&options.JwtSecret, "jwt-secret", s.JwtSecret, "Secret to sign jwt token, must not be empty.") fs.DurationVar(&options.OAuthOptions.AccessTokenMaxAge, "access-token-max-age", s.OAuthOptions.AccessTokenMaxAge, "AccessTokenMaxAgeSeconds control the lifetime of access tokens, 0 means no expiration.") diff --git a/pkg/apiserver/authentication/token/jwt.go b/pkg/apiserver/authentication/token/jwt.go index 48fe961b..2ae85f7c 100644 --- a/pkg/apiserver/authentication/token/jwt.go +++ b/pkg/apiserver/authentication/token/jwt.go @@ -54,16 +54,14 @@ func (s *jwtTokenIssuer) Verify(tokenString string) (User, error) { } clm := &Claims{} - _, err := jwt.ParseWithClaims(tokenString, clm, s.keyFunc) - if err != nil { return nil, err } - // 0 means no expiration. - // validate token cache - if s.options.OAuthOptions.AccessTokenMaxAge > 0 { + // accessTokenMaxAge = 0 or token without expiration time means that the token will not expire + // do not validate token cache + if s.options.OAuthOptions.AccessTokenMaxAge > 0 && clm.ExpiresAt > 0 { _, err = s.cache.Get(tokenCacheKey(tokenString)) if err != nil { diff --git a/pkg/apiserver/config/config_test.go b/pkg/apiserver/config/config_test.go index 88a47e58..ed8b0964 100644 --- a/pkg/apiserver/config/config_test.go +++ b/pkg/apiserver/config/config_test.go @@ -121,7 +121,6 @@ func newTestConfig() (*Config, error) { AuthenticationOptions: &authoptions.AuthenticationOptions{ AuthenticateRateLimiterMaxTries: 5, AuthenticateRateLimiterDuration: 30 * time.Minute, - MaxAuthenticateRetries: 6, JwtSecret: "xxxxxx", MultipleLogin: false, OAuthOptions: &oauth.Options{ diff --git a/pkg/apiserver/filters/authentication.go b/pkg/apiserver/filters/authentication.go index c5bd5fdc..26e7e0ec 100644 --- a/pkg/apiserver/filters/authentication.go +++ b/pkg/apiserver/filters/authentication.go @@ -18,6 +18,7 @@ package filters import ( "errors" + "fmt" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -54,7 +55,7 @@ func WithAuthentication(handler http.Handler, auth authenticator.Request) http.H } gv := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} - responsewriters.ErrorNegotiated(apierrors.NewUnauthorized("Unauthorized"), s, gv, w, req) + responsewriters.ErrorNegotiated(apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized:%s", err)), s, gv, w, req) return } diff --git a/pkg/controller/namespace/namespace_controller.go b/pkg/controller/namespace/namespace_controller.go index c189a885..7b4e1659 100644 --- a/pkg/controller/namespace/namespace_controller.go +++ b/pkg/controller/namespace/namespace_controller.go @@ -205,7 +205,8 @@ func (r *ReconcileNamespace) bindWorkspace(namespace *corev1.Namespace) error { } func removeWorkspaceOwnerReferences(ownerReferences []metav1.OwnerReference) []metav1.OwnerReference { - for i, owner := range ownerReferences { + for i := 0; i < len(ownerReferences); i++ { + owner := ownerReferences[i] if owner.Kind == tenantv1alpha1.ResourceKindWorkspace { ownerReferences = append(ownerReferences[:i], ownerReferences[i+1:]...) i-- diff --git a/pkg/kapis/iam/v1alpha2/handler.go b/pkg/kapis/iam/v1alpha2/handler.go index c5353dcb..20ca3fcf 100644 --- a/pkg/kapis/iam/v1alpha2/handler.go +++ b/pkg/kapis/iam/v1alpha2/handler.go @@ -5,11 +5,16 @@ import ( "github.com/emicklei/go-restful" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apiserver/pkg/authentication/user" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/api/iam" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" "kubesphere.io/kubesphere/pkg/apiserver/query" + apirequest "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/im" servererr "kubesphere.io/kubesphere/pkg/server/errors" @@ -17,14 +22,16 @@ import ( ) type iamHandler struct { - am am.AccessManagementInterface - im im.IdentityManagementInterface + am am.AccessManagementInterface + im im.IdentityManagementInterface + authorizer authorizer.Authorizer } func newIAMHandler(im im.IdentityManagementInterface, am am.AccessManagementInterface, options *authoptions.AuthenticationOptions) *iamHandler { return &iamHandler{ - am: am, - im: im, + am: am, + im: im, + authorizer: authorizerfactory.NewRBACAuthorizer(am), } } @@ -37,24 +44,19 @@ func (h *iamHandler) DescribeUser(request *restful.Request, response *restful.Re username := request.PathParameter("user") user, err := h.im.DescribeUser(username) - if err != nil { api.HandleInternalError(response, request, err) return } globalRole, err := h.am.GetGlobalRoleOfUser(username) - if err != nil && !errors.IsNotFound(err) { api.HandleInternalError(response, request, err) return } if globalRole != nil { - if user.Annotations == nil { - user.Annotations = make(map[string]string, 0) - } - user.Annotations[iamv1alpha2.GlobalRoleAnnotation] = globalRole.Name + user = appendGlobalRoleAnnotation(user, globalRole.Name) } response.WriteEntity(user) @@ -66,7 +68,6 @@ func (h *iamHandler) RetrieveMemberRoleTemplates(request *restful.Request, respo username := request.PathParameter("user") globalRole, err := h.am.GetGlobalRoleOfUser(username) - if err != nil { // if role binding not exist return empty list if errors.IsNotFound(err) { @@ -206,20 +207,23 @@ func (h *iamHandler) ListUsers(request *restful.Request, response *restful.Respo } if globalRole != nil { - if user.Annotations == nil { - user.Annotations = make(map[string]string, 0) - } - user.Annotations[iamv1alpha2.GlobalRoleAnnotation] = globalRole.Name + user = appendGlobalRoleAnnotation(user, globalRole.Name) } result.Items[i] = user } response.WriteEntity(result) } -func (h *iamHandler) ListRoles(request *restful.Request, response *restful.Response) { +func appendGlobalRoleAnnotation(user *iamv1alpha2.User, globalRole string) *iamv1alpha2.User { + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + user.Annotations[iamv1alpha2.GlobalRoleAnnotation] = globalRole + return user +} +func (h *iamHandler) ListRoles(request *restful.Request, response *restful.Response) { namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -266,9 +270,7 @@ func (h *iamHandler) ListNamespaceMembers(request *restful.Request, response *re } queryParam.Filters[iamv1alpha2.ScopeNamespace] = query.Value(namespace) - result, err := h.im.ListUsers(queryParam) - if err != nil { api.HandleInternalError(response, request, err) return @@ -280,7 +282,6 @@ func (h *iamHandler) ListNamespaceMembers(request *restful.Request, response *re func (h *iamHandler) DescribeNamespaceMember(request *restful.Request, response *restful.Response) { username := request.PathParameter("member") namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -292,7 +293,6 @@ func (h *iamHandler) DescribeNamespaceMember(request *restful.Request, response queryParam.Filters[iamv1alpha2.ScopeNamespace] = query.Value(namespace) result, err := h.im.ListUsers(queryParam) - if err != nil { api.HandleInternalError(response, request, err) return @@ -334,7 +334,6 @@ func (h *iamHandler) ListWorkspaceMembers(request *restful.Request, response *re queryParam.Filters[iamv1alpha2.ScopeWorkspace] = query.Value(workspace) result, err := h.im.ListUsers(queryParam) - if err != nil { api.HandleInternalError(response, request, err) return @@ -352,7 +351,6 @@ func (h *iamHandler) DescribeWorkspaceMember(request *restful.Request, response queryParam.Filters[iamv1alpha2.ScopeWorkspace] = query.Value(workspace) result, err := h.im.ListUsers(queryParam) - if err != nil { api.HandleInternalError(response, request, err) return @@ -372,9 +370,7 @@ func (h *iamHandler) UpdateWorkspaceRole(request *restful.Request, response *res workspaceRoleName := request.PathParameter("workspacerole") var workspaceRole iamv1alpha2.WorkspaceRole - err := request.ReadEntity(&workspaceRole) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -389,7 +385,6 @@ func (h *iamHandler) UpdateWorkspaceRole(request *restful.Request, response *res } updated, err := h.am.CreateOrUpdateWorkspaceRole(workspace, &workspaceRole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -403,9 +398,7 @@ func (h *iamHandler) CreateWorkspaceRole(request *restful.Request, response *res workspace := request.PathParameter("workspace") var workspaceRole iamv1alpha2.WorkspaceRole - err := request.ReadEntity(&workspaceRole) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -413,7 +406,6 @@ func (h *iamHandler) CreateWorkspaceRole(request *restful.Request, response *res } created, err := h.am.CreateOrUpdateWorkspaceRole(workspace, &workspaceRole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -428,7 +420,6 @@ func (h *iamHandler) DeleteWorkspaceRole(request *restful.Request, response *res workspaceRoleName := request.PathParameter("workspacerole") err := h.am.DeleteWorkspaceRole(workspace, workspaceRoleName) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -510,22 +501,62 @@ func (h *iamHandler) UpdateUser(request *restful.Request, response *restful.Resp return } - if globalRole != "" { - if err := h.am.CreateGlobalRoleBinding(user.Name, globalRole); err != nil { + operator, ok := apirequest.UserFrom(request.Request.Context()) + + if globalRole != "" && ok { + err = h.updateGlobalRoleBinding(operator, updated, globalRole) + if err != nil { klog.Error(err) handleError(request, response, err) return } + updated = appendGlobalRoleAnnotation(updated, globalRole) } response.WriteEntity(updated) } +func (h *iamHandler) ModifyPassword(request *restful.Request, response *restful.Response) { + username := request.PathParameter("user") + + var passwordReset iam.PasswordReset + err := request.ReadEntity(&passwordReset) + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + operator, ok := apirequest.UserFrom(request.Request.Context()) + // change password by self + if ok && operator.GetName() == username { + _, err := h.im.Authenticate(username, passwordReset.CurrentPassword) + if err != nil { + if err == im.AuthFailedIncorrectPassword { + err = errors.NewForbidden(iamv1alpha2.Resource(iamv1alpha2.ResourcesSingularUser), username, err) + klog.Warning(err) + handleError(request, response, err) + return + } + klog.Error(err) + handleError(request, response, err) + return + } + } + + err = h.im.ModifyPassword(username, passwordReset.Password) + if err != nil { + klog.Error(err) + handleError(request, response, err) + return + } + response.WriteEntity(servererr.None) +} + func (h *iamHandler) DeleteUser(request *restful.Request, response *restful.Response) { username := request.PathParameter("user") err := h.im.DeleteUser(username) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -540,7 +571,6 @@ func (h *iamHandler) CreateGlobalRole(request *restful.Request, response *restfu var globalRole iamv1alpha2.GlobalRole err := request.ReadEntity(&globalRole) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -548,7 +578,6 @@ func (h *iamHandler) CreateGlobalRole(request *restful.Request, response *restfu } created, err := h.am.CreateOrUpdateGlobalRole(&globalRole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -562,7 +591,6 @@ func (h *iamHandler) DeleteGlobalRole(request *restful.Request, response *restfu globalRole := request.PathParameter("globalrole") err := h.am.DeleteGlobalRole(globalRole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -578,7 +606,6 @@ func (h *iamHandler) UpdateGlobalRole(request *restful.Request, response *restfu var globalRole iamv1alpha2.GlobalRole err := request.ReadEntity(&globalRole) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -593,7 +620,6 @@ func (h *iamHandler) UpdateGlobalRole(request *restful.Request, response *restfu } updated, err := h.am.CreateOrUpdateGlobalRole(&globalRole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -611,15 +637,12 @@ func (h *iamHandler) DescribeGlobalRole(request *restful.Request, response *rest handleError(request, response, err) return } - response.WriteEntity(globalRole) } func (h *iamHandler) CreateClusterRole(request *restful.Request, response *restful.Response) { var clusterRole rbacv1.ClusterRole - err := request.ReadEntity(&clusterRole) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -627,7 +650,6 @@ func (h *iamHandler) CreateClusterRole(request *restful.Request, response *restf } created, err := h.am.CreateOrUpdateClusterRole(&clusterRole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -641,7 +663,6 @@ func (h *iamHandler) DeleteClusterRole(request *restful.Request, response *restf clusterrole := request.PathParameter("clusterrole") err := h.am.DeleteClusterRole(clusterrole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -657,7 +678,6 @@ func (h *iamHandler) UpdateClusterRole(request *restful.Request, response *restf var clusterRole rbacv1.ClusterRole err := request.ReadEntity(&clusterRole) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -672,7 +692,6 @@ func (h *iamHandler) UpdateClusterRole(request *restful.Request, response *restf } updated, err := h.am.CreateOrUpdateClusterRole(&clusterRole) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -690,7 +709,6 @@ func (h *iamHandler) DescribeClusterRole(request *restful.Request, response *res handleError(request, response, err) return } - response.WriteEntity(clusterRole) } @@ -703,14 +721,12 @@ func (h *iamHandler) DescribeWorkspaceRole(request *restful.Request, response *r handleError(request, response, err) return } - response.WriteEntity(workspaceRole) } func (h *iamHandler) CreateNamespaceRole(request *restful.Request, response *restful.Response) { namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -718,9 +734,7 @@ func (h *iamHandler) CreateNamespaceRole(request *restful.Request, response *res } var role rbacv1.Role - err = request.ReadEntity(&role) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -728,7 +742,6 @@ func (h *iamHandler) CreateNamespaceRole(request *restful.Request, response *res } created, err := h.am.CreateOrUpdateNamespaceRole(namespace, &role) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -739,10 +752,9 @@ func (h *iamHandler) CreateNamespaceRole(request *restful.Request, response *res } func (h *iamHandler) DeleteNamespaceRole(request *restful.Request, response *restful.Response) { - role := request.PathParameter("role") - namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) + namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) if err != nil { klog.Error(err) handleError(request, response, err) @@ -750,7 +762,6 @@ func (h *iamHandler) DeleteNamespaceRole(request *restful.Request, response *res } err = h.am.DeleteNamespaceRole(namespace, role) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -761,10 +772,9 @@ func (h *iamHandler) DeleteNamespaceRole(request *restful.Request, response *res } func (h *iamHandler) UpdateNamespaceRole(request *restful.Request, response *restful.Response) { - roleName := request.PathParameter("role") - namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) + namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) if err != nil { klog.Error(err) handleError(request, response, err) @@ -772,9 +782,7 @@ func (h *iamHandler) UpdateNamespaceRole(request *restful.Request, response *res } var role rbacv1.Role - err = request.ReadEntity(&role) - if err != nil { klog.Errorf("%+v", err) api.HandleBadRequest(response, request, err) @@ -789,7 +797,6 @@ func (h *iamHandler) UpdateNamespaceRole(request *restful.Request, response *res } updated, err := h.am.CreateOrUpdateNamespaceRole(namespace, &role) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -803,9 +810,7 @@ func (h *iamHandler) CreateWorkspaceMembers(request *restful.Request, response * workspace := request.PathParameter("workspace") var members []Member - err := request.ReadEntity(&members) - if err != nil { klog.Error(err) api.HandleBadRequest(response, request, err) @@ -829,7 +834,6 @@ func (h *iamHandler) RemoveWorkspaceMember(request *restful.Request, response *r username := request.PathParameter("workspacemember") err := h.am.RemoveUserFromWorkspace(username, workspace) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -844,9 +848,7 @@ func (h *iamHandler) UpdateWorkspaceMember(request *restful.Request, response *r username := request.PathParameter("workspacemember") var member Member - err := request.ReadEntity(&member) - if err != nil { klog.Error(err) api.HandleBadRequest(response, request, err) @@ -873,7 +875,6 @@ func (h *iamHandler) UpdateWorkspaceMember(request *restful.Request, response *r func (h *iamHandler) CreateNamespaceMembers(request *restful.Request, response *restful.Response) { namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -881,9 +882,7 @@ func (h *iamHandler) CreateNamespaceMembers(request *restful.Request, response * } var members []Member - err = request.ReadEntity(&members) - if err != nil { klog.Error(err) api.HandleBadRequest(response, request, err) @@ -904,8 +903,8 @@ func (h *iamHandler) CreateNamespaceMembers(request *restful.Request, response * func (h *iamHandler) UpdateNamespaceMember(request *restful.Request, response *restful.Response) { username := request.PathParameter("member") - namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) + namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) if err != nil { klog.Error(err) handleError(request, response, err) @@ -913,9 +912,7 @@ func (h *iamHandler) UpdateNamespaceMember(request *restful.Request, response *r } var member Member - err = request.ReadEntity(&member) - if err != nil { klog.Error(err) api.HandleBadRequest(response, request, err) @@ -941,8 +938,8 @@ func (h *iamHandler) UpdateNamespaceMember(request *restful.Request, response *r func (h *iamHandler) RemoveNamespaceMember(request *restful.Request, response *restful.Response) { username := request.PathParameter("member") - namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) + namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) if err != nil { klog.Error(err) handleError(request, response, err) @@ -950,7 +947,6 @@ func (h *iamHandler) RemoveNamespaceMember(request *restful.Request, response *r } err = h.am.RemoveUserFromNamespace(username, namespace) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -964,7 +960,6 @@ func (h *iamHandler) CreateClusterMembers(request *restful.Request, response *re var members []Member err := request.ReadEntity(&members) - if err != nil { klog.Error(err) api.HandleBadRequest(response, request, err) @@ -987,7 +982,6 @@ func (h *iamHandler) RemoveClusterMember(request *restful.Request, response *res username := request.PathParameter("clustermember") err := h.am.RemoveUserFromCluster(username) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -1001,9 +995,7 @@ func (h *iamHandler) UpdateClusterMember(request *restful.Request, response *res username := request.PathParameter("clustermember") var member Member - err := request.ReadEntity(&member) - if err != nil { klog.Error(err) api.HandleBadRequest(response, request, err) @@ -1035,7 +1027,6 @@ func (h *iamHandler) DescribeClusterMember(request *restful.Request, response *r queryParam.Filters[iamv1alpha2.ScopeCluster] = "true" result, err := h.im.ListUsers(queryParam) - if err != nil { api.HandleInternalError(response, request, err) return @@ -1052,11 +1043,9 @@ func (h *iamHandler) DescribeClusterMember(request *restful.Request, response *r func (h *iamHandler) ListClusterMembers(request *restful.Request, response *restful.Response) { queryParam := query.ParseQueryParameter(request) - queryParam.Filters[iamv1alpha2.ScopeCluster] = "true" result, err := h.im.ListUsers(queryParam) - if err != nil { api.HandleInternalError(response, request, err) return @@ -1066,10 +1055,8 @@ func (h *iamHandler) ListClusterMembers(request *restful.Request, response *rest } func (h *iamHandler) DescribeNamespaceRole(request *restful.Request, response *restful.Response) { - roleName := request.PathParameter("role") namespace, err := h.resolveNamespace(request.PathParameter("namespace"), request.PathParameter("devops")) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -1077,7 +1064,6 @@ func (h *iamHandler) DescribeNamespaceRole(request *restful.Request, response *r } role, err := h.am.GetNamespaceRole(namespace, roleName) - if err != nil { klog.Error(err) handleError(request, response, err) @@ -1108,9 +1094,7 @@ func (h *iamHandler) PatchWorkspaceRole(request *restful.Request, response *rest } workspaceRole.Name = workspaceRoleName - patched, err := h.am.PatchWorkspaceRole(workspaceName, &workspaceRole) - if err != nil { handleError(request, response, err) return @@ -1131,9 +1115,7 @@ func (h *iamHandler) PatchGlobalRole(request *restful.Request, response *restful } globalRole.Name = globalRoleName - patched, err := h.am.PatchGlobalRole(&globalRole) - if err != nil { handleError(request, response, err) return @@ -1160,9 +1142,7 @@ func (h *iamHandler) PatchNamespaceRole(request *restful.Request, response *rest } role.Name = roleName - patched, err := h.am.PatchNamespaceRole(namespaceName, &role) - if err != nil { handleError(request, response, err) return @@ -1183,9 +1163,7 @@ func (h *iamHandler) PatchClusterRole(request *restful.Request, response *restfu } clusterRole.Name = clusterRoleName - patched, err := h.am.PatchClusterRole(&clusterRole) - if err != nil { handleError(request, response, err) return @@ -1194,6 +1172,31 @@ func (h *iamHandler) PatchClusterRole(request *restful.Request, response *restfu response.WriteEntity(patched) } +func (h *iamHandler) updateGlobalRoleBinding(operator user.Info, user *iamv1alpha2.User, globalRole string) error { + userManagement := authorizer.AttributesRecord{ + Resource: iamv1alpha2.ResourcesPluralUser, + Verb: "update", + ResourceScope: apirequest.GlobalScope, + ResourceRequest: true, + User: operator, + } + decision, _, err := h.authorizer.Authorize(userManagement) + if err != nil { + klog.Error(err) + return err + } + if decision != authorizer.DecisionAllow { + err = errors.NewForbidden(iamv1alpha2.Resource(iamv1alpha2.ResourcesSingularUser), user.Name, fmt.Errorf("update global role binding not allowed")) + klog.Warning(err) + return err + } + if err := h.am.CreateGlobalRoleBinding(user.Name, globalRole); err != nil { + klog.Error(err) + return err + } + return nil +} + func handleError(request *restful.Request, response *restful.Response, err error) { if errors.IsBadRequest(err) { api.HandleBadRequest(response, request, err) @@ -1201,6 +1204,8 @@ func handleError(request *restful.Request, response *restful.Response, err error api.HandleNotFound(response, request, err) } else if errors.IsAlreadyExists(err) { api.HandleConflict(response, request, err) + } else if errors.IsForbidden(err) { + api.HandleForbidden(response, request, err) } else { api.HandleInternalError(response, request, err) } diff --git a/pkg/kapis/iam/v1alpha2/register.go b/pkg/kapis/iam/v1alpha2/register.go index 651bde35..2bde28d7 100644 --- a/pkg/kapis/iam/v1alpha2/register.go +++ b/pkg/kapis/iam/v1alpha2/register.go @@ -22,6 +22,7 @@ import ( rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime/schema" "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/api/iam" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" "kubesphere.io/kubesphere/pkg/apiserver/runtime" @@ -62,6 +63,13 @@ func AddToContainer(container *restful.Container, im im.IdentityManagementInterf Param(ws.PathParameter("user", "username")). Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) + ws.Route(ws.PUT("/users/{user}/password"). + To(handler.ModifyPassword). + Doc("Modify user's password."). + Reads(iam.PasswordReset{}). + Param(ws.PathParameter("user", "username")). + Returns(http.StatusOK, api.StatusOK, errors.None). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/users/{user}"). To(handler.DescribeUser). Doc("Retrieve user details."). diff --git a/pkg/kapis/oauth/handler.go b/pkg/kapis/oauth/handler.go index 0c71fc4f..abba5c09 100644 --- a/pkg/kapis/oauth/handler.go +++ b/pkg/kapis/oauth/handler.go @@ -90,7 +90,6 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp redirectURI := req.QueryParameter("redirect_uri") conf, err := h.options.OAuthOptions.OAuthClient(clientId) - if err != nil { err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) resp.WriteError(http.StatusUnauthorized, err) @@ -109,14 +108,7 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp return } - expiresIn := h.options.OAuthOptions.AccessTokenMaxAge - - if conf.AccessTokenMaxAge != nil { - expiresIn = *conf.AccessTokenMaxAge - } - - accessToken, err := h.issuer.IssueTo(user, expiresIn) - + token, err := h.issueTo(user.GetName()) if err != nil { err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) resp.WriteError(http.StatusUnauthorized, err) @@ -131,12 +123,11 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp return } - redirectURL = fmt.Sprintf("%s#access_token=%s&token_type=Bearer", redirectURL, accessToken) + redirectURL = fmt.Sprintf("%s#access_token=%s&token_type=Bearer", redirectURL, token.AccessToken) - if expiresIn > 0 { - redirectURL = fmt.Sprintf("%s&expires_in=%v", redirectURL, expiresIn.Seconds()) + 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) } @@ -212,23 +203,57 @@ func (h *oauthHandler) OAuthCallBackHandler(req *restful.Request, resp *restful. return } + result, err := h.issueTo(user.GetName()) + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + return + } + resp.WriteEntity(result) +} + +func (h *oauthHandler) Login(request *restful.Request, response *restful.Response) { + var loginRequest auth.LoginRequest + + err := request.ReadEntity(&loginRequest) + if err != nil || loginRequest.Username == "" || loginRequest.Password == "" { + response.WriteHeaderAndEntity(http.StatusUnauthorized, fmt.Errorf("empty username or password")) + return + } + + user, err := h.im.Authenticate(loginRequest.Username, loginRequest.Password) + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + response.WriteError(http.StatusUnauthorized, err) + return + } + + result, err := h.issueTo(user.Name) + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + response.WriteError(http.StatusUnauthorized, err) + return + } + response.WriteEntity(result) +} + +func (h *oauthHandler) issueTo(username string) (*oauth.Token, error) { expiresIn := h.options.OAuthOptions.AccessTokenMaxAge accessToken, err := h.issuer.IssueTo(&authuser.DefaultInfo{ - Name: user.GetName(), + Name: username, }, expiresIn) if err != nil { - err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) - resp.WriteError(http.StatusUnauthorized, err) - return + klog.Error(err) + return nil, err } - result := oauth.Token{ + result := &oauth.Token{ AccessToken: accessToken, TokenType: "Bearer", ExpiresIn: int(expiresIn.Seconds()), } - resp.WriteEntity(result) + return result, nil } diff --git a/pkg/kapis/oauth/register.go b/pkg/kapis/oauth/register.go index acd6cf82..907611c2 100644 --- a/pkg/kapis/oauth/register.go +++ b/pkg/kapis/oauth/register.go @@ -90,5 +90,20 @@ func AddToContainer(c *restful.Container, im im.IdentityManagementInterface, iss c.Add(ws) + // legacy auth API + legacy := &restful.WebService{} + legacy.Path("/kapis/iam.kubesphere.io/v1alpha2/login"). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON) + legacy.Route(legacy.POST(""). + To(handler.Login). + Deprecate(). + Doc("KubeSphere APIs support token-based authentication via the Authtoken request header. The POST Login API is used to retrieve the authentication token. After the authentication token is obtained, it must be inserted into the Authtoken header for all requests."). + Reads(auth.LoginRequest{}). + Returns(http.StatusOK, api.StatusOK, oauth.Token{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) + + c.Add(legacy) + return nil } diff --git a/pkg/models/iam/im/im.go b/pkg/models/iam/im/im.go index 3f160716..1f6727de 100644 --- a/pkg/models/iam/im/im.go +++ b/pkg/models/iam/im/im.go @@ -22,11 +22,13 @@ import ( "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" "kubesphere.io/kubesphere/pkg/apiserver/query" kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" "kubesphere.io/kubesphere/pkg/informers" resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "net/mail" + "time" ) type IdentityManagementInterface interface { @@ -36,6 +38,7 @@ type IdentityManagementInterface interface { UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) DescribeUser(username string) (*iamv1alpha2.User, error) Authenticate(username, password string) (*iamv1alpha2.User, error) + ModifyPassword(username string, password string) error } var ( @@ -45,34 +48,66 @@ var ( UserNotExists = errors.New("user not exists") ) -func NewOperator(ksClient kubesphereclient.Interface, factory informers.InformerFactory) IdentityManagementInterface { - - return &defaultIMOperator{ +func NewOperator(ksClient kubesphereclient.Interface, factory informers.InformerFactory, options *authoptions.AuthenticationOptions) IdentityManagementInterface { + im := &defaultIMOperator{ ksClient: ksClient, resourceGetter: resourcev1alpha3.NewResourceGetter(factory), } - + if options != nil { + im.authenticateRateLimiterDuration = options.AuthenticateRateLimiterDuration + im.authenticateRateLimiterMaxTries = options.AuthenticateRateLimiterMaxTries + } + return im } type defaultIMOperator struct { - ksClient kubesphereclient.Interface - resourceGetter *resourcev1alpha3.ResourceGetter + ksClient kubesphereclient.Interface + resourceGetter *resourcev1alpha3.ResourceGetter + authenticateRateLimiterMaxTries int + authenticateRateLimiterDuration time.Duration } func (im *defaultIMOperator) UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", user.Name) - if err != nil { klog.Error(err) return nil, err } old := obj.(*iamv1alpha2.User).DeepCopy() + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation] = old.Annotations[iamv1alpha2.PasswordEncryptedAnnotation] user.Spec.EncryptedPassword = old.Spec.EncryptedPassword user.Status = old.Status - return im.ksClient.IamV1alpha2().Users().Update(user) + updated, err := im.ksClient.IamV1alpha2().Users().Update(user) + if err != nil { + klog.Error(err) + return nil, err + } + return ensurePasswordNotOutput(updated), nil +} + +func (im *defaultIMOperator) ModifyPassword(username string, password string) error { + obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username) + + if err != nil { + klog.Error(err) + return err + } + + user := obj.(*iamv1alpha2.User).DeepCopy() + delete(user.Annotations, iamv1alpha2.PasswordEncryptedAnnotation) + user.Spec.EncryptedPassword = password + + _, err = im.ksClient.IamV1alpha2().Users().Update(user) + if err != nil { + klog.Error(err) + return err + } + return nil } func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { @@ -80,26 +115,21 @@ func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alph var user *iamv1alpha2.User if _, err := mail.ParseAddress(username); err != nil { - obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username) - if err != nil { klog.Error(err) return nil, err } - user = obj.(*iamv1alpha2.User) } else { objs, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", &query.Query{ Pagination: query.NoPagination, Filters: map[query.Field]query.Value{iamv1alpha2.FieldEmail: query.Value(username)}, }) - if err != nil { klog.Error(err) return nil, err } - if len(objs.Items) != 1 { if len(objs.Items) == 0 { klog.Warningf("username or email: %s not exist", username) @@ -108,34 +138,36 @@ func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alph } return nil, AuthFailedIncorrectPassword } - user = objs.Items[0].(*iamv1alpha2.User) } + if im.authRateLimitExceeded(user) { + im.authFailRecord(user, AuthRateLimitExceeded) + return nil, AuthRateLimitExceeded + } + if checkPasswordHash(password, user.Spec.EncryptedPassword) { return user, nil } + im.authFailRecord(user, AuthFailedIncorrectPassword) return nil, AuthFailedIncorrectPassword } func (im *defaultIMOperator) ListUsers(query *query.Query) (result *api.ListResult, err error) { result, err = im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", query) - if err != nil { klog.Error(err) return nil, err } items := make([]interface{}, 0) - for _, item := range result.Items { user := item.(*iamv1alpha2.User) - items = append(items, ensurePasswordNotOutput(user)) + out := ensurePasswordNotOutput(user) + items = append(items, out) } - result.Items = items - return result, nil } @@ -146,14 +178,12 @@ func checkPasswordHash(password, hash string) bool { func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username) - if err != nil { klog.Error(err) return nil, err } user := obj.(*iamv1alpha2.User) - return ensurePasswordNotOutput(user), nil } @@ -170,6 +200,29 @@ func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.Us return user, nil } +func (im *defaultIMOperator) authRateLimitEnabled() bool { + if im.authenticateRateLimiterMaxTries <= 0 || im.authenticateRateLimiterDuration == 0 { + return false + } + return true +} + +func (im *defaultIMOperator) authRateLimitExceeded(user *iamv1alpha2.User) bool { + if !im.authRateLimitEnabled() { + return false + } + // TODO record login history using CRD + return false +} + +func (im *defaultIMOperator) authFailRecord(user *iamv1alpha2.User, err error) { + if !im.authRateLimitEnabled() { + return + } + + // TODO record login history using CRD +} + func ensurePasswordNotOutput(user *iamv1alpha2.User) *iamv1alpha2.User { out := user.DeepCopy() // ensure encrypted password will not be output diff --git a/pkg/models/iam/im/ldap_operator.go b/pkg/models/iam/im/ldap_operator.go deleted file mode 100644 index ff60a6a0..00000000 --- a/pkg/models/iam/im/ldap_operator.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2020 The KubeSphere Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package im - -import ( - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - "kubesphere.io/kubesphere/pkg/apiserver/query" - "kubesphere.io/kubesphere/pkg/simple/client/ldap" -) - -type ldapOperator struct { - ldapClient ldap.Interface -} - -func NewLDAPOperator(ldapClient ldap.Interface) IdentityManagementInterface { - return &ldapOperator{ - ldapClient: ldapClient, - } -} - -func (im *ldapOperator) UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { - - err := im.ldapClient.Update(user) - - if err != nil { - return nil, err - } - - return im.ldapClient.Get(user.Name) -} - -func (im *ldapOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { - - user, err := im.ldapClient.Get(username) - - if err != nil { - return nil, err - } - - err = im.ldapClient.Authenticate(user.Name, password) - if err != nil { - return nil, err - } - - return user, nil -} - -func (im *ldapOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { - return im.ldapClient.Get(username) -} - -func (im *ldapOperator) DeleteUser(username string) error { - return im.ldapClient.Delete(username) -} - -func (im *ldapOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { - err := im.ldapClient.Create(user) - - if err != nil { - return nil, err - } - - return user, nil -} - -func (im *ldapOperator) ListUsers(query *query.Query) (*api.ListResult, error) { - result, err := im.ldapClient.List(query) - - if err != nil { - klog.Error(err) - return nil, err - } - - return result, nil -} diff --git a/pkg/models/types.go b/pkg/models/types.go index 6f729287..4e08ddfa 100644 --- a/pkg/models/types.go +++ b/pkg/models/types.go @@ -43,10 +43,3 @@ type PodInfo struct { Pod string `json:"pod" description:"pod name"` Container string `json:"container" description:"container name"` } - -type AuthGrantResponse struct { - TokenType string `json:"token_type,omitempty"` - Token string `json:"access_token" description:"access token"` - ExpiresIn float64 `json:"expires_in,omitempty"` - RefreshToken string `json:"refresh_token,omitempty"` -} -- GitLab