fix: Synchronize users to LDAP
Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
@@ -48,6 +48,7 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
|
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||||
@@ -60,6 +61,7 @@ func addControllers(
|
|||||||
informerFactory informers.InformerFactory,
|
informerFactory informers.InformerFactory,
|
||||||
devopsClient devops.Interface,
|
devopsClient devops.Interface,
|
||||||
s3Client s3.Interface,
|
s3Client s3.Interface,
|
||||||
|
ldapClient ldapclient.Interface,
|
||||||
openpitrixClient openpitrix.Client,
|
openpitrixClient openpitrix.Client,
|
||||||
multiClusterEnabled bool,
|
multiClusterEnabled bool,
|
||||||
networkPolicyEnabled bool,
|
networkPolicyEnabled bool,
|
||||||
@@ -207,8 +209,8 @@ func addControllers(
|
|||||||
}
|
}
|
||||||
|
|
||||||
userController := user.NewController(client.Kubernetes(), client.KubeSphere(), client.Config(),
|
userController := user.NewController(client.Kubernetes(), client.KubeSphere(), client.Config(),
|
||||||
kubesphereInformer.Iam().V1alpha2().Users(),
|
kubesphereInformer.Iam().V1alpha2().Users(), fedUserCache, fedUserCacheController,
|
||||||
fedUserCache, fedUserCacheController, kubernetesInformer.Core().V1().ConfigMaps(), multiClusterEnabled)
|
kubernetesInformer.Core().V1().ConfigMaps(), ldapClient, multiClusterEnabled)
|
||||||
|
|
||||||
csrController := certificatesigningrequest.NewController(client.Kubernetes(), kubernetesInformer.Certificates().V1beta1().CertificateSigningRequests(),
|
csrController := certificatesigningrequest.NewController(client.Kubernetes(), kubernetesInformer.Certificates().V1beta1().CertificateSigningRequests(),
|
||||||
kubernetesInformer.Core().V1().ConfigMaps(), client.Config())
|
kubernetesInformer.Core().V1().ConfigMaps(), client.Config())
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
|
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/multicluster"
|
"kubesphere.io/kubesphere/pkg/simple/client/multicluster"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/network"
|
"kubesphere.io/kubesphere/pkg/simple/client/network"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||||
@@ -21,6 +22,7 @@ type KubeSphereControllerManagerOptions struct {
|
|||||||
KubernetesOptions *k8s.KubernetesOptions
|
KubernetesOptions *k8s.KubernetesOptions
|
||||||
DevopsOptions *jenkins.Options
|
DevopsOptions *jenkins.Options
|
||||||
S3Options *s3.Options
|
S3Options *s3.Options
|
||||||
|
LdapOptions *ldapclient.Options
|
||||||
OpenPitrixOptions *openpitrix.Options
|
OpenPitrixOptions *openpitrix.Options
|
||||||
NetworkOptions *network.Options
|
NetworkOptions *network.Options
|
||||||
MultiClusterOptions *multicluster.Options
|
MultiClusterOptions *multicluster.Options
|
||||||
@@ -35,6 +37,7 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions
|
|||||||
KubernetesOptions: k8s.NewKubernetesOptions(),
|
KubernetesOptions: k8s.NewKubernetesOptions(),
|
||||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||||
S3Options: s3.NewS3Options(),
|
S3Options: s3.NewS3Options(),
|
||||||
|
LdapOptions: ldapclient.NewOptions(),
|
||||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||||
NetworkOptions: network.NewNetworkOptions(),
|
NetworkOptions: network.NewNetworkOptions(),
|
||||||
MultiClusterOptions: multicluster.NewOptions(),
|
MultiClusterOptions: multicluster.NewOptions(),
|
||||||
@@ -57,6 +60,7 @@ func (s *KubeSphereControllerManagerOptions) Flags() cliflag.NamedFlagSets {
|
|||||||
s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions)
|
s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions)
|
||||||
s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
|
s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
|
||||||
s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options)
|
s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options)
|
||||||
|
s.LdapOptions.AddFlags(fss.FlagSet("ldap"), s.LdapOptions)
|
||||||
s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
|
s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
|
||||||
s.NetworkOptions.AddFlags(fss.FlagSet("network"), s.NetworkOptions)
|
s.NetworkOptions.AddFlags(fss.FlagSet("network"), s.NetworkOptions)
|
||||||
s.MultiClusterOptions.AddFlags(fss.FlagSet("multicluster"), s.MultiClusterOptions)
|
s.MultiClusterOptions.AddFlags(fss.FlagSet("multicluster"), s.MultiClusterOptions)
|
||||||
@@ -92,6 +96,7 @@ func (s *KubeSphereControllerManagerOptions) Validate() []error {
|
|||||||
errs = append(errs, s.S3Options.Validate()...)
|
errs = append(errs, s.S3Options.Validate()...)
|
||||||
errs = append(errs, s.OpenPitrixOptions.Validate()...)
|
errs = append(errs, s.OpenPitrixOptions.Validate()...)
|
||||||
errs = append(errs, s.NetworkOptions.Validate()...)
|
errs = append(errs, s.NetworkOptions.Validate()...)
|
||||||
|
errs = append(errs, s.LdapOptions.Validate()...)
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
|
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
||||||
"kubesphere.io/kubesphere/pkg/utils/term"
|
"kubesphere.io/kubesphere/pkg/utils/term"
|
||||||
@@ -58,6 +59,7 @@ func NewControllerManagerCommand() *cobra.Command {
|
|||||||
KubernetesOptions: conf.KubernetesOptions,
|
KubernetesOptions: conf.KubernetesOptions,
|
||||||
DevopsOptions: conf.DevopsOptions,
|
DevopsOptions: conf.DevopsOptions,
|
||||||
S3Options: conf.S3Options,
|
S3Options: conf.S3Options,
|
||||||
|
LdapOptions: conf.LdapOptions,
|
||||||
OpenPitrixOptions: conf.OpenPitrixOptions,
|
OpenPitrixOptions: conf.OpenPitrixOptions,
|
||||||
NetworkOptions: conf.NetworkOptions,
|
NetworkOptions: conf.NetworkOptions,
|
||||||
MultiClusterOptions: conf.MultiClusterOptions,
|
MultiClusterOptions: conf.MultiClusterOptions,
|
||||||
@@ -114,6 +116,22 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ldapClient ldapclient.Interface
|
||||||
|
if s.LdapOptions == nil || len(s.LdapOptions.Host) == 0 {
|
||||||
|
klog.Errorf("Failed to create devops client invalid ldap options")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.LdapOptions.Host == ldapclient.FAKE_HOST {
|
||||||
|
ldapClient = ldapclient.NewSimpleLdap()
|
||||||
|
} else {
|
||||||
|
ldapClient, err = ldapclient.NewLdapClient(s.LdapOptions, stopCh)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to create ldap client %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var openpitrixClient openpitrix.Client
|
var openpitrixClient openpitrix.Client
|
||||||
if s.OpenPitrixOptions != nil && !s.OpenPitrixOptions.IsEmpty() {
|
if s.OpenPitrixOptions != nil && !s.OpenPitrixOptions.IsEmpty() {
|
||||||
openpitrixClient, err = openpitrix.NewClient(s.OpenPitrixOptions)
|
openpitrixClient, err = openpitrix.NewClient(s.OpenPitrixOptions)
|
||||||
@@ -160,7 +178,8 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
|||||||
|
|
||||||
// TODO(jeff): refactor config with CRD
|
// TODO(jeff): refactor config with CRD
|
||||||
servicemeshEnabled := s.ServiceMeshOptions != nil && len(s.ServiceMeshOptions.IstioPilotHost) != 0
|
servicemeshEnabled := s.ServiceMeshOptions != nil && len(s.ServiceMeshOptions.IstioPilotHost) != 0
|
||||||
if err = addControllers(mgr, kubernetesClient, informerFactory, devopsClient, s3Client, openpitrixClient, s.MultiClusterOptions.Enable, s.NetworkOptions.EnableNetworkPolicy, servicemeshEnabled, stopCh); err != nil {
|
if err = addControllers(mgr, kubernetesClient, informerFactory, devopsClient, s3Client, ldapClient, openpitrixClient,
|
||||||
|
s.MultiClusterOptions.Enable, s.NetworkOptions.EnableNetworkPolicy, servicemeshEnabled, stopCh); err != nil {
|
||||||
klog.Fatalf("unable to register controllers to the manager: %v", err)
|
klog.Fatalf("unable to register controllers to the manager: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||||
eventsclient "kubesphere.io/kubesphere/pkg/simple/client/events/elasticsearch"
|
eventsclient "kubesphere.io/kubesphere/pkg/simple/client/events/elasticsearch"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
|
||||||
esclient "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
|
esclient "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
|
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||||
@@ -53,7 +52,6 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
|||||||
s.AuthorizationOptions.AddFlags(fss.FlagSet("authorization"), s.AuthorizationOptions)
|
s.AuthorizationOptions.AddFlags(fss.FlagSet("authorization"), s.AuthorizationOptions)
|
||||||
s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
|
s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
|
||||||
s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions)
|
s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions)
|
||||||
s.LdapOptions.AddFlags(fss.FlagSet("ldap"), s.LdapOptions)
|
|
||||||
s.RedisOptions.AddFlags(fss.FlagSet("redis"), s.RedisOptions)
|
s.RedisOptions.AddFlags(fss.FlagSet("redis"), s.RedisOptions)
|
||||||
s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options)
|
s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options)
|
||||||
s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
|
s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
|
||||||
@@ -138,18 +136,6 @@ func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIS
|
|||||||
apiServer.SonarClient = sonarqube.NewSonar(sonarClient.SonarQube())
|
apiServer.SonarClient = sonarqube.NewSonar(sonarClient.SonarQube())
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.LdapOptions.Host != "" {
|
|
||||||
if s.LdapOptions.Host == fakeInterface && s.DebugMode {
|
|
||||||
apiServer.LdapClient = ldap.NewSimpleLdap()
|
|
||||||
} else {
|
|
||||||
ldapClient, err := ldap.NewLdapClient(s.LdapOptions, stopCh)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
apiServer.LdapClient = ldapClient
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheClient cache.Interface
|
var cacheClient cache.Interface
|
||||||
if s.RedisOptions.Host != "" {
|
if s.RedisOptions.Host != "" {
|
||||||
if s.RedisOptions.Host == fakeInterface && s.DebugMode {
|
if s.RedisOptions.Host == fakeInterface && s.DebugMode {
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/events"
|
"kubesphere.io/kubesphere/pkg/simple/client/events"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/logging"
|
"kubesphere.io/kubesphere/pkg/simple/client/logging"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
|
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||||
@@ -131,9 +130,6 @@ type APIServer struct {
|
|||||||
//
|
//
|
||||||
S3Client s3.Interface
|
S3Client s3.Interface
|
||||||
|
|
||||||
//
|
|
||||||
LdapClient ldap.Interface
|
|
||||||
|
|
||||||
SonarClient sonarqube.SonarInterface
|
SonarClient sonarqube.SonarInterface
|
||||||
|
|
||||||
EventsClient events.Client
|
EventsClient events.Client
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ import (
|
|||||||
userlister "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
userlister "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
"kubesphere.io/kubesphere/pkg/constants"
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
|
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
|
||||||
|
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||||
|
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -67,6 +69,7 @@ type Controller struct {
|
|||||||
cmSynced cache.InformerSynced
|
cmSynced cache.InformerSynced
|
||||||
fedUserCache cache.Store
|
fedUserCache cache.Store
|
||||||
fedUserController cache.Controller
|
fedUserController cache.Controller
|
||||||
|
ldapClient ldapclient.Interface
|
||||||
// workqueue is a rate limited work queue. This is used to queue work to be
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
// processed instead of performing it as soon as a change happens. This
|
// processed instead of performing it as soon as a change happens. This
|
||||||
// means we can ensure we only process a fixed amount of resources at a
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
@@ -81,7 +84,7 @@ type Controller struct {
|
|||||||
|
|
||||||
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface,
|
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface,
|
||||||
config *rest.Config, userInformer userinformer.UserInformer, fedUserCache cache.Store, fedUserController cache.Controller,
|
config *rest.Config, userInformer userinformer.UserInformer, fedUserCache cache.Store, fedUserController cache.Controller,
|
||||||
configMapInformer corev1informers.ConfigMapInformer, multiClusterEnabled bool) *Controller {
|
configMapInformer corev1informers.ConfigMapInformer, ldapClient ldapclient.Interface, multiClusterEnabled bool) *Controller {
|
||||||
// Create event broadcaster
|
// Create event broadcaster
|
||||||
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
// logged for sample-controller types.
|
// logged for sample-controller types.
|
||||||
@@ -106,6 +109,7 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
|
|||||||
cmSynced: configMapInformer.Informer().HasSynced,
|
cmSynced: configMapInformer.Informer().HasSynced,
|
||||||
fedUserCache: fedUserCache,
|
fedUserCache: fedUserCache,
|
||||||
fedUserController: fedUserController,
|
fedUserController: fedUserController,
|
||||||
|
ldapClient: ldapClient,
|
||||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Users"),
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Users"),
|
||||||
recorder: recorder,
|
recorder: recorder,
|
||||||
multiClusterEnabled: multiClusterEnabled,
|
multiClusterEnabled: multiClusterEnabled,
|
||||||
@@ -239,6 +243,48 @@ func (c *Controller) reconcile(key string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// name of your custom finalizer
|
||||||
|
finalizer := "finalizers.kubesphere.io/users"
|
||||||
|
|
||||||
|
if user.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||||
|
// The object is not being deleted, so if it does not have our finalizer,
|
||||||
|
// then lets add the finalizer and update the object.
|
||||||
|
if !sliceutil.HasString(user.Finalizers, finalizer) {
|
||||||
|
user.ObjectMeta.Finalizers = append(user.ObjectMeta.Finalizers, finalizer)
|
||||||
|
|
||||||
|
if user, err = c.ksClient.IamV1alpha2().Users().Update(user); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The object is being deleted
|
||||||
|
if sliceutil.HasString(user.ObjectMeta.Finalizers, finalizer) {
|
||||||
|
|
||||||
|
klog.V(4).Infof("delete user %s", key)
|
||||||
|
if err = c.ldapClient.Delete(key); err != nil && err != ldapclient.ErrUserNotExists {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove our finalizer from the list and update it.
|
||||||
|
user.Finalizers = sliceutil.RemoveString(user.ObjectMeta.Finalizers, func(item string) bool {
|
||||||
|
return item == finalizer
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := c.ksClient.IamV1alpha2().Users().Update(user); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our finalizer has finished, so the reconciler can do nothing.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.ldapSync(user); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if user, err = c.ensurePasswordIsEncrypted(user); err != nil {
|
if user, err = c.ensurePasswordIsEncrypted(user); err != nil {
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
@@ -269,9 +315,9 @@ func (c *Controller) Start(stopCh <-chan struct{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ensurePasswordIsEncrypted(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
|
func (c *Controller) ensurePasswordIsEncrypted(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
|
||||||
encrypted, err := strconv.ParseBool(user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation])
|
encrypted, _ := strconv.ParseBool(user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation])
|
||||||
// password is not encrypted
|
// password is not encrypted
|
||||||
if err != nil || !encrypted {
|
if !encrypted {
|
||||||
password, err := encrypt(user.Spec.EncryptedPassword)
|
password, err := encrypt(user.Spec.EncryptedPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
@@ -282,7 +328,6 @@ func (c *Controller) ensurePasswordIsEncrypted(user *iamv1alpha2.User) (*iamv1al
|
|||||||
if user.Annotations == nil {
|
if user.Annotations == nil {
|
||||||
user.Annotations = make(map[string]string, 0)
|
user.Annotations = make(map[string]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation] = "true"
|
user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation] = "true"
|
||||||
user.Status.State = iamv1alpha2.UserActive
|
user.Status.State = iamv1alpha2.UserActive
|
||||||
return c.ksClient.IamV1alpha2().Users().Update(user)
|
return c.ksClient.IamV1alpha2().Users().Update(user)
|
||||||
@@ -419,6 +464,25 @@ func (c *Controller) updateFederatedUser(fedUser *iamv1alpha2.FederatedUser) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ldapSync(user *iamv1alpha2.User) error {
|
||||||
|
encrypted, _ := strconv.ParseBool(user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation])
|
||||||
|
if encrypted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := c.ldapClient.Get(user.Name)
|
||||||
|
if err != nil {
|
||||||
|
if err == ldapclient.ErrUserNotExists {
|
||||||
|
klog.V(4).Infof("create user %s", user.Name)
|
||||||
|
return c.ldapClient.Create(user)
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
klog.V(4).Infof("update user %s", user.Name)
|
||||||
|
return c.ldapClient.Update(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func encrypt(password string) (string, error) {
|
func encrypt(password string) (string, error) {
|
||||||
// when user is already mapped to another identity, password is empty by default
|
// when user is already mapped to another identity, password is empty by default
|
||||||
// unable to log in directly until password reset
|
// unable to log in directly until password reset
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
|
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
|
||||||
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||||
|
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -81,6 +82,7 @@ func newUser(name string) *iamv1alpha2.User {
|
|||||||
func (f *fixture) newController() (*Controller, ksinformers.SharedInformerFactory, kubeinformers.SharedInformerFactory) {
|
func (f *fixture) newController() (*Controller, ksinformers.SharedInformerFactory, kubeinformers.SharedInformerFactory) {
|
||||||
f.ksclient = fake.NewSimpleClientset(f.objects...)
|
f.ksclient = fake.NewSimpleClientset(f.objects...)
|
||||||
f.k8sclient = k8sfake.NewSimpleClientset(f.kubeobjects...)
|
f.k8sclient = k8sfake.NewSimpleClientset(f.kubeobjects...)
|
||||||
|
ldapClient := ldapclient.NewSimpleLdap()
|
||||||
|
|
||||||
ksinformers := ksinformers.NewSharedInformerFactory(f.ksclient, noResyncPeriodFunc())
|
ksinformers := ksinformers.NewSharedInformerFactory(f.ksclient, noResyncPeriodFunc())
|
||||||
k8sinformers := kubeinformers.NewSharedInformerFactory(f.k8sclient, noResyncPeriodFunc())
|
k8sinformers := kubeinformers.NewSharedInformerFactory(f.k8sclient, noResyncPeriodFunc())
|
||||||
@@ -92,7 +94,7 @@ func (f *fixture) newController() (*Controller, ksinformers.SharedInformerFactor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c := NewController(f.k8sclient, f.ksclient, nil, ksinformers.Iam().V1alpha2().Users(), nil, nil, k8sinformers.Core().V1().ConfigMaps(), false)
|
c := NewController(f.k8sclient, f.ksclient, nil, ksinformers.Iam().V1alpha2().Users(), nil, nil, k8sinformers.Core().V1().ConfigMaps(), ldapClient, false)
|
||||||
c.userSynced = alwaysReady
|
c.userSynced = alwaysReady
|
||||||
c.recorder = &record.FakeRecorder{}
|
c.recorder = &record.FakeRecorder{}
|
||||||
|
|
||||||
@@ -208,6 +210,7 @@ func filterInformerActions(actions []core.Action) []core.Action {
|
|||||||
var ret []core.Action
|
var ret []core.Action
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if action.Matches("list", "users") ||
|
if action.Matches("list", "users") ||
|
||||||
|
action.Matches("list", "configmaps") ||
|
||||||
action.Matches("watch", "users") {
|
action.Matches("watch", "users") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -219,9 +222,14 @@ func filterInformerActions(actions []core.Action) []core.Action {
|
|||||||
|
|
||||||
func (f *fixture) expectUpdateUserStatusAction(user *iamv1alpha2.User) {
|
func (f *fixture) expectUpdateUserStatusAction(user *iamv1alpha2.User) {
|
||||||
expect := user.DeepCopy()
|
expect := user.DeepCopy()
|
||||||
|
expect.Finalizers = []string{"finalizers.kubesphere.io/users"}
|
||||||
|
action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "users"}, "", expect)
|
||||||
|
f.actions = append(f.actions, action)
|
||||||
|
|
||||||
|
expect = expect.DeepCopy()
|
||||||
expect.Status.State = iamv1alpha2.UserActive
|
expect.Status.State = iamv1alpha2.UserActive
|
||||||
expect.Annotations = map[string]string{iamv1alpha2.PasswordEncryptedAnnotation: "true"}
|
expect.Annotations = map[string]string{iamv1alpha2.PasswordEncryptedAnnotation: "true"}
|
||||||
action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "users"}, "", expect)
|
action = core.NewUpdateAction(schema.GroupVersionResource{Resource: "users"}, "", expect)
|
||||||
f.actions = append(f.actions, action)
|
f.actions = append(f.actions, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package ldap
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/go-ldap/ldap"
|
"github.com/go-ldap/ldap"
|
||||||
"github.com/google/uuid"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"kubesphere.io/kubesphere/pkg/api"
|
"kubesphere.io/kubesphere/pkg/api"
|
||||||
@@ -184,7 +183,7 @@ func (l *ldapInterfaceImpl) dnForUsername(username string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ldapInterfaceImpl) filterForUsername(username string) string {
|
func (l *ldapInterfaceImpl) filterForUsername(username string) string {
|
||||||
return fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", username, username)
|
return fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(%s=%s)(%s=%s)))", ldapAttributeUserID, username, ldapAttributeMail, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ldapInterfaceImpl) Get(name string) (*iamv1alpha2.User, error) {
|
func (l *ldapInterfaceImpl) Get(name string) (*iamv1alpha2.User, error) {
|
||||||
@@ -205,7 +204,6 @@ func (l *ldapInterfaceImpl) Get(name string) (*iamv1alpha2.User, error) {
|
|||||||
Attributes: []string{
|
Attributes: []string{
|
||||||
ldapAttributeMail,
|
ldapAttributeMail,
|
||||||
ldapAttributeDescription,
|
ldapAttributeDescription,
|
||||||
ldapAttributePreferredLanguage,
|
|
||||||
ldapAttributeCreateTimestamp,
|
ldapAttributeCreateTimestamp,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -215,7 +213,7 @@ func (l *ldapInterfaceImpl) Get(name string) (*iamv1alpha2.User, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(searchResults.Entries) != 1 {
|
if len(searchResults.Entries) == 0 {
|
||||||
return nil, ErrUserNotExists
|
return nil, ErrUserNotExists
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,28 +225,22 @@ func (l *ldapInterfaceImpl) Get(name string) (*iamv1alpha2.User, error) {
|
|||||||
},
|
},
|
||||||
Spec: iamv1alpha2.UserSpec{
|
Spec: iamv1alpha2.UserSpec{
|
||||||
Email: userEntry.GetAttributeValue(ldapAttributeMail),
|
Email: userEntry.GetAttributeValue(ldapAttributeMail),
|
||||||
Lang: userEntry.GetAttributeValue(ldapAttributePreferredLanguage),
|
|
||||||
Description: userEntry.GetAttributeValue(ldapAttributeDescription),
|
Description: userEntry.GetAttributeValue(ldapAttributeDescription),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
createTimestamp, _ := time.Parse(ldapAttributeCreateTimestampLayout, userEntry.GetAttributeValue(ldapAttributeCreateTimestamp))
|
createTimestamp, _ := time.Parse(ldapAttributeCreateTimestampLayout, userEntry.GetAttributeValue(ldapAttributeCreateTimestamp))
|
||||||
user.ObjectMeta.CreationTimestamp.Time = createTimestamp
|
user.ObjectMeta.CreationTimestamp.Time = createTimestamp
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ldapInterfaceImpl) Create(user *iamv1alpha2.User) error {
|
func (l *ldapInterfaceImpl) Create(user *iamv1alpha2.User) error {
|
||||||
if _, err := l.Get(user.Name); err != nil {
|
|
||||||
return ErrUserAlreadyExisted
|
|
||||||
}
|
|
||||||
|
|
||||||
createRequest := &ldap.AddRequest{
|
createRequest := &ldap.AddRequest{
|
||||||
DN: l.dnForUsername(user.Name),
|
DN: l.dnForUsername(user.Name),
|
||||||
Attributes: []ldap.Attribute{
|
Attributes: []ldap.Attribute{
|
||||||
{
|
{
|
||||||
Type: ldapAttributeObjectClass,
|
Type: ldapAttributeObjectClass,
|
||||||
Vals: []string{"inetOrgPerson", "posixAccount", "top"},
|
Vals: []string{"inetOrgPerson", "top"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: ldapAttributeCommonName,
|
Type: ldapAttributeCommonName,
|
||||||
@@ -256,40 +248,12 @@ func (l *ldapInterfaceImpl) Create(user *iamv1alpha2.User) error {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: ldapAttributeSerialNumber,
|
Type: ldapAttributeSerialNumber,
|
||||||
Vals: []string{" "},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: ldapAttributeGlobalIDNumber,
|
|
||||||
Vals: []string{"500"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: ldapAttributeHomeDirectory,
|
|
||||||
Vals: []string{"/home/" + user.Name},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: ldapAttributeUserID,
|
|
||||||
Vals: []string{user.Name},
|
Vals: []string{user.Name},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: ldapAttributeUserIDNumber,
|
|
||||||
Vals: []string{uuid.New().String()},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: ldapAttributeMail,
|
|
||||||
Vals: []string{user.Spec.Email},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Type: ldapAttributeUserPassword,
|
Type: ldapAttributeUserPassword,
|
||||||
Vals: []string{user.Spec.EncryptedPassword},
|
Vals: []string{user.Spec.EncryptedPassword},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: ldapAttributePreferredLanguage,
|
|
||||||
Vals: []string{user.Spec.Lang},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: ldapAttributeDescription,
|
|
||||||
Vals: []string{user.Spec.Description},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,14 +305,6 @@ func (l *ldapInterfaceImpl) Update(newUser *iamv1alpha2.User) error {
|
|||||||
DN: l.dnForUsername(newUser.Name),
|
DN: l.dnForUsername(newUser.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
if newUser.Spec.Description != "" {
|
|
||||||
modifyRequest.Replace(ldapAttributeDescription, []string{newUser.Spec.Description})
|
|
||||||
}
|
|
||||||
|
|
||||||
if newUser.Spec.Lang != "" {
|
|
||||||
modifyRequest.Replace(ldapAttributePreferredLanguage, []string{newUser.Spec.Lang})
|
|
||||||
}
|
|
||||||
|
|
||||||
if newUser.Spec.EncryptedPassword != "" {
|
if newUser.Spec.EncryptedPassword != "" {
|
||||||
modifyRequest.Replace(ldapAttributeUserPassword, []string{newUser.Spec.EncryptedPassword})
|
modifyRequest.Replace(ldapAttributeUserPassword, []string{newUser.Spec.EncryptedPassword})
|
||||||
}
|
}
|
||||||
@@ -385,55 +341,37 @@ func (l *ldapInterfaceImpl) List(query *query.Query) (*api.ListResult, error) {
|
|||||||
|
|
||||||
filter := "(&(objectClass=inetOrgPerson))"
|
filter := "(&(objectClass=inetOrgPerson))"
|
||||||
|
|
||||||
if keyword := query.Filters["keyword"]; keyword != "" {
|
|
||||||
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword)
|
|
||||||
}
|
|
||||||
|
|
||||||
if username := query.Filters["username"]; username != "" {
|
|
||||||
uidFilter := ""
|
|
||||||
for _, username := range strings.Split(string(username), "|") {
|
|
||||||
uidFilter += fmt.Sprintf("(uid=%s)", username)
|
|
||||||
}
|
|
||||||
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", uidFilter)
|
|
||||||
}
|
|
||||||
|
|
||||||
if email := query.Filters["email"]; email != "" {
|
|
||||||
emailFilter := ""
|
|
||||||
for _, username := range strings.Split(string(email), "|") {
|
|
||||||
emailFilter += fmt.Sprintf("(mail=%s)", username)
|
|
||||||
}
|
|
||||||
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", emailFilter)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
userSearchRequest := ldap.NewSearchRequest(
|
userSearchRequest := ldap.NewSearchRequest(
|
||||||
l.userSearchBase,
|
l.userSearchBase,
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
filter,
|
filter,
|
||||||
[]string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"},
|
[]string{ldapAttributeUserID, ldapAttributeMail, ldapAttributeDescription, ldapAttributeCreateTimestamp},
|
||||||
[]ldap.Control{pageControl},
|
[]ldap.Control{pageControl},
|
||||||
)
|
)
|
||||||
|
|
||||||
response, err := conn.Search(userSearchRequest)
|
response, err := conn.Search(userSearchRequest)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorln("search user", err)
|
klog.Error(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range response.Entries {
|
for _, entry := range response.Entries {
|
||||||
|
|
||||||
uid := entry.GetAttributeValue("uid")
|
uid := entry.GetAttributeValue(ldapAttributeUserID)
|
||||||
email := entry.GetAttributeValue("mail")
|
email := entry.GetAttributeValue(ldapAttributeMail)
|
||||||
description := entry.GetAttributeValue("description")
|
description := entry.GetAttributeValue(ldapAttributeDescription)
|
||||||
lang := entry.GetAttributeValue("preferredLanguage")
|
createTimestamp, _ := time.Parse(ldapAttributeCreateTimestampLayout, entry.GetAttributeValue(ldapAttributeCreateTimestamp))
|
||||||
createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp"))
|
|
||||||
|
|
||||||
user := iamv1alpha2.User{ObjectMeta: metav1.ObjectMeta{Name: uid, CreationTimestamp: metav1.Time{Time: createTimestamp}}, Spec: iamv1alpha2.UserSpec{
|
user := iamv1alpha2.User{
|
||||||
Email: email,
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Lang: lang,
|
Name: uid,
|
||||||
Description: description,
|
CreationTimestamp: metav1.Time{Time: createTimestamp}},
|
||||||
}}
|
Spec: iamv1alpha2.UserSpec{
|
||||||
|
Email: email,
|
||||||
|
Description: description,
|
||||||
|
}}
|
||||||
|
|
||||||
users = append(users, user)
|
users = append(users, user)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const FAKE_HOST string = "FAKE"
|
||||||
|
|
||||||
// simpleLdap is a implementation of ldap.Interface, you should never use this in production env!
|
// simpleLdap is a implementation of ldap.Interface, you should never use this in production env!
|
||||||
type simpleLdap struct {
|
type simpleLdap struct {
|
||||||
store map[string]*iamv1alpha2.User
|
store map[string]*iamv1alpha2.User
|
||||||
|
|||||||
Reference in New Issue
Block a user