diff --git a/pkg/api/auth/types.go b/pkg/api/auth/types.go index a5bfccf2bdf41f8b3bd7a02b2d0e31e16edc7632..258125ef3085e5c2e2f0eeec87cd1ba171f96cc6 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 ea79b299c450cecffdc1f8df838b401e02740171..e331c0658ac424bcb6e07e6a154d77768178870c 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 a46518494fe7de68835b1385761cba5c384c1f89..89f25f5af360a274973c378255dc6ef2e4eade14 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 91b6f845e045c131b17d5e9ec7688cdeff820cbe..3b275779198a07fe0b41ffd1d4bdb75fe4de8394 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 48fe961b16613f25a52df24a10b6a3adda572c34..2ae85f7c239ec0479fececd1af137b2e36e2df37 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 88a47e5858cfff2da1c99511c41ce189a76045c5..ed8b09647497a32bc223da8972bf8e000a18ab3f 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 c5bd5fdc4254ba9c29f79920bee29325cb392f51..26e7e0ec899f4731f87ed439b1c623d3f528302e 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 c189a885690877fe487c096297bd6c6ea3fc0bc4..7b4e1659cce0659526116c31f754113166221f3c 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 c5353dcb221c70cf04b70fda52f598592c83ba8e..20ca3fcfc1eb855087bb3f9a671a29542fe45164 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 651bde3563b657ff0bf616beaad62e308a92d1dd..2bde28d7a5db6956ca43bc5d57394f9b782af451 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 0c71fc4fca54697d3b14c165225efda5023ac8ca..abba5c093991c078608d35df6a9282a7f984d305 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 acd6cf820a7384d5423eebcf17bd4e1e59ea1708..907611c27fe43758aded0b5b0eab767bfef4b458 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 3f1607165aa9910a3c7698f7f42eb0e0d1f18245..1f6727de74fce6cdaaa4b129159e4d5ca1c300f7 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 ff60a6a00e075098c69642b9d9022b501621142f..0000000000000000000000000000000000000000 --- 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 6f729287f30c5e70616bf54fd63afc91a00f6944..4e08ddfa7e9869fa7b3256db34de478cea7ce728 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"` -}