Explorar o código

Synchronizers,user status,cas expires

Synchronizers,user status,cas expires
MaxKey %!s(int64=4) %!d(string=hai) anos
pai
achega
7fe786773c
Modificáronse 51 ficheiros con 3502 adicións e 444 borrados
  1. 122 122
      gradle.properties
  2. 5 2
      maxkey-core/src/main/java/org/maxkey/constants/ConstantsStatus.java
  3. 319 0
      maxkey-core/src/main/java/org/maxkey/entity/Synchronizers.java
  4. 222 204
      maxkey-core/src/main/java/org/maxkey/entity/UserInfo.java
  5. 26 12
      maxkey-core/src/main/java/org/maxkey/entity/apps/AppsCasDetails.java
  6. 13 0
      maxkey-identitys/maxkey-synchronizers/build.gradle
  7. 23 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ISynchronizerService.java
  8. 63 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/SynchronizerJob.java
  9. 122 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryOrganizationService.java
  10. 68 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectorySynchronizerService.java
  11. 152 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryUsersService.java
  12. 85 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingAccessTokenService.java
  13. 109 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingOrganizationService.java
  14. 81 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingSynchronizerService.java
  15. 129 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingUsersService.java
  16. 78 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/entity/AccessToken.java
  17. 42 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/entity/ResponseData.java
  18. 127 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ldap/LdapOrganizationService.java
  19. 63 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ldap/LdapSynchronizerService.java
  20. 144 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ldap/LdapUsersService.java
  21. 84 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinAccessTokenService.java
  22. 106 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinOrganizationService.java
  23. 81 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinSynchronizerService.java
  24. 116 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinUsersService.java
  25. 72 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinDepts.java
  26. 40 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinDeptsResponse.java
  27. 245 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinUsers.java
  28. 40 0
      maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinUsersResponse.java
  29. BIN=BIN
      maxkey-lib/taobao-sdk-java-auto_1479188381469-20210326.jar
  30. 33 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/mapper/SynchronizersMapper.java
  31. 48 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/SynchronizersService.java
  32. 25 0
      maxkey-persistence/src/main/resources/org/maxkey/persistence/mapper/xml/mysql/SynchronizersMapper.xml
  33. 4 0
      maxkey-web-manage/build.gradle
  34. 96 0
      maxkey-web-manage/src/main/java/org/maxkey/web/contorller/SynchronizersController.java
  35. 32 19
      maxkey-web-manage/src/main/resources/messages/message.properties
  36. 35 19
      maxkey-web-manage/src/main/resources/messages/message_en.properties
  37. 31 18
      maxkey-web-manage/src/main/resources/messages/message_zh_CN.properties
  38. 3 1
      maxkey-web-manage/src/main/resources/static/jquery/platform.common.js
  39. 7 0
      maxkey-web-manage/src/main/resources/templates/views/apps/cas/appAdd.ftl
  40. 6 0
      maxkey-web-manage/src/main/resources/templates/views/apps/cas/appUpdate.ftl
  41. 15 1
      maxkey-web-manage/src/main/resources/templates/views/layout/sidenav.ftl
  42. 179 0
      maxkey-web-manage/src/main/resources/templates/views/synchronizers/synchronizerUpdate.ftl
  43. 122 0
      maxkey-web-manage/src/main/resources/templates/views/synchronizers/synchronizersList.ftl
  44. 33 24
      maxkey-web-manage/src/main/resources/templates/views/userinfo/userAdd.ftl
  45. 28 20
      maxkey-web-manage/src/main/resources/templates/views/userinfo/userUpdate.ftl
  46. 18 2
      maxkey-web-manage/src/main/resources/templates/views/userinfo/usersList.ftl
  47. 1 0
      maxkey-web-maxkey/src/main/resources/messages/message.properties
  48. 1 0
      maxkey-web-maxkey/src/main/resources/messages/message_en.properties
  49. 1 0
      maxkey-web-maxkey/src/main/resources/messages/message_zh_CN.properties
  50. 6 0
      maxkey-web-maxkey/src/main/resources/templates/views/profile/myProfile.ftl
  51. 1 0
      settings.gradle

+ 122 - 122
gradle.properties

@@ -1,133 +1,133 @@
-#gradle properties for maxkey
-group	=maxkey.top
-version	=2.8.0
-vendor	=https://www.maxkey.top
-author	=MaxKeyTop
+#properties for maxkey
+group							=maxkey.top
+version							=2.8.0
+vendor							=https://www.maxkey.top
+author							=MaxKeyTop
 #Version For use jar
 #Apache
-commonsbeanutilsVersion=1.9.3
-commonscodecVersion=1.15
-commonscollectionsVersion=3.2.2
-commonscollections4Version=4.4
-commonscsvVersion=1.7
-commonstextVersion=1.9
-commonsdbcp2Version=2.6.0
-commonsdbutilsVersion=1.7
-commonsdigester3Version=3.2
-commonsdigesterVersion=2.1
-commonsioVersion=2.8.0
-commonslangVersion=2.6
-commonslang3Version=3.11
-commonsloggingVersion=1.2
-commonspool2Version=2.6.2
-commonshttpclientVersion=3.1
-commonsfileuploadVersion=1.4
-commonsemailVersion=1.5
-httpcoreVersion=4.4.13
-velocityVersion=1.7
-velocitydepVersion=1.4
-freemarkerVersion=2.3.31
-xmlbeansVersion=3.0.1
-commonscompressVersion=1.20
-log4jVersion         	=2.14.1
-kafkaclientsVersion=2.6.1
-httpcomponentsVersion	=4.5.12
-poiVersion	 			=4.1.2
-tomcatVersion			=9.0.44
-tomcatembedloggingjuliVersion=8.5.2
+commonsbeanutilsVersion			=1.9.3
+commonscodecVersion				=1.15
+commonscollectionsVersion		=3.2.2
+commonscollections4Version		=4.4
+commonscsvVersion				=1.7
+commonstextVersion				=1.9
+commonsdbcp2Version				=2.6.0
+commonsdbutilsVersion			=1.7
+commonsdigester3Version			=3.2
+commonsdigesterVersion			=2.1
+commonsioVersion				=2.8.0
+commonslangVersion				=2.6
+commonslang3Version				=3.11
+commonsloggingVersion			=1.2
+commonspool2Version				=2.6.2
+commonshttpclientVersion		=3.1
+commonsfileuploadVersion		=1.4
+commonsemailVersion				=1.5
+httpcoreVersion					=4.4.13
+velocityVersion					=1.7
+velocitydepVersion				=1.4
+freemarkerVersion				=2.3.31
+xmlbeansVersion					=3.0.1
+commonscompressVersion			=1.20
+log4jVersion         			=2.14.1
+kafkaclientsVersion				=2.6.1
+httpcomponentsVersion			=4.5.12
+poiVersion	 					=4.1.2
+tomcatVersion					=9.0.44
+tomcatembedloggingjuliVersion	=8.5.2
 #spring
-springVersion        	=5.3.6
-springBootVersion    	=2.4.5
-springSecurityVersion	=5.4.6
-springDataVersion    	=2.4.1
-springSessionVersion    =2.4.1
-springkafkaVersion=2.6.6
-springretryVersion=1.3.0
-springplugincoreVersion=2.0.0.RELEASE
-springpluginmetadataVersion=2.0.0.RELEASE
-springfoxVersion  		=3.0.0
+springVersion        			=5.3.6
+springBootVersion    			=2.4.5
+springSecurityVersion			=5.4.6
+springDataVersion    			=2.4.1
+springSessionVersion    		=2.4.1
+springkafkaVersion				=2.6.6
+springretryVersion				=1.3.0
+springplugincoreVersion			=2.0.0.RELEASE
+springpluginmetadataVersion		=2.0.0.RELEASE
+springfoxVersion  				=3.0.0
 #google
-jibGradlePluginVersion  =2.7.1
-kaptchaVersion=2.3.2
-gsonVersion=2.8.6
-guavaVersion=30.1.1-jre
-tinkVersion=1.4.0
-zxingcoreVersion=3.4.1
+jibGradlePluginVersion  		=2.7.1
+kaptchaVersion					=2.3.2
+gsonVersion						=2.8.6
+guavaVersion					=30.1.1-jre
+tinkVersion						=1.4.0
+zxingcoreVersion				=3.4.1
 #jboss
-jbossloggingVersion=3.4.1.Final
-hibernateVersion	 	=6.2.0.Final
+jbossloggingVersion				=3.4.1.Final
+hibernateVersion	 			=6.2.0.Final
 #doc
-swaggerVersion  		=1.6.2
-swaggerV3Version  		=2.1.6
-knife4jVersion  		=3.0.2
+swaggerVersion  				=1.6.2
+swaggerV3Version  				=2.1.6
+knife4jVersion  				=3.0.2
 #database
-mysqlconnectorjavaVersion=8.0.21
-druidVersion=1.2.5
-druidspringbootstarterVersion=1.2.5
-jedisVersion=3.4.1
-ehcacheVersion=3.9.0
-mybatisVersion=3.5.6
-mybatisspringVersion=2.0.6
+mysqlconnectorjavaVersion		=8.0.21
+druidVersion					=1.2.5
+druidspringbootstarterVersion	=1.2.5
+jedisVersion					=3.4.1
+ehcacheVersion					=3.9.0
+mybatisVersion					=3.5.7
+mybatisspringVersion			=2.0.6
 #saml
-opensamlVersion=2.6.6
-openwsVersion=1.5.6
-xmltoolingVersion=1.4.6
-javasupportVersion=7.5.1
+opensamlVersion					=2.6.6
+openwsVersion					=1.5.6
+xmltoolingVersion				=1.4.6
+javasupportVersion				=7.5.1
 #others
-jhlabsfiltersVersion=2.0.235-1
-slf4jVersion	 	 	=1.7.30
-jacksonVersion		 	=2.12.1
-bouncycastleVersion	 	=1.64
-junitVersion=4.11
-mockitoallVersion=1.10.19
-xmlunitVersion=1.6
-nimbusjosejwtVersion=9.4.1
-jcipannotationsVersion=1.0
-minidevjsonsmartVersion=2.3
-minidevasmVersion=1.0.2
-simplehttpVersion=1.0.3
-JustAuthVersion=1.15.9
-javassistVersion=3.23.0-GA
-esapiVersion=2.2.0.0
-javaxmailVersion=1.6.2
-javaxpersistenceVersion=2.2.1
-activationVersion=1.1.1
-javaxannotationapiVersion=1.3.2
-jtaVersion=1.1
-javaxtransactionapiVersion=1.3
-validationapiVersion=2.0.1.Final
-jsr173Version=1.0
-jaxbapiVersion=2.3.1
-jaxbcoreVersion=2.3.0.1
-jaxbimplVersion=2.3.2
-jaxbxjcVersion=2.3.2
-classmateVersion=1.5.0
-fastjsonVersion=1.2.74
-reactivestreamsVersion=1.0.2
-reactorcoreVersion=3.2.10.RELEASE
-szxcvbnVersion=0.2
-quartzVersion=2.3.2
-jodatimeVersion=2.10.9
-snakeyamlVersion=1.26
-nekohtmlVersion=1.9.22
-ognlVersion=3.2.14
-cglibVersion=3.3.0
-asmVersion=7.3.1
-aopallianceVersion=1.0
-aspectjtoolsVersion=1.9.4
+jhlabsfiltersVersion			=2.0.235-1
+slf4jVersion	 	 			=1.7.30
+jacksonVersion		 			=2.12.1
+bouncycastleVersion	 			=1.64
+junitVersion					=4.11
+mockitoallVersion				=1.10.19
+xmlunitVersion					=1.6
+nimbusjosejwtVersion			=9.4.1
+jcipannotationsVersion			=1.0
+minidevjsonsmartVersion			=2.3
+minidevasmVersion				=1.0.2
+simplehttpVersion				=1.0.3
+JustAuthVersion					=1.15.9
+javassistVersion				=3.23.0-GA
+esapiVersion					=2.2.0.0
+javaxmailVersion				=1.6.2
+javaxpersistenceVersion			=2.2.1
+activationVersion				=1.1.1
+javaxannotationapiVersion		=1.3.2
+jtaVersion						=1.1
+javaxtransactionapiVersion		=1.3
+validationapiVersion			=2.0.1.Final
+jsr173Version					=1.0
+jaxbapiVersion					=2.3.1
+jaxbcoreVersion					=2.3.0.1
+jaxbimplVersion					=2.3.2
+jaxbxjcVersion					=2.3.2
+classmateVersion				=1.5.0
+fastjsonVersion					=1.2.74
+reactivestreamsVersion			=1.0.2
+reactorcoreVersion				=3.2.10.RELEASE
+szxcvbnVersion					=0.2
+quartzVersion					=2.3.2
+jodatimeVersion					=2.10.9
+snakeyamlVersion				=1.26
+nekohtmlVersion					=1.9.22
+ognlVersion						=3.2.14
+cglibVersion					=3.3.0
+asmVersion						=7.3.1
+aopallianceVersion				=1.0
+aspectjtoolsVersion				=1.9.4
 #xml
-jdomVersion=2.0.2
-dom4jVersion=1.6.1
-serializerVersion=2.7.2
-xmlresolverVersion=1.2
-xmlsecVersion=1.5.8
-xpp3Version=1.1.6
-xstreamVersion=1.4.10
-passayVersion=1.6.0
-micrometercoreVersion=1.6.4
-LatencyUtilsVersion=2.0.3
-stax2apiVersion=4.2.1
-mapstructVersion=1.4.1.Final
+jdomVersion						=2.0.2
+dom4jVersion					=1.6.1
+serializerVersion				=2.7.2
+xmlresolverVersion				=1.2
+xmlsecVersion					=1.5.8
+xpp3Version						=1.1.6
+xstreamVersion					=1.4.10
+passayVersion					=1.6.0
+micrometercoreVersion			=1.6.4
+LatencyUtilsVersion				=2.0.3
+stax2apiVersion					=4.2.1
+mapstructVersion				=1.4.1.Final
 #sdk
-aliyunjavasdkcoreVersion=4.5.1
-tencentcloudsdkjavaVersion=3.1.33
+aliyunjavasdkcoreVersion		=4.5.1
+tencentcloudsdkjavaVersion		=3.1.33

+ 5 - 2
maxkey-core/src/main/java/org/maxkey/constants/ConstantsStatus.java

@@ -18,6 +18,7 @@
 package org.maxkey.constants;
 
 public final class ConstantsStatus {
+	
     public static final int ACTIVE = 1;
 
     public static final int INACTIVE = 2;
@@ -42,8 +43,10 @@ public final class ConstantsStatus {
 
     public static final int STOP = 12;
 
-    public static final int APPROVED = 13;
+    public static final int APPLY = 13;
     
-    public static final int QUITED = 14;
+    public static final int APPROVED = 14;
+    
+    public static final int QUITED = 15;
 
 }

+ 319 - 0
maxkey-core/src/main/java/org/maxkey/entity/Synchronizers.java

@@ -0,0 +1,319 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.entity;
+
+import java.io.Serializable;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import org.apache.mybatis.jpa.persistence.JpaBaseEntity;
+import org.hibernate.validator.constraints.Length;
+@Entity
+@Table(name = "MXK_SYNCHRONIZERS")
+public class Synchronizers extends JpaBaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 4660258495864814777L;
+    @Id
+    @Column
+    @GeneratedValue(strategy = GenerationType.AUTO, generator = "snowflakeid")
+    String id;
+
+    @Length(max = 60)
+    @Column
+    String name;
+    @Column
+    String filters ;
+    @Column
+    String sourceType;
+    @Column
+    String resumeTime; 
+    @Column
+    String suspendTime;
+    @Column
+    String scheduler;
+    @Column
+    String providerUrl;
+    @Column
+    String driverClass;
+    @Column
+    String principal;
+    @Column
+    String credentials;
+    @Column
+    String basedn;
+    @Column
+    String msadDomain;
+    @Column
+    String ssl;
+    @Column
+    String trustStore;
+    @Column
+    String trustStorePassword;
+    @Column
+    String description;
+    @Column
+    String createdBy;
+    @Column
+    String createdDate;
+    @Column
+    String modifiedBy;
+    @Column
+    String modifiedDate;
+    @Column
+    String status;
+
+    public Synchronizers() {
+    }
+
+    public Synchronizers(String id) {
+        this.id = id;
+    }
+
+
+
+    public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getFilters() {
+		return filters;
+	}
+
+	public void setFilters(String filters) {
+		this.filters = filters;
+	}
+
+	public String getSourceType() {
+		return sourceType;
+	}
+
+	public void setSourceType(String sourceType) {
+		this.sourceType = sourceType;
+	}
+
+	public String getResumeTime() {
+		return resumeTime;
+	}
+
+	public void setResumeTime(String resumeTime) {
+		this.resumeTime = resumeTime;
+	}
+
+	public String getSuspendTime() {
+		return suspendTime;
+	}
+
+	public void setSuspendTime(String suspendTime) {
+		this.suspendTime = suspendTime;
+	}
+
+	public String getScheduler() {
+		return scheduler;
+	}
+
+	public void setScheduler(String scheduler) {
+		this.scheduler = scheduler;
+	}
+
+	public String getProviderUrl() {
+		return providerUrl;
+	}
+
+	public void setProviderUrl(String providerUrl) {
+		this.providerUrl = providerUrl;
+	}
+
+	public String getDriverClass() {
+		return driverClass;
+	}
+
+	public void setDriverClass(String driverClass) {
+		this.driverClass = driverClass;
+	}
+
+	public String getPrincipal() {
+		return principal;
+	}
+
+	public void setPrincipal(String principal) {
+		this.principal = principal;
+	}
+
+	public String getCredentials() {
+		return credentials;
+	}
+
+	public void setCredentials(String credentials) {
+		this.credentials = credentials;
+	}
+
+	public String getBasedn() {
+		return basedn;
+	}
+
+	public void setBasedn(String basedn) {
+		this.basedn = basedn;
+	}
+
+	public String getMsadDomain() {
+		return msadDomain;
+	}
+
+	public void setMsadDomain(String msadDomain) {
+		this.msadDomain = msadDomain;
+	}
+
+	public String getSsl() {
+		return ssl;
+	}
+
+	public void setSsl(String ssl) {
+		this.ssl = ssl;
+	}
+
+	public String getTrustStore() {
+		return trustStore;
+	}
+
+	public void setTrustStore(String trustStore) {
+		this.trustStore = trustStore;
+	}
+
+	public String getTrustStorePassword() {
+		return trustStorePassword;
+	}
+
+	public void setTrustStorePassword(String trustStorePassword) {
+		this.trustStorePassword = trustStorePassword;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getCreatedBy() {
+		return createdBy;
+	}
+
+	public void setCreatedBy(String createdBy) {
+		this.createdBy = createdBy;
+	}
+
+	public String getCreatedDate() {
+		return createdDate;
+	}
+
+	public void setCreatedDate(String createdDate) {
+		this.createdDate = createdDate;
+	}
+
+	public String getModifiedBy() {
+		return modifiedBy;
+	}
+
+	public void setModifiedBy(String modifiedBy) {
+		this.modifiedBy = modifiedBy;
+	}
+
+	public String getModifiedDate() {
+		return modifiedDate;
+	}
+
+	public void setModifiedDate(String modifiedDate) {
+		this.modifiedDate = modifiedDate;
+	}
+
+	public String getStatus() {
+		return status;
+	}
+
+	public void setStatus(String status) {
+		this.status = status;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("Synchronizers [id=");
+		builder.append(id);
+		builder.append(", name=");
+		builder.append(name);
+		builder.append(", filters=");
+		builder.append(filters);
+		builder.append(", sourceType=");
+		builder.append(sourceType);
+		builder.append(", resumeTime=");
+		builder.append(resumeTime);
+		builder.append(", suspendTime=");
+		builder.append(suspendTime);
+		builder.append(", scheduler=");
+		builder.append(scheduler);
+		builder.append(", providerUrl=");
+		builder.append(providerUrl);
+		builder.append(", driverClass=");
+		builder.append(driverClass);
+		builder.append(", principal=");
+		builder.append(principal);
+		builder.append(", credentials=");
+		builder.append(credentials);
+		builder.append(", basedn=");
+		builder.append(basedn);
+		builder.append(", msadDomain=");
+		builder.append(msadDomain);
+		builder.append(", ssl=");
+		builder.append(ssl);
+		builder.append(", trustStore=");
+		builder.append(trustStore);
+		builder.append(", trustStorePassword=");
+		builder.append(trustStorePassword);
+		builder.append(", description=");
+		builder.append(description);
+		builder.append(", createdBy=");
+		builder.append(createdBy);
+		builder.append(", createdDate=");
+		builder.append(createdDate);
+		builder.append(", modifiedBy=");
+		builder.append(modifiedBy);
+		builder.append(", modifiedDate=");
+		builder.append(modifiedDate);
+		builder.append(", status=");
+		builder.append(status);
+		builder.append("]");
+		return builder.toString();
+	}
+
+}

+ 222 - 204
maxkey-core/src/main/java/org/maxkey/entity/UserInfo.java

@@ -19,6 +19,7 @@ package org.maxkey.entity;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -61,6 +62,10 @@ public class UserInfo extends JpaBaseEntity {
      */
     @Column
     protected String userType;
+    
+    @Column
+    protected String userState;
+    
     @Column
     protected String windowsAccount;
 
@@ -1226,209 +1231,222 @@ public class UserInfo extends JpaBaseEntity {
         this.theme = theme;
     }
 
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("UserInfo [id=");
-        builder.append(id);
-        builder.append(", username=");
-        builder.append(username);
-        builder.append(", password=");
-        builder.append(password);
-        builder.append(", decipherable=");
-        builder.append(decipherable);
-        builder.append(", sharedSecret=");
-        builder.append(sharedSecret);
-        builder.append(", sharedCounter=");
-        builder.append(sharedCounter);
-        builder.append(", userType=");
-        builder.append(userType);
-        builder.append(", windowsAccount=");
-        builder.append(windowsAccount);
-        builder.append(", displayName=");
-        builder.append(displayName);
-        builder.append(", nickName=");
-        builder.append(nickName);
-        builder.append(", nameZhSpell=");
-        builder.append(nameZhSpell);
-        builder.append(", nameZhShortSpell=");
-        builder.append(nameZhShortSpell);
-        builder.append(", givenName=");
-        builder.append(givenName);
-        builder.append(", middleName=");
-        builder.append(middleName);
-        builder.append(", familyName=");
-        builder.append(familyName);
-        builder.append(", honorificPrefix=");
-        builder.append(honorificPrefix);
-        builder.append(", honorificSuffix=");
-        builder.append(honorificSuffix);
-        builder.append(", formattedName=");
-        builder.append(formattedName);
-        builder.append(", married=");
-        builder.append(married);
-        builder.append(", gender=");
-        builder.append(gender);
-        builder.append(", birthDate=");
-        builder.append(birthDate);
-        builder.append(", picture=");
-        builder.append(picture);
-        builder.append(", pictureFile=");
-        builder.append(pictureFile);
-        builder.append(", idType=");
-        builder.append(idType);
-        builder.append(", idCardNo=");
-        builder.append(idCardNo);
-        builder.append(", webSite=");
-        builder.append(webSite);
-        builder.append(", startWorkDate=");
-        builder.append(startWorkDate);
-        builder.append(", authnType=");
-        builder.append(authnType);
-        builder.append(", email=");
-        builder.append(email);
-        builder.append(", emailVerified=");
-        builder.append(emailVerified);
-        builder.append(", mobile=");
-        builder.append(mobile);
-        builder.append(", mobileVerified=");
-        builder.append(mobileVerified);
-        builder.append(", passwordQuestion=");
-        builder.append(passwordQuestion);
-        builder.append(", passwordAnswer=");
-        builder.append(passwordAnswer);
-        builder.append(", appLoginAuthnType=");
-        builder.append(appLoginAuthnType);
-        builder.append(", appLoginPassword=");
-        builder.append(appLoginPassword);
-        builder.append(", protectedApps=");
-        builder.append(protectedApps);
-        builder.append(", protectedAppsMap=");
-        builder.append(protectedAppsMap);
-        builder.append(", passwordLastSetTime=");
-        builder.append(passwordLastSetTime);
-        builder.append(", badPasswordCount=");
-        builder.append(badPasswordCount);
-        builder.append(", badPasswordTime=");
-        builder.append(badPasswordTime);
-        builder.append(", unLockTime=");
-        builder.append(unLockTime);
-        builder.append(", isLocked=");
-        builder.append(isLocked);
-        builder.append(", lastLoginTime=");
-        builder.append(lastLoginTime);
-        builder.append(", lastLoginIp=");
-        builder.append(lastLoginIp);
-        builder.append(", lastLogoffTime=");
-        builder.append(lastLogoffTime);
-        builder.append(", passwordSetType=");
-        builder.append(passwordSetType);
-        builder.append(", loginCount=");
-        builder.append(loginCount);
-        builder.append(", locale=");
-        builder.append(locale);
-        builder.append(", timeZone=");
-        builder.append(timeZone);
-        builder.append(", preferredLanguage=");
-        builder.append(preferredLanguage);
-        builder.append(", workCountry=");
-        builder.append(workCountry);
-        builder.append(", workRegion=");
-        builder.append(workRegion);
-        builder.append(", workLocality=");
-        builder.append(workLocality);
-        builder.append(", workStreetAddress=");
-        builder.append(workStreetAddress);
-        builder.append(", workAddressFormatted=");
-        builder.append(workAddressFormatted);
-        builder.append(", workEmail=");
-        builder.append(workEmail);
-        builder.append(", workPhoneNumber=");
-        builder.append(workPhoneNumber);
-        builder.append(", workPostalCode=");
-        builder.append(workPostalCode);
-        builder.append(", workFax=");
-        builder.append(workFax);
-        builder.append(", homeCountry=");
-        builder.append(homeCountry);
-        builder.append(", homeRegion=");
-        builder.append(homeRegion);
-        builder.append(", homeLocality=");
-        builder.append(homeLocality);
-        builder.append(", homeStreetAddress=");
-        builder.append(homeStreetAddress);
-        builder.append(", homeAddressFormatted=");
-        builder.append(homeAddressFormatted);
-        builder.append(", homeEmail=");
-        builder.append(homeEmail);
-        builder.append(", homePhoneNumber=");
-        builder.append(homePhoneNumber);
-        builder.append(", homePostalCode=");
-        builder.append(homePostalCode);
-        builder.append(", homeFax=");
-        builder.append(homeFax);
-        builder.append(", employeeNumber=");
-        builder.append(employeeNumber);
-        builder.append(", costCenter=");
-        builder.append(costCenter);
-        builder.append(", organization=");
-        builder.append(organization);
-        builder.append(", division=");
-        builder.append(division);
-        builder.append(", departmentId=");
-        builder.append(departmentId);
-        builder.append(", department=");
-        builder.append(department);
-        builder.append(", jobTitle=");
-        builder.append(jobTitle);
-        builder.append(", jobLevel=");
-        builder.append(jobLevel);
-        builder.append(", managerId=");
-        builder.append(managerId);
-        builder.append(", manager=");
-        builder.append(manager);
-        builder.append(", assistantId=");
-        builder.append(assistantId);
-        builder.append(", assistant=");
-        builder.append(assistant);
-        builder.append(", entryDate=");
-        builder.append(entryDate);
-        builder.append(", quitDate=");
-        builder.append(quitDate);
-        builder.append(", defineIm=");
-        builder.append(defineIm);
-        builder.append(", weixinFollow=");
-        builder.append(weixinFollow);
-        builder.append(", theme=");
-        builder.append(theme);
-        builder.append(", extraAttribute=");
-        builder.append(extraAttribute);
-        builder.append(", extraAttributeName=");
-        builder.append(extraAttributeName);
-        builder.append(", extraAttributeValue=");
-        builder.append(extraAttributeValue);
-        builder.append(", extraAttributeMap=");
-        builder.append(extraAttributeMap);
-        builder.append(", online=");
-        builder.append(online);
-        builder.append(", ldapDn=");
-        builder.append(ldapDn);
-        builder.append(", gridList=");
-        builder.append(gridList);
-        builder.append(", createdBy=");
-        builder.append(createdBy);
-        builder.append(", createdDate=");
-        builder.append(createdDate);
-        builder.append(", modifiedBy=");
-        builder.append(modifiedBy);
-        builder.append(", modifiedDate=");
-        builder.append(modifiedDate);
-        builder.append(", status=");
-        builder.append(status);
-        builder.append(", description=");
-        builder.append(description);
-        builder.append("]");
-        return builder.toString();
-    }
+    
+    public String getUserState() {
+		return userState;
+	}
+
+	public void setUserState(String userState) {
+		this.userState = userState;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("UserInfo [id=");
+		builder.append(id);
+		builder.append(", username=");
+		builder.append(username);
+		builder.append(", password=");
+		builder.append(password);
+		builder.append(", decipherable=");
+		builder.append(decipherable);
+		builder.append(", sharedSecret=");
+		builder.append(sharedSecret);
+		builder.append(", sharedCounter=");
+		builder.append(sharedCounter);
+		builder.append(", userType=");
+		builder.append(userType);
+		builder.append(", userState=");
+		builder.append(userState);
+		builder.append(", windowsAccount=");
+		builder.append(windowsAccount);
+		builder.append(", displayName=");
+		builder.append(displayName);
+		builder.append(", nickName=");
+		builder.append(nickName);
+		builder.append(", nameZhSpell=");
+		builder.append(nameZhSpell);
+		builder.append(", nameZhShortSpell=");
+		builder.append(nameZhShortSpell);
+		builder.append(", givenName=");
+		builder.append(givenName);
+		builder.append(", middleName=");
+		builder.append(middleName);
+		builder.append(", familyName=");
+		builder.append(familyName);
+		builder.append(", honorificPrefix=");
+		builder.append(honorificPrefix);
+		builder.append(", honorificSuffix=");
+		builder.append(honorificSuffix);
+		builder.append(", formattedName=");
+		builder.append(formattedName);
+		builder.append(", married=");
+		builder.append(married);
+		builder.append(", gender=");
+		builder.append(gender);
+		builder.append(", birthDate=");
+		builder.append(birthDate);
+		builder.append(", picture=");
+		builder.append(Arrays.toString(picture));
+		builder.append(", pictureFile=");
+		builder.append(pictureFile);
+		builder.append(", idType=");
+		builder.append(idType);
+		builder.append(", idCardNo=");
+		builder.append(idCardNo);
+		builder.append(", webSite=");
+		builder.append(webSite);
+		builder.append(", startWorkDate=");
+		builder.append(startWorkDate);
+		builder.append(", authnType=");
+		builder.append(authnType);
+		builder.append(", email=");
+		builder.append(email);
+		builder.append(", emailVerified=");
+		builder.append(emailVerified);
+		builder.append(", mobile=");
+		builder.append(mobile);
+		builder.append(", mobileVerified=");
+		builder.append(mobileVerified);
+		builder.append(", passwordQuestion=");
+		builder.append(passwordQuestion);
+		builder.append(", passwordAnswer=");
+		builder.append(passwordAnswer);
+		builder.append(", appLoginAuthnType=");
+		builder.append(appLoginAuthnType);
+		builder.append(", appLoginPassword=");
+		builder.append(appLoginPassword);
+		builder.append(", protectedApps=");
+		builder.append(protectedApps);
+		builder.append(", protectedAppsMap=");
+		builder.append(protectedAppsMap);
+		builder.append(", passwordLastSetTime=");
+		builder.append(passwordLastSetTime);
+		builder.append(", badPasswordCount=");
+		builder.append(badPasswordCount);
+		builder.append(", badPasswordTime=");
+		builder.append(badPasswordTime);
+		builder.append(", unLockTime=");
+		builder.append(unLockTime);
+		builder.append(", isLocked=");
+		builder.append(isLocked);
+		builder.append(", lastLoginTime=");
+		builder.append(lastLoginTime);
+		builder.append(", lastLoginIp=");
+		builder.append(lastLoginIp);
+		builder.append(", lastLogoffTime=");
+		builder.append(lastLogoffTime);
+		builder.append(", passwordSetType=");
+		builder.append(passwordSetType);
+		builder.append(", loginCount=");
+		builder.append(loginCount);
+		builder.append(", locale=");
+		builder.append(locale);
+		builder.append(", timeZone=");
+		builder.append(timeZone);
+		builder.append(", preferredLanguage=");
+		builder.append(preferredLanguage);
+		builder.append(", workCountry=");
+		builder.append(workCountry);
+		builder.append(", workRegion=");
+		builder.append(workRegion);
+		builder.append(", workLocality=");
+		builder.append(workLocality);
+		builder.append(", workStreetAddress=");
+		builder.append(workStreetAddress);
+		builder.append(", workAddressFormatted=");
+		builder.append(workAddressFormatted);
+		builder.append(", workEmail=");
+		builder.append(workEmail);
+		builder.append(", workPhoneNumber=");
+		builder.append(workPhoneNumber);
+		builder.append(", workPostalCode=");
+		builder.append(workPostalCode);
+		builder.append(", workFax=");
+		builder.append(workFax);
+		builder.append(", workOfficeName=");
+		builder.append(workOfficeName);
+		builder.append(", homeCountry=");
+		builder.append(homeCountry);
+		builder.append(", homeRegion=");
+		builder.append(homeRegion);
+		builder.append(", homeLocality=");
+		builder.append(homeLocality);
+		builder.append(", homeStreetAddress=");
+		builder.append(homeStreetAddress);
+		builder.append(", homeAddressFormatted=");
+		builder.append(homeAddressFormatted);
+		builder.append(", homeEmail=");
+		builder.append(homeEmail);
+		builder.append(", homePhoneNumber=");
+		builder.append(homePhoneNumber);
+		builder.append(", homePostalCode=");
+		builder.append(homePostalCode);
+		builder.append(", homeFax=");
+		builder.append(homeFax);
+		builder.append(", employeeNumber=");
+		builder.append(employeeNumber);
+		builder.append(", costCenter=");
+		builder.append(costCenter);
+		builder.append(", organization=");
+		builder.append(organization);
+		builder.append(", division=");
+		builder.append(division);
+		builder.append(", departmentId=");
+		builder.append(departmentId);
+		builder.append(", department=");
+		builder.append(department);
+		builder.append(", jobTitle=");
+		builder.append(jobTitle);
+		builder.append(", jobLevel=");
+		builder.append(jobLevel);
+		builder.append(", managerId=");
+		builder.append(managerId);
+		builder.append(", manager=");
+		builder.append(manager);
+		builder.append(", assistantId=");
+		builder.append(assistantId);
+		builder.append(", assistant=");
+		builder.append(assistant);
+		builder.append(", entryDate=");
+		builder.append(entryDate);
+		builder.append(", quitDate=");
+		builder.append(quitDate);
+		builder.append(", defineIm=");
+		builder.append(defineIm);
+		builder.append(", weixinFollow=");
+		builder.append(weixinFollow);
+		builder.append(", theme=");
+		builder.append(theme);
+		builder.append(", extraAttribute=");
+		builder.append(extraAttribute);
+		builder.append(", extraAttributeName=");
+		builder.append(extraAttributeName);
+		builder.append(", extraAttributeValue=");
+		builder.append(extraAttributeValue);
+		builder.append(", extraAttributeMap=");
+		builder.append(extraAttributeMap);
+		builder.append(", online=");
+		builder.append(online);
+		builder.append(", ldapDn=");
+		builder.append(ldapDn);
+		builder.append(", gridList=");
+		builder.append(gridList);
+		builder.append(", createdBy=");
+		builder.append(createdBy);
+		builder.append(", createdDate=");
+		builder.append(createdDate);
+		builder.append(", modifiedBy=");
+		builder.append(modifiedBy);
+		builder.append(", modifiedDate=");
+		builder.append(modifiedDate);
+		builder.append(", status=");
+		builder.append(status);
+		builder.append(", description=");
+		builder.append(description);
+		builder.append("]");
+		return builder.toString();
+	}
 
 }

+ 26 - 12
maxkey-core/src/main/java/org/maxkey/entity/apps/AppsCasDetails.java

@@ -39,6 +39,8 @@ public class AppsCasDetails extends Apps {
     @Column
     private String service;
     @Column
+    private Integer expires;
+    @Column
     private String callbackUrl;
 
     /**
@@ -63,17 +65,29 @@ public class AppsCasDetails extends Apps {
         this.callbackUrl = callbackUrl;
     }
 
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("AppsCasDetails [id=");
-        builder.append(id);
-        builder.append(", service=");
-        builder.append(service);
-        builder.append(", callbackUrl=");
-        builder.append(callbackUrl);
-        builder.append("]");
-        return builder.toString();
-    }
+
+
+	public Integer getExpires() {
+		return expires;
+	}
+
+	public void setExpires(Integer expires) {
+		this.expires = expires;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("AppsCasDetails [id=");
+		builder.append(id);
+		builder.append(", service=");
+		builder.append(service);
+		builder.append(", expires=");
+		builder.append(expires);
+		builder.append(", callbackUrl=");
+		builder.append(callbackUrl);
+		builder.append("]");
+		return builder.toString();
+	}
 
 }

+ 13 - 0
maxkey-identitys/maxkey-synchronizers/build.gradle

@@ -0,0 +1,13 @@
+description = "maxkey-synchronizers"
+
+apply plugin: 'java'
+
+dependencies {
+	//local jars
+	implementation fileTree(dir: '../maxkey-lib/*/', include: '*.jar')
+	
+	implementation project(":maxkey-common")
+	implementation project(":maxkey-core")
+	implementation project(":maxkey-persistence")
+   
+}

+ 23 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ISynchronizerService.java

@@ -0,0 +1,23 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer;
+
+public interface ISynchronizerService {
+
+	public void sync() throws Exception ;
+}

+ 63 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/SynchronizerJob.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SynchronizerJob  implements Job {
+	final static Logger _logger = LoggerFactory.getLogger(SynchronizerJob.class);
+    
+    
+    public static class JOBSTATUS{
+        public static int STOP = 0;
+        public static int RUNNING = 1;
+        public static int FINISHED = 2;
+    }
+    
+    private static int jobStatus = JOBSTATUS.STOP;
+
+    @Override
+    public void execute(JobExecutionContext context){
+        if(jobStatus == JOBSTATUS.RUNNING) {
+            _logger.info("SynchronizerJob is in running . " );
+            return;
+        }
+        
+        _logger.debug("SynchronizerJob is running ... " );
+        jobStatus = JOBSTATUS.RUNNING;
+        try {
+        	ISynchronizerService service =
+        			(ISynchronizerService)context.getMergedJobDataMap().get("synchronizerService");
+        	service.sync();
+            Thread.sleep(10 *1000);
+            
+            _logger.debug("SynchronizerJob is success  " );
+        }catch(Exception e) {
+            _logger.error("Exception " ,e);
+            jobStatus = JOBSTATUS.STOP;
+        }
+        jobStatus = JOBSTATUS.FINISHED;
+        _logger.debug("SynchronizerJob is finished . " );
+    }
+    
+    
+
+}

+ 122 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryOrganizationService.java

@@ -0,0 +1,122 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.activedirectory;
+
+import java.util.HashMap;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import org.maxkey.constants.ldap.OrganizationalUnit;
+import org.maxkey.entity.Organizations;
+import org.maxkey.persistence.ldap.ActiveDirectoryUtils;
+import org.maxkey.persistence.ldap.LdapUtils;
+import org.maxkey.persistence.service.OrganizationsService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ActiveDirectoryOrganizationService   implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(ActiveDirectoryOrganizationService.class);
+
+	ActiveDirectoryUtils ldapUtils;
+	
+	@Autowired
+	OrganizationsService organizationsService;
+	
+	public void sync() {
+		_logger.info("Sync Organizations ...");
+		try {
+			SearchControls constraints = new SearchControls();
+			constraints.setSearchScope(ldapUtils.getSearchScope());
+			NamingEnumeration<SearchResult> results = ldapUtils.getConnection()
+					.search(ldapUtils.getBaseDN(), "(&(objectClass=OrganizationalUnit))", constraints);
+			
+			long recordCount = 0;
+			while (null != results && results.hasMoreElements()) {
+				Object obj = results.nextElement();
+				if (obj instanceof SearchResult) {
+					recordCount ++;
+					SearchResult si = (SearchResult) obj;
+					_logger.info("Sync OrganizationalUnit  Record " + recordCount+" --------------------------------------------------");
+					_logger.trace("name " + si.getName());
+					_logger.info("NameInNamespace " + si.getNameInNamespace());
+					
+					HashMap<String,Attribute> attributeMap = new HashMap<String,Attribute>();
+					NamingEnumeration<? extends Attribute>  attrs = si.getAttributes().getAll();
+					while (null != attrs && attrs.hasMoreElements()) {
+						Attribute  objAttrs = attrs.nextElement();
+						_logger.trace("attribute "+objAttrs.getID() + " : " + objAttrs.get());
+						attributeMap.put(objAttrs.getID().toLowerCase(), objAttrs);
+					}
+					
+					Organizations org = buildOrganization(attributeMap,si.getName(),si.getNameInNamespace());
+					
+					_logger.info("Organizations " + org);
+				}
+			}
+			
+			//ldapUtils.close();
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		
+		
+	}
+	
+	public Organizations buildOrganization(HashMap<String,Attribute> attributeMap,String name,String nameInNamespace) {
+		Organizations org = new Organizations();
+		org.setLdapDn(nameInNamespace);
+		try {
+			org.setName(LdapUtils.getAttributeStringValue(OrganizationalUnit.OU,attributeMap));
+
+			org.setCountry(LdapUtils.getAttributeStringValue(OrganizationalUnit.CO,attributeMap));
+			org.setRegion(LdapUtils.getAttributeStringValue(OrganizationalUnit.ST,attributeMap));
+			org.setLocality(LdapUtils.getAttributeStringValue(OrganizationalUnit.L,attributeMap));
+			org.setStreet(LdapUtils.getAttributeStringValue(OrganizationalUnit.STREET,attributeMap));
+			org.setPostalCode(LdapUtils.getAttributeStringValue(OrganizationalUnit.POSTALCODE,attributeMap));
+			org.setDescription(LdapUtils.getAttributeStringValue(OrganizationalUnit.DESCRIPTION,attributeMap));
+			
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		return org;
+	}
+	
+
+	public ActiveDirectoryUtils getLdapUtils() {
+		return ldapUtils;
+	}
+
+	public void setLdapUtils(ActiveDirectoryUtils ldapUtils) {
+		this.ldapUtils = ldapUtils;
+	}
+
+	public OrganizationsService getOrganizationsService() {
+		return organizationsService;
+	}
+
+	public void setOrganizationsService(OrganizationsService organizationsService) {
+		this.organizationsService = organizationsService;
+	}
+
+}

+ 68 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectorySynchronizerService.java

@@ -0,0 +1,68 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.activedirectory;
+
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ActiveDirectorySynchronizerService   implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(ActiveDirectorySynchronizerService.class);
+	
+	@Autowired
+	ActiveDirectoryUsersService activeDirectoryUsersService;
+	
+	@Autowired
+	ActiveDirectoryOrganizationService activeDirectoryOrganizationService;
+	
+	public ActiveDirectorySynchronizerService() {
+		super();
+	}
+
+	public void sync() {
+		_logger.info("Sync ...");
+		
+		activeDirectoryOrganizationService.sync();
+		
+		activeDirectoryUsersService.sync();
+		
+	}
+
+	public ActiveDirectoryUsersService getActiveDirectoryUsersService() {
+		return activeDirectoryUsersService;
+	}
+
+	public void setActiveDirectoryUsersService(ActiveDirectoryUsersService activeDirectoryUsersService) {
+		this.activeDirectoryUsersService = activeDirectoryUsersService;
+	}
+
+	public ActiveDirectoryOrganizationService getActiveDirectoryOrganizationService() {
+		return activeDirectoryOrganizationService;
+	}
+
+	public void setActiveDirectoryOrganizationService(
+			ActiveDirectoryOrganizationService activeDirectoryOrganizationService) {
+		this.activeDirectoryOrganizationService = activeDirectoryOrganizationService;
+	}
+
+
+	
+}

+ 152 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryUsersService.java

@@ -0,0 +1,152 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.activedirectory;
+
+import java.util.HashMap;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import org.maxkey.constants.ldap.ActiveDirectoryUser;
+import org.maxkey.entity.UserInfo;
+import org.maxkey.persistence.ldap.ActiveDirectoryUtils;
+import org.maxkey.persistence.ldap.LdapUtils;
+import org.maxkey.persistence.service.UserInfoService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ActiveDirectoryUsersService   implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(ActiveDirectoryUsersService.class);
+
+	ActiveDirectoryUtils ldapUtils;
+	
+	@Autowired
+	UserInfoService userInfoService;
+	
+	
+	public void sync() {
+		_logger.info("Sync Users...");
+		try {
+			SearchControls constraints = new SearchControls();
+			constraints.setSearchScope(ldapUtils.getSearchScope());
+			NamingEnumeration<SearchResult> results = ldapUtils.getConnection()
+					.search(ldapUtils.getBaseDN(), "(&(objectClass=User))", constraints);
+			
+			long recordCount = 0;
+			while (null != results && results.hasMoreElements()) {
+				Object obj = results.nextElement();
+				if (obj instanceof SearchResult) {
+					recordCount ++;
+					SearchResult si = (SearchResult) obj;
+					_logger.info("Sync Users  Record " + recordCount+" --------------------------------------------------");
+					_logger.trace("name " + si.getName());
+					_logger.info("NameInNamespace " + si.getNameInNamespace());
+					
+					HashMap<String,Attribute> attributeMap = new HashMap<String,Attribute>();
+					NamingEnumeration<? extends Attribute>  attrs = si.getAttributes().getAll();
+					while (null != attrs && attrs.hasMoreElements()) {
+						Attribute  objAttrs = attrs.nextElement();
+						_logger.trace("attribute "+objAttrs.getID() + " : " + objAttrs.get());
+						attributeMap.put(objAttrs.getID().toLowerCase(), objAttrs);
+					}
+					
+					UserInfo userInfo =buildUserInfo(attributeMap,si.getName(),si.getNameInNamespace());
+					
+					
+					_logger.info("userInfo " + userInfo);
+				}
+			}
+
+			//ldapUtils.close();
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	public void postSync(UserInfo userInfo) {
+		
+	}
+
+	public UserInfo buildUserInfo(HashMap<String,Attribute> attributeMap,String name,String nameInNamespace) {
+		UserInfo userInfo = new  UserInfo();
+		userInfo.setLdapDn(nameInNamespace);
+		try {
+			userInfo.setFormattedName(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.CN,attributeMap));//閸忋劌鎮�
+			//鐠愶附鍩�
+			userInfo.setUsername(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.SAMACCOUNTNAME,attributeMap));//鐠愶箑褰�
+			userInfo.setWindowsAccount(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.USERPRINCIPALNAME,attributeMap));//閻ц缍�
+			
+			//鐢瓕顫�
+			userInfo.setFamilyName(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.SN,attributeMap));//婵拷
+			userInfo.setGivenName(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.GIVENNAME,attributeMap));//閸氾拷
+			userInfo.setNickName(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.INITIALS,attributeMap));//閺勭數袨
+			userInfo.setNameZhShortSpell(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.INITIALS,attributeMap));//閼昏鲸鏋冪紓鈺佸晸
+			userInfo.setDisplayName(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.DISPLAYNAME,attributeMap));//閺勫墽銇氶崥宥囆�
+			userInfo.setDescription(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.DESCRIPTION,attributeMap));//閹诲繗鍫�
+			userInfo.setWorkPhoneNumber(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.TELEPHONENUMBER,attributeMap));//閻絻鐦介崣椋庣垳
+			userInfo.setWorkOfficeName(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.PHYSICALDELIVERYOFFICENAME,attributeMap));//閸旂偛鍙曠�癸拷
+			userInfo.setWorkEmail(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.MAIL,attributeMap));//闁喕娆�
+			userInfo.setWebSite(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.WWWHOMEPAGE,attributeMap));//缂冩垿銆�
+			//閸︽澘娼�
+			userInfo.setWorkCountry(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.CO,attributeMap));//閸ヨ棄顔�
+			userInfo.setWorkRegion(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.ST,attributeMap));//閻拷
+			userInfo.setWorkLocality(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.L,attributeMap));//閸橈拷
+			userInfo.setWorkStreetAddress(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.STREETADDRESS,attributeMap));//鐞涙浜�
+			userInfo.setWorkPostalCode(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.POSTALCODE,attributeMap));//闁喚绱�
+			userInfo.setWorkAddressFormatted(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.POSTOFFICEBOX,attributeMap));//闁喗鏂傞柇顔绢唸
+			
+			userInfo.setMobile(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.MOBILE,attributeMap));//閹靛婧�
+			userInfo.setHomePhoneNumber(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.HOMEPHONE,attributeMap));//鐎硅泛娑甸悽浣冪樈
+			userInfo.setWorkFax(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.FACSIMILETELEPHONENUMBER,attributeMap));//娴肩姷婀�
+			userInfo.setHomeAddressFormatted(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.INFO,attributeMap));//閻絻鐦芥径鍥ㄦ暈
+			
+			userInfo.setDivision(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.COMPANY,attributeMap)); //閸忣剙寰�
+			userInfo.setDepartment(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.DEPARTMENT,attributeMap)); //闁劑妫�
+			//userInfo.setDepartmentId(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.DEPARTMENT,attributeMap)); //闁劑妫紓鏍у娇
+			userInfo.setJobTitle(LdapUtils.getAttributeStringValue(ActiveDirectoryUser.TITLE,attributeMap));//閼卞苯濮�
+			
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		return userInfo;
+	}
+
+	public ActiveDirectoryUtils getLdapUtils() {
+		return ldapUtils;
+	}
+
+	public void setLdapUtils(ActiveDirectoryUtils ldapUtils) {
+		this.ldapUtils = ldapUtils;
+	}
+
+	public UserInfoService getUserInfoService() {
+		return userInfoService;
+	}
+
+	public void setUserInfoService(UserInfoService userInfoService) {
+		this.userInfoService = userInfoService;
+	}
+	
+	
+}

+ 85 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingAccessTokenService.java

@@ -0,0 +1,85 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.dingding;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiGettokenRequest;
+import com.dingtalk.api.response.OapiGettokenResponse;
+import com.taobao.api.ApiException;
+
+public class DingdingAccessTokenService {
+	final static Logger _logger = LoggerFactory.getLogger(DingdingAccessTokenService.class);
+	
+	String appkey;
+	
+	String appsecret;
+	
+	
+	public DingdingAccessTokenService(String appkey, String appsecret) {
+		super();
+		this.appkey = appkey;
+		this.appsecret = appsecret;
+	}
+
+
+	public String requestToken() throws ApiException {
+		DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+		OapiGettokenRequest request = new OapiGettokenRequest();
+		request.setAppkey(appkey);
+		request.setAppsecret(appsecret);
+		request.setHttpMethod("GET");
+		OapiGettokenResponse response = client.execute(request);
+		_logger.info("response : " + response.getBody());
+		
+		if(response.getErrcode()== 0){
+			return response.getAccessToken();
+		}
+		return "";
+	}
+	
+	
+	public String getAppkey() {
+		return appkey;
+	}
+
+
+	public void setAppkey(String appkey) {
+		this.appkey = appkey;
+	}
+
+
+	public String getAppsecret() {
+		return appsecret;
+	}
+
+
+	public void setAppsecret(String appsecret) {
+		this.appsecret = appsecret;
+	}
+
+
+	public static void main(String[] args) {
+		// TODO Auto-generated method stub
+
+	}
+
+}

+ 109 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingOrganizationService.java

@@ -0,0 +1,109 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.dingding;
+
+import org.maxkey.entity.Organizations;
+import org.maxkey.persistence.ldap.ActiveDirectoryUtils;
+import org.maxkey.persistence.service.OrganizationsService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiV2DepartmentListsubRequest;
+import com.dingtalk.api.response.OapiV2DepartmentListsubResponse;
+import com.dingtalk.api.response.OapiV2DepartmentListsubResponse.DeptBaseResponse;
+import com.taobao.api.ApiException;
+
+@Service
+public class DingdingOrganizationService   implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(DingdingOrganizationService.class);
+	
+	OapiV2DepartmentListsubResponse rspDepts;
+	
+	@Autowired
+	OrganizationsService organizationsService;
+	
+	String access_token;
+	
+	public void sync() {
+		_logger.info("Sync Organizations ...");
+	
+		try {			
+			OapiV2DepartmentListsubResponse rsp = requestDepartmentList(access_token);
+			
+			for(DeptBaseResponse dept : rsp.getResult()) {
+				_logger.info("dept : " + dept.getDeptId()+" "+ dept.getName()+" "+ dept.getParentId());
+				Organizations org = buildOrganization(dept);
+				_logger.info("Organizations : " + org);
+			}
+
+		} catch (ApiException e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	public OapiV2DepartmentListsubResponse requestDepartmentList(String access_token) throws ApiException {
+		DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");
+		OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
+		req.setDeptId(1L);
+		req.setLanguage("zh_CN");
+		rspDepts = client.execute(req, access_token);
+		_logger.info("response : " + rspDepts.getBody());
+		return rspDepts;
+	}
+	
+	
+	
+	public Organizations buildOrganization(DeptBaseResponse dept) {
+		Organizations org = new Organizations();
+		org.setId(dept.getDeptId()+"");
+		org.setName(dept.getName());
+		org.setParentId(dept.getParentId()+"");
+		org.setExtId(dept.getDeptId()+"");	
+		org.setExtParentId(dept.getParentId()+"");
+		return org;
+	}
+
+
+
+	public String getAccess_token() {
+		return access_token;
+	}
+
+	public void setAccess_token(String access_token) {
+		this.access_token = access_token;
+	}
+
+	public OapiV2DepartmentListsubResponse getRspDepts() {
+		return rspDepts;
+	}
+
+	public OrganizationsService getOrganizationsService() {
+		return organizationsService;
+	}
+
+	public void setOrganizationsService(OrganizationsService organizationsService) {
+		this.organizationsService = organizationsService;
+	}
+	
+	
+}

+ 81 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingSynchronizerService.java

@@ -0,0 +1,81 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.dingding;
+
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.taobao.api.ApiException;
+
+@Service
+public class DingdingSynchronizerService  implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(DingdingSynchronizerService.class);
+	
+	@Autowired
+	DingdingUsersService dingdingUsersService;
+	
+	@Autowired
+	DingdingOrganizationService dingdingOrganizationService;
+	
+
+	DingdingAccessTokenService dingdingAccessTokenService;
+	
+	public DingdingSynchronizerService() {
+		super();
+	}
+
+	public void sync() throws ApiException {
+		_logger.info("Sync ...");
+		
+		String access_token=dingdingAccessTokenService.requestToken();
+		
+		dingdingOrganizationService.setAccess_token(access_token);
+		dingdingOrganizationService.sync();
+		
+		dingdingUsersService.setAccess_token(access_token);
+		dingdingUsersService.sync();
+	}
+
+	public DingdingUsersService getDingdingUsersService() {
+		return dingdingUsersService;
+	}
+
+	public void setDingdingUsersService(DingdingUsersService dingdingUsersService) {
+		this.dingdingUsersService = dingdingUsersService;
+	}
+
+	public DingdingOrganizationService getDingdingOrganizationService() {
+		return dingdingOrganizationService;
+	}
+
+	public void setDingdingOrganizationService(DingdingOrganizationService dingdingOrganizationService) {
+		this.dingdingOrganizationService = dingdingOrganizationService;
+	}
+
+	public DingdingAccessTokenService getDingdingAccessTokenService() {
+		return dingdingAccessTokenService;
+	}
+
+	public void setDingdingAccessTokenService(DingdingAccessTokenService dingdingAccessTokenService) {
+		this.dingdingAccessTokenService = dingdingAccessTokenService;
+	}
+
+}

+ 129 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/dingding/DingdingUsersService.java

@@ -0,0 +1,129 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.dingding;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.maxkey.entity.UserInfo;
+import org.maxkey.persistence.service.UserInfoService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiV2UserListRequest;
+import com.dingtalk.api.response.OapiV2DepartmentListsubResponse;
+import com.dingtalk.api.response.OapiV2UserListResponse;
+import com.dingtalk.api.response.OapiV2DepartmentListsubResponse.DeptBaseResponse;
+import com.dingtalk.api.response.OapiV2UserListResponse.ListUserResponse;
+
+@Service
+public class DingdingUsersService   implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(DingdingUsersService.class);
+	
+	@Autowired
+	DingdingOrganizationService organizationService;
+	
+	@Autowired
+	UserInfoService userInfoService;
+	
+	String access_token;
+	
+	public void sync() {
+		_logger.info("Sync Users...");
+		try {
+			
+			OapiV2DepartmentListsubResponse rspDepts = organizationService.getRspDepts();
+			for(DeptBaseResponse dept : rspDepts.getResult()) {
+				DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/list");
+				OapiV2UserListRequest req = new OapiV2UserListRequest();
+				req.setDeptId(dept.getDeptId());
+				req.setCursor(0L);
+				req.setSize(100L);
+				req.setOrderField("modify_desc");
+				req.setContainAccessLimit(true);
+				req.setLanguage("zh_CN");
+				OapiV2UserListResponse rsp = client.execute(req, access_token);
+				_logger.info("response : " + rsp.getBody());
+				
+				if(rsp.getErrcode()==0) {
+					for(ListUserResponse user :rsp.getResult().getList()) {
+						_logger.info("name : " + user.getName()+" , "+user.getLoginId()+" , "+user.getUserid());
+						UserInfo userInfo  = buildUserInfo(user);
+						_logger.info("userInfo " + userInfo);
+					}
+				}
+			}
+
+			
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	public void postSync(UserInfo userInfo) {
+		
+	}
+
+	public UserInfo buildUserInfo(ListUserResponse user) {
+		UserInfo userInfo = new  UserInfo();
+
+		userInfo.setUsername(user.getUserid());//鐧诲綍鍚�
+		userInfo.setNickName(user.getName());//鐢ㄦ埛鍚�
+		userInfo.setDisplayName(user.getName());//鐢ㄦ埛鍚�
+		userInfo.setFormattedName(user.getName());//鐢ㄦ埛鍚�
+		
+		userInfo.setEmail(user.getEmail());
+		userInfo.setEntryDate(new DateTime(user.getHiredDate()).toString(DateTimeFormat.forPattern("yyyy-MM-dd")));
+		userInfo.setMobile(user.getMobile());//鎵嬫満
+		userInfo.setDepartmentId(user.getDeptIdList().get(0)+"");
+		userInfo.setJobTitle(user.getTitle());//鑱屽姟
+		userInfo.setWorkEmail(user.getOrgEmail());//宸ヤ綔閭欢
+		userInfo.setWorkPhoneNumber(user.getTelephone());//鍏徃鐢佃瘽
+		userInfo.setWorkOfficeName(user.getWorkPlace());//鍔炲叕瀹�
+		userInfo.setDescription(user.getRemark());//澶囨敞
+		
+		return userInfo;
+	}
+
+	public void setOrganizationService(DingdingOrganizationService organizationService) {
+		this.organizationService = organizationService;
+	}
+
+	public void setAccess_token(String access_token) {
+		this.access_token = access_token;
+	}
+
+	public UserInfoService getUserInfoService() {
+		return userInfoService;
+	}
+
+	public void setUserInfoService(UserInfoService userInfoService) {
+		this.userInfoService = userInfoService;
+	}
+
+	public DingdingOrganizationService getOrganizationService() {
+		return organizationService;
+	}
+
+
+	
+}

+ 78 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/entity/AccessToken.java

@@ -0,0 +1,78 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.entity;
+
+public class AccessToken {
+
+	int errcode;
+	String errmsg;
+	String access_token;
+	String expires_in;
+
+	public AccessToken() {
+		super();
+	}
+
+	public int getErrcode() {
+		return errcode;
+	}
+
+	public void setErrcode(int errcode) {
+		this.errcode = errcode;
+	}
+
+	public String getErrmsg() {
+		return errmsg;
+	}
+
+	public void setErrmsg(String errmsg) {
+		this.errmsg = errmsg;
+	}
+
+	public String getAccess_token() {
+		return access_token;
+	}
+
+	public void setAccess_token(String access_token) {
+		this.access_token = access_token;
+	}
+
+	public String getExpires_in() {
+		return expires_in;
+	}
+
+	public void setExpires_in(String expires_in) {
+		this.expires_in = expires_in;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("AccessToken [errcode=");
+		builder.append(errcode);
+		builder.append(", errmsg=");
+		builder.append(errmsg);
+		builder.append(", access_token=");
+		builder.append(access_token);
+		builder.append(", expires_in=");
+		builder.append(expires_in);
+		builder.append("]");
+		return builder.toString();
+	}
+
+}

+ 42 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/entity/ResponseData.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.entity;
+
+public class ResponseData {
+
+	protected long errcode;
+	protected String errmsg;
+	
+	public long getErrcode() {
+		return errcode;
+	}
+	public void setErrcode(long errcode) {
+		this.errcode = errcode;
+	}
+	public String getErrmsg() {
+		return errmsg;
+	}
+	public void setErrmsg(String errmsg) {
+		this.errmsg = errmsg;
+	}
+	public ResponseData() {
+		super();
+	}
+	
+	
+}

+ 127 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ldap/LdapOrganizationService.java

@@ -0,0 +1,127 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.ldap;
+
+import java.util.HashMap;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import org.maxkey.constants.ldap.OrganizationalUnit;
+import org.maxkey.entity.Organizations;
+import org.maxkey.persistence.ldap.LdapUtils;
+import org.maxkey.persistence.service.OrganizationsService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class LdapOrganizationService  implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(LdapOrganizationService.class);
+
+	LdapUtils ldapUtils;
+	
+	@Autowired
+	OrganizationsService organizationsService;
+	
+	public void sync() {
+		_logger.info("Sync Organizations ...");
+		
+		_logger.info("Sync Organizations ...");
+		try {
+			SearchControls constraints = new SearchControls();
+			constraints.setSearchScope(ldapUtils.getSearchScope());
+			NamingEnumeration<SearchResult> results = ldapUtils.getConnection()
+					.search(ldapUtils.getBaseDN(), "(&(objectClass=OrganizationalUnit))", constraints);
+			
+			long recordCount = 0;
+			while (null != results && results.hasMoreElements()) {
+				Object obj = results.nextElement();
+				if (obj instanceof SearchResult) {
+					recordCount ++;
+					SearchResult si = (SearchResult) obj;
+					_logger.info("Sync OrganizationalUnit  Record " + recordCount+" --------------------------------------------------");
+					_logger.trace("name " + si.getName());
+					_logger.info("NameInNamespace " + si.getNameInNamespace());
+					
+					HashMap<String,Attribute> attributeMap = new HashMap<String,Attribute>();
+					NamingEnumeration<? extends Attribute>  attrs = si.getAttributes().getAll();
+					while (null != attrs && attrs.hasMoreElements()) {
+						Attribute  objAttrs = attrs.nextElement();
+						_logger.trace("attribute "+objAttrs.getID() + " : " + objAttrs.get());
+						attributeMap.put(objAttrs.getID().toLowerCase(), objAttrs);
+					}
+					
+					Organizations org = buildOrganization(attributeMap,si.getName(),si.getNameInNamespace());
+					
+					_logger.info("Organizations " + org);
+				}
+			}
+			
+			//ldapUtils.close();
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	public Organizations buildOrganization(HashMap<String,Attribute> attributeMap,String name,String nameInNamespace) {
+		Organizations org = new Organizations();
+		org.setLdapDn(nameInNamespace);
+		try {
+			org.setName(LdapUtils.getAttributeStringValue(OrganizationalUnit.OU,attributeMap));
+
+			//org.setCountry(LdapUtils.getAttributeStringValue(OrganizationalUnit.CO,attributeMap));
+			org.setRegion(LdapUtils.getAttributeStringValue(OrganizationalUnit.ST,attributeMap));
+			org.setLocality(LdapUtils.getAttributeStringValue(OrganizationalUnit.L,attributeMap));
+			org.setStreet(LdapUtils.getAttributeStringValue(OrganizationalUnit.STREET,attributeMap));
+			org.setPostalCode(LdapUtils.getAttributeStringValue(OrganizationalUnit.POSTALCODE,attributeMap));
+			org.setAddress(LdapUtils.getAttributeStringValue(OrganizationalUnit.POSTALADDRESS,attributeMap));
+			org.setPhone(LdapUtils.getAttributeStringValue(OrganizationalUnit.TELEPHONENUMBER,attributeMap));
+			org.setFax(LdapUtils.getAttributeStringValue(OrganizationalUnit.FACSIMILETELEPHONENUMBER,attributeMap));
+			org.setDescription(LdapUtils.getAttributeStringValue(OrganizationalUnit.DESCRIPTION,attributeMap));
+			
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		return org;
+	}
+	
+
+	public LdapUtils getLdapUtils() {
+		return ldapUtils;
+	}
+
+	public void setLdapUtils(LdapUtils ldapUtils) {
+		this.ldapUtils = ldapUtils;
+	}
+
+	public OrganizationsService getOrganizationsService() {
+		return organizationsService;
+	}
+
+	public void setOrganizationsService(OrganizationsService organizationsService) {
+		this.organizationsService = organizationsService;
+	}
+	
+	
+}

+ 63 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ldap/LdapSynchronizerService.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.ldap;
+
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class LdapSynchronizerService  implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(LdapSynchronizerService.class);
+	
+	@Autowired
+	LdapUsersService ldapUsersService;
+	
+	@Autowired
+	LdapOrganizationService ldapOrganizationService;
+	
+	public LdapSynchronizerService() {
+		super();
+	}
+
+	public void sync() {
+		_logger.info("Sync ...");
+		ldapOrganizationService.sync();
+		ldapUsersService.sync();
+	}
+
+	public LdapUsersService getLdapUsersService() {
+		return ldapUsersService;
+	}
+
+	public void setLdapUsersService(LdapUsersService ldapUsersService) {
+		this.ldapUsersService = ldapUsersService;
+	}
+
+	public LdapOrganizationService getLdapOrganizationService() {
+		return ldapOrganizationService;
+	}
+
+	public void setLdapOrganizationService(LdapOrganizationService ldapOrganizationService) {
+		this.ldapOrganizationService = ldapOrganizationService;
+	}
+
+	
+}

+ 144 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/ldap/LdapUsersService.java

@@ -0,0 +1,144 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.ldap;
+
+import java.util.HashMap;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import org.maxkey.constants.ldap.InetOrgPerson;
+import org.maxkey.entity.UserInfo;
+import org.maxkey.persistence.ldap.LdapUtils;
+import org.maxkey.persistence.service.UserInfoService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class LdapUsersService  implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(LdapUsersService.class);
+
+	LdapUtils ldapUtils;
+	
+	@Autowired
+	UserInfoService userInfoService;
+	
+	public void sync() {
+		_logger.info("Sync Users...");
+		try {
+			SearchControls constraints = new SearchControls();
+			constraints.setSearchScope(ldapUtils.getSearchScope());
+			NamingEnumeration<SearchResult> results = ldapUtils.getConnection()
+					.search(ldapUtils.getBaseDN(), "(&(objectClass=inetOrgPerson))", constraints);
+			
+			while (null != results && results.hasMoreElements()) {
+				Object obj = results.nextElement();
+				if (obj instanceof SearchResult) {
+					SearchResult si = (SearchResult) obj;
+					_logger.trace("name " + si.getName());
+					_logger.info("NameInNamespace " + si.getNameInNamespace());
+					
+					HashMap<String,Attribute> attributeMap = new HashMap<String,Attribute>();
+					NamingEnumeration<? extends Attribute>  attrs = si.getAttributes().getAll();
+					while (null != attrs && attrs.hasMoreElements()) {
+						Attribute  objAttrs = attrs.nextElement();
+						_logger.trace("attribute "+objAttrs.getID() + " , " + objAttrs.get());
+						attributeMap.put(objAttrs.getID(), objAttrs);
+					}
+					
+					UserInfo userInfo  = buildUserInfo(attributeMap,si.getName(),si.getNameInNamespace());
+					_logger.info("userInfo " + userInfo);
+				}
+			}
+
+			//ldapUtils.close();
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	public void postSync(UserInfo userInfo) {
+		
+	}
+
+	public UserInfo buildUserInfo(HashMap<String,Attribute> attributeMap,String name,String nameInNamespace) {
+		UserInfo userInfo = new  UserInfo();
+		userInfo.setLdapDn(nameInNamespace);
+		
+		try {
+			userInfo.setFormattedName(LdapUtils.getAttributeStringValue(InetOrgPerson.CN,attributeMap));//閸忋劌鎮�
+			//鐠愶附鍩�
+			userInfo.setUsername(LdapUtils.getAttributeStringValue(InetOrgPerson.UID,attributeMap));//鐠愶箑褰�
+			userInfo.setFamilyName(LdapUtils.getAttributeStringValue(InetOrgPerson.SN,attributeMap));//婵拷
+			userInfo.setGivenName(LdapUtils.getAttributeStringValue(InetOrgPerson.GIVENNAME,attributeMap));//閸氾拷
+			userInfo.setNickName(LdapUtils.getAttributeStringValue(InetOrgPerson.INITIALS,attributeMap));//閺勭數袨
+			userInfo.setNameZhShortSpell(LdapUtils.getAttributeStringValue(InetOrgPerson.INITIALS,attributeMap));//閼昏鲸鏋冪紓鈺佸晸
+			userInfo.setDisplayName(LdapUtils.getAttributeStringValue(InetOrgPerson.DISPLAYNAME,attributeMap));//閺勫墽銇氶崥宥囆�
+			
+			userInfo.setEmployeeNumber(LdapUtils.getAttributeStringValue(InetOrgPerson.EMPLOYEENUMBER,attributeMap));
+			userInfo.setDepartment(LdapUtils.getAttributeStringValue(InetOrgPerson.OU,attributeMap));
+			userInfo.setDepartmentId(LdapUtils.getAttributeStringValue(InetOrgPerson.DEPARTMENTNUMBER,attributeMap));
+			userInfo.setJobTitle(LdapUtils.getAttributeStringValue(InetOrgPerson.TITLE,attributeMap));//閼卞苯濮�
+			userInfo.setWorkOfficeName(LdapUtils.getAttributeStringValue(InetOrgPerson.PHYSICALDELIVERYOFFICENAME,attributeMap));//閸旂偛鍙曠�癸拷
+			userInfo.setWorkEmail(LdapUtils.getAttributeStringValue(InetOrgPerson.MAIL,attributeMap));//闁喕娆�
+			userInfo.setWorkRegion(LdapUtils.getAttributeStringValue(InetOrgPerson.ST,attributeMap));//閻拷
+			userInfo.setWorkLocality(LdapUtils.getAttributeStringValue(InetOrgPerson.L,attributeMap));//閸橈拷
+			userInfo.setWorkStreetAddress(LdapUtils.getAttributeStringValue(InetOrgPerson.STREET,attributeMap));//鐞涙浜�
+			userInfo.setWorkPostalCode(LdapUtils.getAttributeStringValue(InetOrgPerson.POSTALCODE,attributeMap));//闁喚绱�
+			userInfo.setWorkAddressFormatted(LdapUtils.getAttributeStringValue(InetOrgPerson.POSTOFFICEBOX,attributeMap));//闁喗鏂傞柇顔绢唸
+			userInfo.setWorkFax(LdapUtils.getAttributeStringValue(InetOrgPerson.FACSIMILETELEPHONENUMBER,attributeMap));
+			
+			userInfo.setHomePhoneNumber(LdapUtils.getAttributeStringValue(InetOrgPerson.HOMEPHONE,attributeMap));//鐎硅泛娑甸悽浣冪樈
+			userInfo.setHomeAddressFormatted(LdapUtils.getAttributeStringValue(InetOrgPerson.HOMEPOSTALADDRESS,attributeMap));//閻絻鐦芥径鍥ㄦ暈
+			
+			userInfo.setMobile(LdapUtils.getAttributeStringValue(InetOrgPerson.MOBILE,attributeMap));//閹靛婧�
+			
+			userInfo.setPreferredLanguage(LdapUtils.getAttributeStringValue(InetOrgPerson.PREFERREDLANGUAGE,attributeMap));//鐠囶叀鈻�
+			
+			userInfo.setDescription(LdapUtils.getAttributeStringValue(InetOrgPerson.DESCRIPTION,attributeMap));//閹诲繗鍫�
+			
+		} catch (NamingException e) {
+			e.printStackTrace();
+		}
+		return userInfo;
+	}
+
+	
+	public LdapUtils getLdapUtils() {
+		return ldapUtils;
+	}
+
+	public void setLdapUtils(LdapUtils ldapUtils) {
+		this.ldapUtils = ldapUtils;
+	}
+
+	public UserInfoService getUserInfoService() {
+		return userInfoService;
+	}
+
+	public void setUserInfoService(UserInfoService userInfoService) {
+		this.userInfoService = userInfoService;
+	}
+	
+	
+}

+ 84 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinAccessTokenService.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin;
+
+import org.maxkey.synchronizer.entity.AccessToken;
+import org.maxkey.util.JsonUtils;
+import org.maxkey.web.HttpRequestAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WorkweixinAccessTokenService {
+	final static Logger _logger = LoggerFactory.getLogger(WorkweixinAccessTokenService.class);
+	
+	String corpid;
+	
+	String corpsecret;
+	
+	public static String TOKEN_URL="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
+
+
+
+	public WorkweixinAccessTokenService(String corpid, String corpsecret) {
+		super();
+		this.corpid = corpid;
+		this.corpsecret = corpsecret;
+	}
+
+
+	public String requestToken() {
+		HttpRequestAdapter request =new HttpRequestAdapter();
+		String responseBody = request.get(String.format(TOKEN_URL, corpid,corpsecret));
+		
+		AccessToken accessToken = JsonUtils.gson2Object(responseBody, AccessToken.class);
+		_logger.debug("accessToken " + accessToken);
+		if(accessToken.getErrcode()== 0){
+			return accessToken.getAccess_token();
+		}
+		return "";
+	}
+	
+	
+
+
+	public String getCorpid() {
+		return corpid;
+	}
+
+
+	public void setCorpid(String corpid) {
+		this.corpid = corpid;
+	}
+
+
+	public String getCorpsecret() {
+		return corpsecret;
+	}
+
+
+	public void setCorpsecret(String corpsecret) {
+		this.corpsecret = corpsecret;
+	}
+
+
+	public static void main(String[] args) {
+		// TODO Auto-generated method stub
+
+	}
+
+}

+ 106 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinOrganizationService.java

@@ -0,0 +1,106 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin;
+
+import org.maxkey.entity.Organizations;
+import org.maxkey.persistence.service.OrganizationsService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.maxkey.synchronizer.workweixin.entity.WorkWeixinDepts;
+import org.maxkey.synchronizer.workweixin.entity.WorkWeixinDeptsResponse;
+import org.maxkey.util.JsonUtils;
+import org.maxkey.web.HttpRequestAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WorkweixinOrganizationService implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(WorkweixinOrganizationService.class);
+	
+	@Autowired
+	OrganizationsService organizationsService;
+	
+	WorkWeixinDeptsResponse deptsResponse;
+	
+	String access_token;
+	
+	static String DEPTS_URL="https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=%s";
+	
+	public void sync() {
+		_logger.info("Sync Organizations ...");
+
+		try {			
+			WorkWeixinDeptsResponse rsp = requestDepartmentList(access_token);
+			
+			for(WorkWeixinDepts dept : rsp.getDepartment()) {
+				_logger.info("dept : " + dept.getId()+" "+ dept.getName()+" "+ dept.getParentid());
+			}
+
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	public WorkWeixinDeptsResponse requestDepartmentList(String access_token) {
+		HttpRequestAdapter request =new HttpRequestAdapter();
+		String responseBody = request.get(String.format(DEPTS_URL, access_token));
+		deptsResponse  =JsonUtils.gson2Object(responseBody, WorkWeixinDeptsResponse.class);
+		
+		_logger.info("response : " + responseBody);
+		for(WorkWeixinDepts dept : deptsResponse.getDepartment()) {
+			_logger.info("WorkWeixinDepts : " + dept);
+		}
+		return deptsResponse;
+	}
+	
+	public Organizations buildOrganization(WorkWeixinDepts dept) {
+		Organizations org = new Organizations();
+		org.setId(dept.getId()+"");
+		org.setName(dept.getName());
+		org.setParentId(dept.getParentid()+"");
+		org.setSortIndex(dept.getOrder()+"");
+		return org;
+	}
+
+	public String getAccess_token() {
+		return access_token;
+	}
+
+	public void setAccess_token(String access_token) {
+		this.access_token = access_token;
+	}
+
+	public WorkWeixinDeptsResponse getDeptsResponse() {
+		return deptsResponse;
+	}
+
+	public void setDeptsResponse(WorkWeixinDeptsResponse deptsResponse) {
+		this.deptsResponse = deptsResponse;
+	}
+
+	public OrganizationsService getOrganizationsService() {
+		return organizationsService;
+	}
+
+	public void setOrganizationsService(OrganizationsService organizationsService) {
+		this.organizationsService = organizationsService;
+	}
+
+}

+ 81 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinSynchronizerService.java

@@ -0,0 +1,81 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin;
+
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.taobao.api.ApiException;
+
+@Service
+public class WorkweixinSynchronizerService  implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(WorkweixinSynchronizerService.class);
+	
+	@Autowired
+	WorkweixinUsersService workweixinUsersService;
+	
+	@Autowired
+	WorkweixinOrganizationService workweixinOrganizationService;
+	
+
+	WorkweixinAccessTokenService workweixinAccessTokenService;
+	
+	public WorkweixinSynchronizerService() {
+		super();
+	}
+
+	public void sync() throws Exception {
+		_logger.info("Sync ...");
+		
+		String access_token=workweixinAccessTokenService.requestToken();
+		
+		workweixinOrganizationService.setAccess_token(access_token);
+		workweixinOrganizationService.sync();
+		
+		workweixinUsersService.setAccess_token(access_token);
+		workweixinUsersService.sync();
+	}
+
+	public WorkweixinUsersService getWorkweixinUsersService() {
+		return workweixinUsersService;
+	}
+
+	public void setWorkweixinUsersService(WorkweixinUsersService workweixinUsersService) {
+		this.workweixinUsersService = workweixinUsersService;
+	}
+
+	public WorkweixinOrganizationService getWorkweixinOrganizationService() {
+		return workweixinOrganizationService;
+	}
+
+	public void setWorkweixinOrganizationService(WorkweixinOrganizationService workweixinOrganizationService) {
+		this.workweixinOrganizationService = workweixinOrganizationService;
+	}
+
+	public WorkweixinAccessTokenService getWorkweixinAccessTokenService() {
+		return workweixinAccessTokenService;
+	}
+
+	public void setWorkweixinAccessTokenService(WorkweixinAccessTokenService workweixinAccessTokenService) {
+		this.workweixinAccessTokenService = workweixinAccessTokenService;
+	}
+
+}

+ 116 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinUsersService.java

@@ -0,0 +1,116 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin;
+
+import org.maxkey.entity.UserInfo;
+import org.maxkey.persistence.service.UserInfoService;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.maxkey.synchronizer.workweixin.entity.WorkWeixinDepts;
+import org.maxkey.synchronizer.workweixin.entity.WorkWeixinUsers;
+import org.maxkey.synchronizer.workweixin.entity.WorkWeixinUsersResponse;
+import org.maxkey.util.JsonUtils;
+import org.maxkey.web.HttpRequestAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WorkweixinUsersService  implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(WorkweixinUsersService.class);
+	
+	@Autowired
+	WorkweixinOrganizationService organizationService;
+	
+	@Autowired
+	UserInfoService userInfoService;
+	
+	String access_token;
+	
+	static String USERS_URL="https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=%s&department_id=%s&fetch_child=0";
+	
+	public void sync() {
+		_logger.info("Sync Users...");
+		try {
+			
+			for (WorkWeixinDepts dept : organizationService.getDeptsResponse().getDepartment()) {
+				HttpRequestAdapter request =new HttpRequestAdapter();
+				String responseBody = request.get(String.format(USERS_URL, access_token,dept.getId()));
+				WorkWeixinUsersResponse usersResponse  =JsonUtils.gson2Object(responseBody, WorkWeixinUsersResponse.class);
+				_logger.info("response : " + responseBody);
+				
+				for(WorkWeixinUsers user : usersResponse.getUserlist()) {
+					UserInfo userInfo  = buildUserInfo(user);
+					_logger.info("userInfo : " + userInfo);
+				}
+			}
+			
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	public void postSync(UserInfo userInfo) {
+		
+	}
+
+	public UserInfo buildUserInfo(WorkWeixinUsers user) {
+		UserInfo userInfo = new  UserInfo();
+		userInfo.setUsername(user.getUserid());//账号
+		userInfo.setNickName(user.getAlias());//名字
+		userInfo.setDisplayName(user.getName());//名字
+		
+		userInfo.setMobile(user.getMobile());//手机
+		userInfo.setEmail(user.getEmail());
+		userInfo.setGender(Integer.parseInt(user.getGender()));
+		
+		userInfo.setWorkPhoneNumber(user.getTelephone());//工作电话
+		userInfo.setDepartmentId(user.getMain_department()+"");
+		userInfo.setJobTitle(user.getPosition());//职务
+		userInfo.setWorkAddressFormatted(user.getAddress());//工作地点
+
+		//激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业。
+		userInfo.setStatus(user.getStatus());
+
+		return userInfo;
+	}
+
+	public void setOrganizationService(WorkweixinOrganizationService organizationService) {
+		this.organizationService = organizationService;
+	}
+
+	public void setAccess_token(String access_token) {
+		this.access_token = access_token;
+	}
+
+	public UserInfoService getUserInfoService() {
+		return userInfoService;
+	}
+
+	public void setUserInfoService(UserInfoService userInfoService) {
+		this.userInfoService = userInfoService;
+	}
+
+	public WorkweixinOrganizationService getOrganizationService() {
+		return organizationService;
+	}
+
+
+	
+}

+ 72 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinDepts.java

@@ -0,0 +1,72 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin.entity;
+
+public class WorkWeixinDepts {
+	
+	long id;
+	String name;
+	String name_en;
+	long parentid;
+	long order;
+
+	public WorkWeixinDepts() {
+		super();
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public void setId(long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getName_en() {
+		return name_en;
+	}
+
+	public void setName_en(String name_en) {
+		this.name_en = name_en;
+	}
+
+	public long getParentid() {
+		return parentid;
+	}
+
+	public void setParentid(long parentid) {
+		this.parentid = parentid;
+	}
+
+	public long getOrder() {
+		return order;
+	}
+
+	public void setOrder(long order) {
+		this.order = order;
+	}
+
+}

+ 40 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinDeptsResponse.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin.entity;
+
+import java.util.ArrayList;
+
+import org.maxkey.synchronizer.entity.ResponseData;
+
+public class WorkWeixinDeptsResponse extends ResponseData{
+
+	ArrayList<WorkWeixinDepts> department;
+
+	public ArrayList<WorkWeixinDepts> getDepartment() {
+		return department;
+	}
+
+	public void setDepartment(ArrayList<WorkWeixinDepts> department) {
+		this.department = department;
+	}
+
+	public WorkWeixinDeptsResponse() {
+		super();
+	}
+
+}

+ 245 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinUsers.java

@@ -0,0 +1,245 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin.entity;
+
+public class WorkWeixinUsers {
+
+	String userid;
+	String name;
+	String mobile;
+	long[] department;
+	long[] order;
+	String position;
+	String gender;
+	String email;
+	long[] is_leader_in_dept;
+	String avatar;
+	String thumb_avatar;
+	String telephone;
+	String alias;
+	int status;
+	int isleader;
+	int enable;
+	String address;
+	int hide_mobile;
+	String english_name;
+	String open_userid;
+	long main_department;
+
+	String qr_code;
+	String external_position;
+
+	public class ExtAttrs {
+
+		String type;
+		String name;
+		String text;
+
+	}
+
+	public String getUserid() {
+		return userid;
+	}
+
+	public void setUserid(String userid) {
+		this.userid = userid;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public long[] getDepartment() {
+		return department;
+	}
+
+	public void setDepartment(long[] department) {
+		this.department = department;
+	}
+
+	public long[] getOrder() {
+		return order;
+	}
+
+	public void setOrder(long[] order) {
+		this.order = order;
+	}
+
+	public String getPosition() {
+		return position;
+	}
+
+	public void setPosition(String position) {
+		this.position = position;
+	}
+
+	public String getGender() {
+		return gender;
+	}
+
+	public void setGender(String gender) {
+		this.gender = gender;
+	}
+
+	public String getEmail() {
+		return email;
+	}
+
+	public void setEmail(String email) {
+		this.email = email;
+	}
+
+
+
+	public long[] getIs_leader_in_dept() {
+		return is_leader_in_dept;
+	}
+
+	public void setIs_leader_in_dept(long[] is_leader_in_dept) {
+		this.is_leader_in_dept = is_leader_in_dept;
+	}
+
+	public String getAvatar() {
+		return avatar;
+	}
+
+	public void setAvatar(String avatar) {
+		this.avatar = avatar;
+	}
+
+	public String getThumb_avatar() {
+		return thumb_avatar;
+	}
+
+	public void setThumb_avatar(String thumb_avatar) {
+		this.thumb_avatar = thumb_avatar;
+	}
+
+	public String getTelephone() {
+		return telephone;
+	}
+
+	public void setTelephone(String telephone) {
+		this.telephone = telephone;
+	}
+
+	public String getAlias() {
+		return alias;
+	}
+
+	public void setAlias(String alias) {
+		this.alias = alias;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+
+	public int getHide_mobile() {
+		return hide_mobile;
+	}
+
+	public void setHide_mobile(int hide_mobile) {
+		this.hide_mobile = hide_mobile;
+	}
+
+	public String getEnglish_name() {
+		return english_name;
+	}
+
+	public void setEnglish_name(String english_name) {
+		this.english_name = english_name;
+	}
+
+	public String getOpen_userid() {
+		return open_userid;
+	}
+
+	public void setOpen_userid(String open_userid) {
+		this.open_userid = open_userid;
+	}
+
+	public long getMain_department() {
+		return main_department;
+	}
+
+	public void setMain_department(long main_department) {
+		this.main_department = main_department;
+	}
+
+	public String getQr_code() {
+		return qr_code;
+	}
+
+	public void setQr_code(String qr_code) {
+		this.qr_code = qr_code;
+	}
+
+	public String getExternal_position() {
+		return external_position;
+	}
+
+	public void setExternal_position(String external_position) {
+		this.external_position = external_position;
+	}
+
+	public int getIsleader() {
+		return isleader;
+	}
+
+	public void setIsleader(int isleader) {
+		this.isleader = isleader;
+	}
+
+	public int getEnable() {
+		return enable;
+	}
+
+	public void setEnable(int enable) {
+		this.enable = enable;
+	}
+
+	public WorkWeixinUsers() {
+		super();
+	}
+
+}

+ 40 - 0
maxkey-identitys/maxkey-synchronizers/src/main/java/org/maxkey/synchronizer/workweixin/entity/WorkWeixinUsersResponse.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.synchronizer.workweixin.entity;
+
+import java.util.ArrayList;
+import org.maxkey.synchronizer.entity.ResponseData;
+
+public class WorkWeixinUsersResponse  extends ResponseData{
+
+	
+	ArrayList<WorkWeixinUsers>userlist;
+
+	public WorkWeixinUsersResponse() {
+		super();
+	}
+
+	public ArrayList<WorkWeixinUsers> getUserlist() {
+		return userlist;
+	}
+
+	public void setUserlist(ArrayList<WorkWeixinUsers> userlist) {
+		this.userlist = userlist;
+	}
+	
+}

BIN=BIN
maxkey-lib/taobao-sdk-java-auto_1479188381469-20210326.jar


+ 33 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/mapper/SynchronizersMapper.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.persistence.mapper;
+
+import org.apache.mybatis.jpa.persistence.IJpaBaseMapper;
+import org.maxkey.entity.Synchronizers;
+
+/**
+ * @author Crystal.sea
+ *
+ */
+
+public  interface SynchronizersMapper extends IJpaBaseMapper<Synchronizers> {
+	
+}

+ 48 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/service/SynchronizersService.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.persistence.service;
+
+import org.apache.mybatis.jpa.persistence.JpaBaseService;
+import org.maxkey.entity.Synchronizers;
+import org.maxkey.persistence.mapper.SynchronizersMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class SynchronizersService  extends JpaBaseService<Synchronizers>{
+    final static Logger _logger = LoggerFactory.getLogger(SynchronizersService.class);
+    
+	public SynchronizersService() {
+		super(SynchronizersMapper.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.connsec.db.service.BaseService#getMapper()
+	 */
+	@Override
+	public SynchronizersMapper getMapper() {
+		// TODO Auto-generated method stub
+		return (SynchronizersMapper)super.getMapper();
+	}
+	
+	
+
+
+	
+}

+ 25 - 0
maxkey-persistence/src/main/resources/org/maxkey/persistence/mapper/xml/mysql/SynchronizersMapper.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.maxkey.persistence.mapper.SynchronizersMapper">
+
+	<sql id="where_statement">
+    	<if test="id != null and id != ''">
+			and	id	=	#{id}
+		</if> 
+		<if test="name != null and name != ''">
+			and name like '%${name}%'
+		</if>
+    </sql>
+    	
+	<select id="queryPageResults" parameterType="Synchronizers" resultType="Synchronizers">
+		select
+			*
+		from
+			mxk_synchronizers
+		where
+			 (1=1)	
+		<include refid="where_statement"/>
+	</select>	
+    
+   
+</mapper>

+ 4 - 0
maxkey-web-manage/build.gradle

@@ -14,6 +14,10 @@ dependencies {
 	
    	implementation project(":maxkey-protocols:maxkey-protocol-oauth-2.0")
    	implementation project(":maxkey-protocols:maxkey-protocol-saml-2.0")   
+   	
+   	//identity
    	implementation project(":maxkey-identitys:maxkey-identity-scim")   
    	implementation project(":maxkey-identitys:maxkey-identity-rest")	
+   	implementation project(":maxkey-identitys:maxkey-synchronizers")
+   	
 }

+ 96 - 0
maxkey-web-manage/src/main/java/org/maxkey/web/contorller/SynchronizersController.java

@@ -0,0 +1,96 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * 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 org.maxkey.web.contorller;
+
+import org.apache.mybatis.jpa.persistence.JpaPageResults;
+import org.maxkey.constants.ConstantsOperateMessage;
+import org.maxkey.entity.Synchronizers;
+import org.maxkey.persistence.service.SynchronizersService;
+import org.maxkey.web.WebContext;
+import org.maxkey.web.message.Message;
+import org.maxkey.web.message.MessageType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+
+@Controller
+@RequestMapping(value={"/synchronizers"})
+public class SynchronizersController {
+	final static Logger _logger = LoggerFactory.getLogger(SynchronizersController.class);
+	
+	@Autowired
+	@Qualifier("synchronizersService")
+	SynchronizersService synchronizerssService;
+
+	
+	
+	@RequestMapping(value={"/list"})
+	public ModelAndView groupsList(){
+		return new ModelAndView("synchronizers/synchronizersList");
+	}
+	
+	
+	
+	@RequestMapping(value = { "/grid" })
+	@ResponseBody
+	public JpaPageResults<Synchronizers> queryDataGrid(@ModelAttribute("synchronizers") Synchronizers synchronizers) {
+		_logger.debug(""+synchronizers);
+		return synchronizerssService.queryPageResults(synchronizers);
+	}
+
+	
+
+	
+	@RequestMapping(value = { "/forwardUpdate/{id}" })
+	public ModelAndView forwardUpdate(@PathVariable("id") String id) {
+		ModelAndView modelAndView=new ModelAndView("synchronizers/synchronizerUpdate");
+		Synchronizers synchronizers=synchronizerssService.get(id);
+		modelAndView.addObject("model",synchronizers);
+		return modelAndView;
+	}
+	
+
+	
+	/**
+	 * 修改
+	 * @param group
+	 * @return
+	 */
+	@ResponseBody
+	@RequestMapping(value={"/update"})  
+	public Message update(@ModelAttribute("synchronizers") Synchronizers synchronizers) {
+		_logger.debug("-update  synchronizers :" + synchronizers);
+		
+		if (synchronizerssService.update(synchronizers)) {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_SUCCESS),MessageType.success);
+			
+		} else {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_ERROR),MessageType.error);
+		}
+		
+	}
+
+}

+ 32 - 19
maxkey-web-manage/src/main/resources/messages/message.properties

@@ -42,6 +42,7 @@ common.text.status.validated=\u6709\u6548
 common.text.status.inactive=\u4e0d\u6d3b\u52a8
 common.text.status.activate=\u6d3b\u52a8
 common.text.status.approved=\u6279\u51c6
+common.text.status.apply=\u7533\u8BF7
 common.text.status.stop=\u505c\u6b62
 common.text.status.enabled=\u542f\u7528
 common.text.status.disabled=\u505c\u7528
@@ -166,7 +167,7 @@ userinfo.preferredLanguage=\u8bed\u97f3\u504f\u597d
 userinfo.timeZone=\u65f6\u533a
 userinfo.locale=\u8bed\u8a00\u9009\u62e9
 userinfo.employeeNumber=\u5458\u5de5\u7f16\u53f7
-userinfo.windowsAccount=AD\u57df\u8d26\u53f7
+userinfo.windowsAccount=Windows\u57df\u8d26\u53f7
 userinfo.organization=\u6240\u5c5e\u673a\u6784
 userinfo.division=\u5206\u652f\u673a\u6784
 userinfo.department=\u90e8\u95e8\u540d\u79f0
@@ -195,11 +196,16 @@ userinfo.homeFax=\u5bb6\u5ead\u4f20\u771f
 userinfo.homePhoneNumber=\u5bb6\u5ead\u7535\u8bdd
 userinfo.homeEmail=\u5bb6\u5ead\u90ae\u7bb1
 userinfo.ims=\u5373\u65f6\u901a\u8baf
-userinfo.status=\u7528\u6237\u72b6\u6001
-userinfo.status.active=\u5728\u518c\u4eba\u5458
-userinfo.status.withdrawn=\u79bb\u804c\u4eba\u5458
-userinfo.status.inactive=\u505c\u85aa\u7559\u804c\u4eba\u5458
-userinfo.status.retiree=\u9000\u4f11\u4eba\u5458
+userinfo.status=\u72b6\u6001
+userinfo.status.active=\u6D3B\u52A8
+userinfo.status.lock=\u9501\u5B9A
+userinfo.status.inactive=\u4E0D\u6D3B\u52A8
+userinfo.status.delete=\u5DF2\u5220\u9664
+userinfo.userstate=\u7528\u6237\u72b6\u6001
+userinfo.userstate.resident=\u5728\u518c\u4eba\u5458
+userinfo.userstate.withdrawn=\u79bb\u804c\u4eba\u5458
+userinfo.userstate.inactive=\u505c\u85aa\u7559\u804c\u4eba\u5458
+userinfo.userstate.retiree=\u9000\u4f11\u4eba\u5458
 
 userinfo.authnType=\u767b\u5f55\u65b9\u5f0f
 userinfo.authnType.authnType.1=\u666e\u901a\u767b\u5f55
@@ -325,18 +331,7 @@ apps.formbased.parameter.value=\u53c2\u6570\u503c
 apps.cas.info=CAS\u8ba4\u8bc1
 apps.cas.service=\u670d\u52a1
 apps.cas.callbackUrl=\u56de\u8c03\u5730\u5740
-#desktop
-apps.desktop.info=\u684c\u9762\u8ba4\u8bc1
-apps.desktop.programPath=\u5e94\u7528\u5b89\u88c5\u8def\u5f84
-apps.desktop.parameter=\u5e94\u7528\u53c2\u6570
-apps.desktop.usernameType=\u7528\u6237\u7c7b\u578b
-apps.desktop.usernameParameter=\u7528\u6237\u53c2\u6570
-apps.desktop.preUsername=\u7528\u6237\u540d\u524d\u671f
-apps.desktop.passwordType=\u51ed\u8bc1\u7c7b\u578b
-apps.desktop.passwordParameter=\u51ed\u8bc1\u53c2\u6570
-apps.desktop.prePassword=\u51ed\u8bc1\u524d\u671f
-apps.desktop.submitType=\u63d0\u4ea4\u7c7b\u578b
-apps.desktop.preSubmit=\u63d0\u4ea4\u524d\u671f
+apps.cas.expires=\u8fc7\u671f\u65f6\u95f4
 #tokenbased
 apps.tokenbased.info=\u4ee4\u724c\u8ba4\u8bc1
 apps.tokenbased.redirectUri=\u8ba4\u8bc1\u5730\u5740
@@ -456,6 +451,23 @@ account.appId=\u5e94\u7528\u7f16\u53f7
 account.appName=\u5e94\u7528\u540d\u79f0
 account.relatedUsername=\u7528\u6237\u8d26\u53f7
 account.relatedPassword=\u8d26\u53f7\u5bc6\u7801
+
+#synchronizers
+synchronizers.id=\u7F16\u53F7
+synchronizers.name=\u540C\u6B65\u5668\u540D\u79F0
+synchronizers.sourceType=\u6765\u6E90
+synchronizers.scheduler=\u4EFB\u52A1\u5B9A\u65F6
+synchronizers.filters=\u8FC7\u6EE4
+synchronizers.providerUrl=\u5730\u5740
+synchronizers.driverClass=\u9A71\u52A8
+synchronizers.principal=\u8D26\u53F7
+synchronizers.credentials=\u51ED\u8BC1
+synchronizers.basedn=\u57FA\u672Cdn
+synchronizers.ssl=\u652F\u6301ssl
+synchronizers.trustStore=\u8BC1\u4E66\u8DEF\u5F84
+synchronizers.trustStorePassword=\u8BC1\u4E66\u5BC6\u94A5
+synchronizers.resumeTime=\u6062\u590D\u65F6\u95F4
+synchronizers.suspendTime=\u6302\u8D77\u65F6\u95F4
  
 button.text.action=\u8bbf\u95ee
 button.text.visit=\u8bbf\u95ee
@@ -549,4 +561,5 @@ navs.role.member=\u89d2\u8272\u7528\u6237
 navs.role.permissions=\u89d2\u8272\u6743\u9650\u7ba1\u7406
 navs.resources=\u8d44\u6e90\u7ba1\u7406
 navs.adapters=\u9002\u914D\u5668\u6CE8\u518C
-navs.notices=\u901A\u77E5\u516C\u544A
+navs.notices=\u901A\u77E5\u516C\u544A
+navs.synchronizers=\u540C\u6B65\u5668

+ 35 - 19
maxkey-web-manage/src/main/resources/messages/message_en.properties

@@ -42,6 +42,7 @@ common.text.status.validated=validated
 common.text.status.inactive=inactive
 common.text.status.activate=activate
 common.text.status.approved=approved
+common.text.status.apply=apply
 common.text.status.stop=stop
 common.text.status.enabled=enabled
 common.text.status.disabled=disabled
@@ -195,11 +196,17 @@ userinfo.homeFax=homeFax
 userinfo.homePhoneNumber=homePhoneNumber
 userinfo.homeEmail=homeEmail
 userinfo.ims=IMS
-userinfo.status=userStatus
-userinfo.status.active=active
-userinfo.status.withdrawn=withdrawn
-userinfo.status.inactive=inactive
-userinfo.status.retiree=retiree
+
+userinfo.status=Status
+userinfo.status.active=Active
+userinfo.status.lock=Locked
+userinfo.status.inactive=Inactive
+userinfo.status.delete=Deleted
+userinfo.userstate=UserState
+userinfo.userstate.resident=Resident
+userinfo.userstate.withdrawn=Withdrawn
+userinfo.userstate.inactive=Inactive
+userinfo.userstate.retiree=Retiree
 
 userinfo.authnType=AuthenticationType
 userinfo.authnType.authnType.1=General login
@@ -322,20 +329,9 @@ apps.formbased.authorizeView=authorizeView
 
 #cas
 apps.cas.info=CAS Info
-apps.cas.service=service
+apps.cas.service=Service
 apps.cas.callbackUrl=CallbackUrl
-#desktop
-apps.desktop.info=Desktop Info
-apps.desktop.programPath=programPath
-apps.desktop.parameter=parameter
-apps.desktop.usernameType=usernameType
-apps.desktop.usernameParameter=usernameParameter
-apps.desktop.preUsername=preUsername
-apps.desktop.passwordType=passwordType
-apps.desktop.passwordParameter=passwordParameter
-apps.desktop.prePassword=prePassword
-apps.desktop.submitType=submitType
-apps.desktop.preSubmit=preSubmit
+apps.cas.expires=Expires
 #tokenbased
 apps.tokenbased.info=tokenbased Info
 apps.tokenbased.redirectUri=redirectUri
@@ -455,6 +451,25 @@ account.appId=appId
 account.appName=appName
 account.relatedUsername=relatedUsername
 account.relatedPassword=relatedPassword
+
+
+#synchronizers
+synchronizers.id=id
+synchronizers.name=name
+synchronizers.sourceType=sourceType
+synchronizers.scheduler=scheduler
+synchronizers.filters=filters
+synchronizers.providerUrl=providerUrl
+synchronizers.driverClass=driverClass
+synchronizers.principal=principal
+synchronizers.credentials=credentials
+synchronizers.basedn=basedn
+synchronizers.ssl=ssl
+synchronizers.trustStore=trustStore
+synchronizers.trustStorePassword=trustStorePassword
+synchronizers.resumeTime=resumeTime
+synchronizers.suspendTime=suspendTime
+
  
 button.text.action=Action
 button.text.visit=Visit
@@ -548,4 +563,5 @@ navs.role.member=RoleMember
 navs.role.permissions=Permissions
 navs.resources=Resources
 navs.adapters=Adapters
-navs.notices=Notices
+navs.notices=Notices
+navs.synchronizers=Synchronizers

+ 31 - 18
maxkey-web-manage/src/main/resources/messages/message_zh_CN.properties

@@ -42,6 +42,7 @@ common.text.status.validated=\u6709\u6548
 common.text.status.inactive=\u4e0d\u6d3b\u52a8
 common.text.status.activate=\u6d3b\u52a8
 common.text.status.approved=\u6279\u51c6
+common.text.status.apply=\u7533\u8BF7
 common.text.status.stop=\u505c\u6b62
 common.text.status.enabled=\u542f\u7528
 common.text.status.disabled=\u505c\u7528
@@ -166,7 +167,7 @@ userinfo.preferredLanguage=\u8bed\u97f3\u504f\u597d
 userinfo.timeZone=\u65f6\u533a
 userinfo.locale=\u8bed\u8a00\u9009\u62e9
 userinfo.employeeNumber=\u5458\u5de5\u7f16\u53f7
-userinfo.windowsAccount=AD\u57df\u8d26\u53f7
+userinfo.windowsAccount=Windows\u57df\u8d26\u53f7
 userinfo.organization=\u6240\u5c5e\u673a\u6784
 userinfo.division=\u5206\u652f\u673a\u6784
 userinfo.department=\u90e8\u95e8\u540d\u79f0
@@ -195,11 +196,16 @@ userinfo.homeFax=\u5bb6\u5ead\u4f20\u771f
 userinfo.homePhoneNumber=\u5bb6\u5ead\u7535\u8bdd
 userinfo.homeEmail=\u5bb6\u5ead\u90ae\u7bb1
 userinfo.ims=\u5373\u65f6\u901a\u8baf
-userinfo.status=\u7528\u6237\u72b6\u6001
-userinfo.status.active=\u5728\u518c\u4eba\u5458
-userinfo.status.withdrawn=\u79bb\u804c\u4eba\u5458
-userinfo.status.inactive=\u505c\u85aa\u7559\u804c\u4eba\u5458
-userinfo.status.retiree=\u9000\u4f11\u4eba\u5458
+userinfo.status=\u72b6\u6001
+userinfo.status.active=\u6D3B\u52A8
+userinfo.status.lock=\u9501\u5B9A
+userinfo.status.inactive=\u4E0D\u6D3B\u52A8
+userinfo.status.delete=\u5DF2\u5220\u9664
+userinfo.userstate=\u7528\u6237\u72b6\u6001
+userinfo.userstate.resident=\u5728\u518c\u4eba\u5458
+userinfo.userstate.withdrawn=\u79bb\u804c\u4eba\u5458
+userinfo.userstate.inactive=\u505c\u85aa\u7559\u804c\u4eba\u5458
+userinfo.userstate.retiree=\u9000\u4f11\u4eba\u5458
 
 userinfo.authnType=\u767b\u5f55\u65b9\u5f0f
 userinfo.authnType.authnType.1=\u666e\u901a\u767b\u5f55
@@ -324,18 +330,7 @@ apps.formbased.parameter.value=\u53c2\u6570\u503c
 apps.cas.info=CAS\u8ba4\u8bc1
 apps.cas.service=\u670d\u52a1
 apps.cas.callbackUrl=\u56de\u8c03\u5730\u5740
-#desktop
-apps.desktop.info=\u684c\u9762\u8ba4\u8bc1
-apps.desktop.programPath=\u5e94\u7528\u5b89\u88c5\u8def\u5f84
-apps.desktop.parameter=\u5e94\u7528\u53c2\u6570
-apps.desktop.usernameType=\u7528\u6237\u7c7b\u578b
-apps.desktop.usernameParameter=\u7528\u6237\u53c2\u6570
-apps.desktop.preUsername=\u7528\u6237\u540d\u524d\u671f
-apps.desktop.passwordType=\u51ed\u8bc1\u7c7b\u578b
-apps.desktop.passwordParameter=\u51ed\u8bc1\u53c2\u6570
-apps.desktop.prePassword=\u51ed\u8bc1\u524d\u671f
-apps.desktop.submitType=\u63d0\u4ea4\u7c7b\u578b
-apps.desktop.preSubmit=\u63d0\u4ea4\u524d\u671f
+apps.cas.expires=\u8fc7\u671f\u65f6\u95f4
 #tokenbased
 apps.tokenbased.info=\u4ee4\u724c\u8ba4\u8bc1
 apps.tokenbased.redirectUri=\u8ba4\u8bc1\u5730\u5740
@@ -449,6 +444,23 @@ resource.resourceUrl=\u8d44\u6e90\u5730\u5740
 resource.resourceAction=\u52a8\u4f5c
 resource.resourceStyle=\u6837\u5f0f
 
+#synchronizers
+synchronizers.id=\u7F16\u53F7
+synchronizers.name=\u540C\u6B65\u5668\u540D\u79F0
+synchronizers.sourceType=\u6765\u6E90
+synchronizers.scheduler=\u4EFB\u52A1\u5B9A\u65F6
+synchronizers.filters=\u8FC7\u6EE4
+synchronizers.providerUrl=\u5730\u5740
+synchronizers.driverClass=\u9A71\u52A8
+synchronizers.principal=\u8D26\u53F7
+synchronizers.credentials=\u51ED\u8BC1
+synchronizers.basedn=\u57FA\u672Cdn
+synchronizers.ssl=\u652F\u6301ssl
+synchronizers.trustStore=\u8BC1\u4E66\u8DEF\u5F84
+synchronizers.trustStorePassword=\u8BC1\u4E66\u5BC6\u94A5
+synchronizers.resumeTime=\u6062\u590D\u65F6\u95F4
+synchronizers.suspendTime=\u6302\u8D77\u65F6\u95F4
+
 #account
 account.username=\u7528\u6237\u540d
 account.displayName=\u7528\u6237\u59d3\u540d
@@ -550,3 +562,4 @@ navs.role.permissions=\u89d2\u8272\u6743\u9650\u7ba1\u7406
 navs.resources=\u8d44\u6e90\u7ba1\u7406
 navs.adapters=\u9002\u914D\u5668\u6CE8\u518C
 navs.notices=\u901A\u77E5\u516C\u544A
+navs.synchronizers=\u540C\u6B65\u5668

+ 3 - 1
maxkey-web-manage/src/main/resources/static/jquery/platform.common.js

@@ -128,7 +128,7 @@ $(function(){
 		}, settings || {});
 
 		var winContent="<iframe " +
-							"scrolling='no' " +
+							"scrolling='yes' " +
 							"frameborder='0' " +
 							"width='"+settings.width+"' " +
 							"height='"+settings.height+"' " +
@@ -439,6 +439,8 @@ $(function(){
 			
 			if($(this).attr("target")&&$(this).attr("target")=="forward"){
 				$.forward($(this).attr("wurl")+"/"+selectId);	
+			}if($(this).attr("target")&&$(this).attr("target")=="_blank"){
+				window.open($(this).attr("wurl")+"/"+selectId);	
 			}else{
 				var settings={
 						url		:	$(this).attr("wurl")+"/"+selectId,//window url

+ 7 - 0
maxkey-web-manage/src/main/resources/templates/views/apps/cas/appAdd.ftl

@@ -52,6 +52,13 @@ $(function(){
 								</td>
 							</tr>
 							<tr>
+								<th style="width:15%;"><@locale code="apps.cas.expires"/>:</th>
+								<td  colspan=3>
+									<input type="text" class="form-control" id="expires" name="expires"  title="" value=""  required="30"  />
+								</td>
+							</tr>
+							
+							<tr>
 								<td  colspan=4>
 									<input class="button"  id="status" type="hidden" name="status"  value="1"/>
     								<input class="button btn btn-primary mr-3"  id="submitBtn" type="submit" value="<@locale code="button.text.save" />"/>

+ 6 - 0
maxkey-web-manage/src/main/resources/templates/views/apps/cas/appUpdate.ftl

@@ -70,6 +70,12 @@ $(function(){
 									</td>
 								</tr>
 								<tr>
+									<th style="width:15%;"><@locale code="apps.cas.expires"/>:</th>
+									<td  colspan=3>
+										<input type="text" class="form-control" id="expires" name="expires"  title="" value="${model.expires}"  required="30"  />
+									</td>
+								</tr>
+								<tr>
 									<td  colspan=4>
 										<input class="button btn btn-primary mr-3"  id="submitBtn" type="submit" value="<@locale code="button.text.save" />"/>
 										<input class="button btn btn-secondary mr-3"  id="backBtn" type="button" value="<@locale code="button.text.cancel" />"/>		  

+ 15 - 1
maxkey-web-manage/src/main/resources/templates/views/layout/sidenav.ftl

@@ -60,7 +60,7 @@
    	</li> 
    	<li>
      	<a class="side-nav-menu has-arrow" href="#">
-       		<@locale code="navs.conf"/>
+       		<@locale code="navs.role.permissions"/>
        		<span class="fa fa-fw fa-cogs fa-lg"></span>
      	</a>
      	<ul>
@@ -89,6 +89,14 @@
                  <span class="fa fa-fw fa-check-square"></span>
              </a>
            </li>
+	    </ul>
+	</li>
+	<li>
+     	<a class="side-nav-menu has-arrow" href="#">
+       		<@locale code="navs.conf"/>
+       		<span class="fa fa-fw fa-cogs fa-lg"></span>
+     	</a>
+     	<ul>
            <li>
              <a class="side-nav-menu" href="<@base />/notices/list/">
                 <@locale code="navs.notices"/>  
@@ -96,6 +104,12 @@
              </a>
            </li>
            <li>
+             <a class="side-nav-menu" href="<@base />/synchronizers/list/">
+                <@locale code="navs.synchronizers"/>  
+                <span class="fa fa-fw fa-chain"></span>
+             </a>
+           </li>
+           <li>
              <a class="side-nav-menu" href="<@base />/apps/adapters/list/">
                 <@locale code="navs.adapters"/>  
                 <span class="fa fa-fw fa-chain"></span>

+ 179 - 0
maxkey-web-manage/src/main/resources/templates/views/synchronizers/synchronizerUpdate.ftl

@@ -0,0 +1,179 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<#include  "../layout/header.ftl"/>
+	<#include  "../layout/common.cssjs.ftl"/>
+<style   type="text/css">
+  .table th, .table td {
+    padding: .2rem;
+    vertical-align: middle;
+  }
+</style>
+</head>
+<body>
+<form id="actionForm"  method="post" type="label" autoclose="true"  action="<@base/>/synchronizers/update"  class="needs-validation" novalidate>
+	 <table  border="0" cellpadding="0" cellspacing="0" class="table table-bordered">
+		<tbody>
+		<tr style="display:none">
+			<th><@locale code="synchronizers.id" />:</th>
+			<td nowrap>
+				<input id="id" type="text" readonly name="id"  class="form-control"   value="${model.id}"/>
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.name" />:</th>
+			<td nowrap>
+				<input type="text" id="name" name="name" class="form-control" title="" value="${model.name}"  required="" />
+			</td>
+		</tr>
+		
+		<tr  style="display:none">
+			<th><@locale code="synchronizers.sourceType" />:</th>
+			<td nowrap>
+				<input type="text" id="sourceType" name="sourceType" class="form-control" title="" value="${model.sourceType}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.scheduler" />:</th>
+			<td nowrap>
+				<input type="text" id="scheduler" name="scheduler" class="form-control" title="" value="${model.scheduler!}"   />
+			</td>
+		</tr>
+	<#if "DINGDING"==model.sourceType || "WORKWEIXIN"==model.sourceType>
+		<tr>
+			<th><@locale code="synchronizers.principal" />:</th>
+			<td nowrap>
+				<input type="text" id="principal" name="principal" class="form-control" title="" value="${model.principal!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.credentials" />:</th>
+			<td nowrap>
+				<input type="text" id="credentials" name="credentials" class="form-control" title="" value="${model.credentials!}"  required="" />
+			</td>
+		</tr>
+	</#if>
+	<#if "JDBC"==model.sourceType>
+		<tr>
+			<th><@locale code="synchronizers.providerUrl" />:</th>
+			<td nowrap>
+				<input type="text" id="providerUrl" name="scheduler" class="form-control" title="" value="${model.providerUrl!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.driverClass" />:</th>
+			<td nowrap>
+				<input type="text" id="driverClass" name="driverClass" class="form-control" title="" value="${model.driverClass!}"   />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.principal" />:</th>
+			<td nowrap>
+				<input type="text" id="principal" name="principal" class="form-control" title="" value="${model.principal!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.credentials" />:</th>
+			<td nowrap>
+				<input type="text" id="credentials" name="credentials" class="form-control" title="" value="${model.credentials!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.filters" />:</th>
+			<td nowrap>
+				<textarea id="filters" name="filters" class="form-control"  rows="3" cols="20">${model.filters!}</textarea>
+			</td>
+		</tr>
+	</#if>
+	<#if "LDAP"==model.sourceType || "MSAD"==model.sourceType>
+		<tr>
+			<th><@locale code="synchronizers.providerUrl" />:</th>
+			<td nowrap>
+				<input type="text" id="providerUrl" name="providerUrl" class="form-control" title="" value="${model.providerUrl!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.principal" />:</th>
+			<td nowrap>
+				<input type="text" id="principal" name="principal" class="form-control" title="" value="${model.principal!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.credentials" />:</th>
+			<td nowrap>
+				<input type="text" id="credentials" name="credentials" class="form-control" title="" value="${model.credentials!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.basedn" />:</th>
+			<td nowrap>
+				<input type="text" id="basedn" name="basedn" class="form-control" title="" value="${model.basedn!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.msadDomain" />:</th>
+			<td nowrap>
+				<input type="text" id="msadDomain" name="msadDomain" class="form-control" title="" value="${model.msadDomain!}"  />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.ssl" />:</th>
+			<td nowrap>
+				<input type="text" id="ssl" name="ssl" class="form-control" title="" value="${model.ssl!}"  />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.trustStore" />:</th>
+			<td nowrap>
+				<input type="text" id="trustStore" name="trustStore" class="form-control" title="" value="${model.trustStore!}"  />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.trustStorePassword" />:</th>
+			<td nowrap>
+				<input type="text" id="trustStorePassword" name="trustStorePassword" class="form-control" title="" value="${model.trustStorePassword!}"  />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="synchronizers.filters" />:</th>
+			<td nowrap>
+				<textarea id="filters" name="filters" class="form-control"  rows="2" cols="20">${model.filters!}</textarea>
+			</td>
+		</tr>
+	</#if>
+		<tr>
+				<th><@locale code="synchronizers.resumeTime" />:</th>
+				<td nowrap>
+					<input type="text" id="resumeTime" name="resumeTime" class="form-control timepicker" title="" value="${model.resumeTime!}"   />
+				</td>
+			</tr>
+			<tr>
+				<th><@locale code="synchronizers.suspendTime" />:</th>
+				<td nowrap>
+					<input type="text" id="suspendTime" name="suspendTime" class="form-control timepicker" title="" value="${model.suspendTime!}"  />
+				</td>
+		</tr>
+
+		
+		<tr>
+                <th><@locale code="common.text.description" />:</th>
+                <td nowrap>
+                	<textarea id="description" name="description" class="form-control"  rows="4" cols="20">${model.description!}</textarea>
+                </td>
+        </tr>
+		<tr>
+			<td nowrap colspan="2"  class="center">
+				<input id="_method" type="hidden" name="_method"  value="post"/>
+				<input id="status" type="hidden" name="status"  value="1"/>
+	    		<input class="button btn btn-primary mr-3"  id="submitBtn" type="submit" value="<@locale code="button.text.save" />">
+  				<input class="button btn btn-secondary mr-3"  id="closeBtn"   type="button" value="<@locale code="button.text.cancel" />">	 
+			</td>
+		</tr>
+		</tbody>
+	  </table>
+</form>
+<div id="orgContent" class="menuContent" style="display:none; position: absolute;">
+	<ul id="orgsTree" class="ztree" style="margin-top:0; width:180px; height: 300px;"></ul>
+</div>
+</body>
+</html>

+ 122 - 0
maxkey-web-manage/src/main/resources/templates/views/synchronizers/synchronizersList.ftl

@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<#include  "../layout/header.ftl"/>
+	<#include  "../layout/common.cssjs.ftl"/>
+	<script type="text/javascript">	
+		function dynamicFormatter(value, row, index){
+	  		return value=='0'? '<@locale code="common.text.no" />':'<@locale code="common.text.yes" />';
+		};
+	</script>
+</head>
+
+<body> 
+<div class="app header-default side-nav-dark">
+<div class="layout">
+	<div class="header navbar">
+		<#include  "../layout/top.ftl"/>
+	</div>
+	
+	<div class="col-md-3 sidebar-nav side-nav" >
+ 		<#include  "../layout/sidenav.ftl"/>
+	</div>
+	<div class="page-container">
+	
+	<div class="main-content">
+		<div class="container-fluid">
+			<div class="breadcrumb-wrapper row">
+				<div class="col-12 col-lg-3 col-md-6">
+					<h4 class="page-title"><@locale code="navs.synchronizers"/></h4>
+				</div>
+				<div class="col-12 col-lg-9 col-md-6">
+					<ol class="breadcrumb float-right">
+						<li><a href="<@base/>/main"><@locale code="navs.home"/></a></li>
+						<li class="active">/ <@locale code="navs.synchronizers"/></li>
+					</ol>
+				</div>
+			</div>
+		</div>
+		<div class="container-fluid">
+			<div class="content-wrapper row">
+			<div class="col-12 grid-margin">
+				<div class="card">
+					<div class="card-body">
+		
+			<table  class="table table-bordered">
+ 				<tr>
+		 			<td width="120px"><@locale code="synchronizers.name"/>:</td>
+		 			<td width="375px">
+		 				<form id="basic_search_form">
+			 				<input class="form-control" type="text" name="name"  style ="width:150px;float:left;">
+			 				<input  class="button btn btn-primary mr-3"    id="searchBtn" type="button" size="50" value="<@locale code="button.text.search"/>">
+				 			<!--<input  class="button btn btn-secondary"  id="advancedSearchExpandBtn" type="button" size="50"  value="<@locale code="button.text.expandsearch"/>" expandValue="<@locale code="button.text.expandsearch"/>"  collapseValue="<@locale code="button.text.collapsesearch"/>">
+					 		-->
+					 	</form>
+		 			</td>
+		 			<td colspan="2">
+		 				<div id="tool_box_right">
+					 	<input class="button btn btn-info mr-3 " id="modifyBtn" type="button" value="<@locale code="button.text.edit"/>" 
+					 				wurl="<@base/>/synchronizers/forwardUpdate"
+					 				wwidth="550"
+						 		    wheight="600"
+					 		    	target="window"> 
+						</div>
+		 			</td>
+		 		</tr>
+		 	</table>
+		
+		 		
+ 	</div>
+ 	
+ 	<div id="advanced_search">
+ 		<form id="advanced_search_form">
+	 		
+	 	</form>
+ 	</div>
+		<table  data-url="<@base/>/synchronizers/grid"
+			id="datagrid"
+				data-toggle="table"
+				data-classes="table table-bordered table-hover table-striped"
+				data-click-to-select="true"
+				data-pagination="true"
+				data-total-field="records"
+				data-page-list="[10, 25, 50, 100]"
+				data-search="false"
+				data-locale="zh-CN"
+				data-query-params="dataGridQueryParams"
+				data-query-params-type="pageSize"
+				data-side-pagination="server">
+		<thead>
+			<tr>
+				<th data-checkbox="true"></th>
+				<th data-sortable="true" data-field="id"   data-visible="false">Id</th>
+				<th data-field="name"><@locale code="synchronizers.name"/></th>
+				<th data-field="description"><@locale code="common.text.description"/></th>
+				<th data-field="createdBy"    data-visible="false"><@locale code="common.text.createdby"/></th>
+				<th data-field="createdDate"  data-visible="false"><@locale code="common.text.createddate"/></th>
+				<th data-field="modifiedBy"   data-visible="false"><@locale code="common.text.modifiedby"/></th>
+				<th data-field="modifiedDate" data-visible="false"><@locale code="common.text.modifieddate"/></th>
+	
+			</tr>
+		</thead>
+	</table>
+	
+	
+</div>
+					</div>
+</div>
+	<footer class="content-footer">
+		<#include  "../layout/footer.ftl"/>
+	</footer>
+
+	</div>
+	
+	</div>
+</div>
+
+<div id="preloader">
+<div class="loader" id="loader-1"></div>
+</div>
+
+</body>
+</html>

+ 33 - 24
maxkey-web-manage/src/main/resources/templates/views/userinfo/userAdd.ftl

@@ -65,23 +65,39 @@
 			<td style="width:35%;">
 				<input class="form-control"  type="text" id="employeeNumber" name="employeeNumber"  title="" value=""/>
 			</td>
-			<td><@locale code="userinfo.userType" />:</td>
+			<td style="width:15%;"><@locale code="userinfo.windowsAccount" />:</td>
 			<td style="width:35%;">
-	
-				<select name="userType"   class="form-control" >
-						<option value="EMPLOYEE"  selected><@locale code="userinfo.userType.employee" /></option>
-						<option value="CONTRACTOR"  selected><@locale code="userinfo.userType.contractor" /></option>
-						<option value="CUSTOMER"  selected><@locale code="userinfo.userType.customer" /></option>
-						<option value="DEALER"  selected><@locale code="userinfo.userType.dealer" /></option>
-						<option value="SUPPLIER"  selected><@locale code="userinfo.userType.supplier" /></option>
-						<option value="PARTNER"  selected><@locale code="userinfo.userType.partner" /></option>
-						<option value="EXTERNAL"  selected><@locale code="userinfo.userType.external" /></option>
-						<option value="INTERN"  selected><@locale code="userinfo.userType.intern" /></option>
-						<option value="TEMP"  selected><@locale code="userinfo.userType.temp" /></option>
-				</select>
+				<input class="form-control"  type="text" id="windowsAccount" name="windowsAccount"  title="" value=""/>
 			</td>
-			
 		</tr>
+		
+	<tr>
+		<td><@locale code="userinfo.userType" />:</td>
+		<td style="width:35%;">
+
+			<select name="userType"   class="form-control" >
+					<option value="EMPLOYEE"  selected><@locale code="userinfo.userType.employee" /></option>
+					<option value="CONTRACTOR"  ><@locale code="userinfo.userType.contractor" /></option>
+					<option value="CUSTOMER"  ><@locale code="userinfo.userType.customer" /></option>
+					<option value="DEALER"  ><@locale code="userinfo.userType.dealer" /></option>
+					<option value="SUPPLIER"  ><@locale code="userinfo.userType.supplier" /></option>
+					<option value="PARTNER"  ><@locale code="userinfo.userType.partner" /></option>
+					<option value="EXTERNAL"  ><@locale code="userinfo.userType.external" /></option>
+					<option value="INTERN"  ><@locale code="userinfo.userType.intern" /></option>
+					<option value="TEMP"  ><@locale code="userinfo.userType.temp" /></option>
+			</select>
+		</td>
+		<td><@locale code="userinfo.userstate" />:</td>
+		<td style="width:35%;">
+			<select name="userState"   class="form-control" >
+					<option value="RESIDENT"  selected ><@locale code="userinfo.userstate.resident" /></option>
+					<option value="WITHDRAWN"  	><@locale code="userinfo.userstate.withdrawn" /></option>
+					<option value="RETIREE"  	><@locale code="userinfo.userstate.retiree" /></option>
+					<option value="INACTIVE"  	><@locale code="userinfo.userstate.inactive" /></option>
+			</select>
+		</td>
+		
+	</tr>
 	<tr>
 		<td colspan="4">&nbsp;
 		</td>
@@ -92,8 +108,8 @@
 		<td>
 			<input class="form-control"  type="text" required="" id="displayName" name="displayName"  title="" value=""/>
 		</td>
-		<td rowspan="4"><@locale code="userinfo.picture" />:</td>
-		<td rowspan="4">
+		<td rowspan="3"><@locale code="userinfo.picture" />:</td>
+		<td rowspan="3">
 			<img id="picture" width="150px" height="150px" src="<@base/>/static/images/uploadimage.jpg" />
 			<input type="file" id="pictureFile" name="pictureFile" style="display:none" />
 					</td>
@@ -115,18 +131,11 @@
 		<td>
 			<input class="form-control"  type="text" id="middleName" name="middleName"  title="" value=""/>
 		</td>
-		
-		
-	</tr>
-	<tr>
 		<td><@locale code="userinfo.nickName" />:</td>
 		<td>
 			<input class="form-control"  type="text" id="nickName" name="nickName"  title="" value=""/>
 		</td>
-		<td style="width:15%;"><@locale code="userinfo.windowsAccount" />:</td>
-			<td style="width:35%;">
-				<input class="form-control"  type="text" id="windowsAccount" name="windowsAccount"  title="" value=""/>
-		</td>
+		
 	</tr>
 	<tr>
 		<td><@locale code="userinfo.gender" />:</td>

+ 28 - 20
maxkey-web-manage/src/main/resources/templates/views/userinfo/userUpdate.ftl

@@ -54,20 +54,15 @@
 		<td style="width:35%;">
 			<select name="status"  id="status" class="form-control" >
 						<option value="1"   <#if 1==model.status>selected</#if>><@locale code="userinfo.status.active" /></option>
-						<option value="2"   <#if 2==model.status>selected</#if>><@locale code="userinfo.status.withdrawn" /></option>
-						<option value="3"   <#if 3==model.status>selected</#if>><@locale code="userinfo.status.inactive" /></option>
-						<option value="4"   <#if 4==model.status>selected</#if>><@locale code="userinfo.status.retiree" /></option>
+						<option value="2"   <#if 2==model.status>selected</#if>><@locale code="userinfo.status.inactive" /></option>
+						<option value="5"   <#if 5==model.status>selected</#if>><@locale code="userinfo.status.lock" /></option>
+						<option value="9"   <#if 9==model.status>selected</#if>><@locale code="userinfo.status.delete" /></option>
 				</select>
 		</td>
 	</tr>
 	<tr>
-			<td style="width:15%;"><@locale code="userinfo.employeeNumber" />:</td>
-			<td style="width:35%;">
-				<input class="form-control"  type="text" id="employeeNumber" name="employeeNumber"  title="" value="${model.employeeNumber!""!""}"/>
-			</td>
-			<td><@locale code="userinfo.userType" />:</td>
+			<td style="width:15%;"><@locale code="userinfo.userType" />:</td>
 			<td style="width:35%;">
-	
 				<select name="userType"   class="form-control" >
 						<option value="EMPLOYEE"  	<#if 'EMPLOYEE'==model.userType>selected</#if> ><@locale code="userinfo.userType.employee" /></option>
 						<option value="CONTRACTOR"  <#if 'CONTRACTOR'==model.userType>selected</#if>><@locale code="userinfo.userType.contractor" /></option>
@@ -80,8 +75,28 @@
 						<option value="TEMP"  		<#if 'TEMP'==model.userType>selected</#if>><@locale code="userinfo.userType.temp" /></option>
 				</select>
 			</td>
+			<td><@locale code="userinfo.userstate" />:</td>
+			<td style="width:35%;">
+				<select name="userState"   class="form-control" >
+						<option value="RESIDENT"  	<#if 'RESIDENT'==model.userState>selected</#if> ><@locale code="userinfo.userstate.resident" /></option>
+						<option value="WITHDRAWN"  	<#if 'WITHDRAWN'==model.userState>selected</#if>><@locale code="userinfo.userstate.withdrawn" /></option>
+						<option value="RETIREE"  	<#if 'RETIREE'==model.userState>selected</#if>><@locale code="userinfo.userstate.retiree" /></option>
+						<option value="INACTIVE"  	<#if 'INACTIVE'==model.userState>selected</#if>><@locale code="userinfo.userstate.inactive" /></option>
+				</select>
+			</td>
 			
-		</tr>
+	</tr>
+		<tr>
+			<td style="width:15%;"><@locale code="userinfo.employeeNumber" />:</td>
+			<td style="width:35%;">
+				<input class="form-control"  type="text" id="employeeNumber" name="employeeNumber"  title="" value="${model.employeeNumber!""!""}"/>
+			</td>
+			<td><@locale code="userinfo.windowsAccount" />:</td>
+			<td style="width:35%;">
+				<input class="form-control"  type="text" id="windowsAccount" name="windowsAccount"  title="" value="${model.windowsAccount!""}"/>
+			</td>
+			
+	</tr>
 	<tr>
 		<td colspan="4">&nbsp;
 		</td>
@@ -92,8 +107,8 @@
 		<td>
 			<input class="form-control"  type="text" id="displayName" name="displayName"  title="" value="${model.displayName!""}"/>
 		</td>
-		<td rowspan="4"><@locale code="userinfo.picture" />:</td>
-		<td rowspan="4">
+		<td rowspan="3"><@locale code="userinfo.picture" />:</td>
+		<td rowspan="3">
 			<img id="picture" width="150px" height="150px" src="<@base/>/static/images/uploadimage.jpg" />
 			<input type="file" id="pictureFile" name="pictureFile" style="display:none" />
 					</td>
@@ -115,18 +130,11 @@
 		<td>
 			<input class="form-control"  type="text" id="middleName" name="middleName"  title="" value="${model.middleName!""}"/>
 		</td>
-		
-		
-	</tr>
-	<tr>
 		<td><@locale code="userinfo.nickName" />:</td>
 		<td>
 			<input class="form-control"  type="text" id="nickName" name="nickName"  title="" value="${model.nickName!""}"/>
 		</td>
-		<td style="width:15%;"><@locale code="userinfo.windowsAccount" />:</td>
-			<td style="width:35%;">
-				<input class="form-control"  type="text" id="windowsAccount" name="windowsAccount"  title="" value="${model.windowsAccount!""}"/>
-		</td>
+		
 	</tr>
 	<tr>
 		<td><@locale code="userinfo.gender" />:</td>

+ 18 - 2
maxkey-web-manage/src/main/resources/templates/views/userinfo/usersList.ftl

@@ -11,6 +11,21 @@
    			return '<@locale code="userinfo.gender.male" />';
    		}
 	};
+	function statusFormatter(value, row, index){
+   		if(value==1){
+   			return '<@locale code="userinfo.status.active" />';
+   		}else if(value==2){
+   			return '<@locale code="userinfo.status.inactive" />';
+   		}else if(value==5){
+   			return '<@locale code="userinfo.status.lock" />';
+   		}else if(value==9){
+   			return '<@locale code="userinfo.status.delete" />';
+   		}else {
+   			return '<@locale code="userinfo.status.inactive" />';
+   		}
+	};
+	
+	
 		
 function onClick (event, treeId, treeNode) {
 	$("#departmentId").val(treeNode.data.id)
@@ -222,13 +237,13 @@ $(function () {
 						 <input class="button btn btn-success mr-3" id="addBtn" type="button" value="<@locale code="button.text.add"/>" 
 						 		    wurl="<@base/>/userinfo/forwardAdd"
 						 		    wwidth="960"
-						 		    wheight="600"
+						 		    wheight="620"
 					 		    	target="window">	    	
 					 		    	
 					 	<input class="button btn btn-info mr-3 " id="modifyBtn" type="button" value="<@locale code="button.text.edit"/>" 
 					 				wurl="<@base/>/userinfo/forwardUpdate"
 					 				wwidth="960"
-						 		    wheight="600"
+						 		    wheight="620"
 					 		    	target="window"> 
 					 	<input class="button btn btn-danger mr-3 "  id="deleteBtn" type="button" value="<@locale code="button.text.delete"/>"
 					 				wurl="<@base/>/userinfo/delete" />
@@ -305,6 +320,7 @@ $(function () {
 				<th data-field="mobile"  data-visible="false"><@locale code="userinfo.mobile"/></th>
 				<th data-field="email"   data-visible="false"><@locale code="userinfo.email"/></th>
 				<th data-field="gender" data-formatter="genderFormatter" ><@locale code="userinfo.gender"/></th>
+				<th data-field="status" data-formatter="statusFormatter" ><@locale code="userinfo.status"/></th>
 				</tr>
 			</thead>
 		</table>

+ 1 - 0
maxkey-web-maxkey/src/main/resources/messages/message.properties

@@ -84,6 +84,7 @@ userinfo.username=\u767b\u5f55\u8d26\u53f7
 userinfo.email=\u90ae\u7bb1\u5730\u5740
 userinfo.mobile=\u624b\u673a\u53f7\u7801
 userinfo.userType=\u7528\u6237\u7c7b\u578b
+userinfo.userstate=\u7528\u6237\u72B6\u6001
 userinfo.picture=\u5934\u50cf
 userinfo.familyName=\u59d3
 userinfo.givenName=\u540d

+ 1 - 0
maxkey-web-maxkey/src/main/resources/messages/message_en.properties

@@ -83,6 +83,7 @@ userinfo.username=username
 userinfo.email=email
 userinfo.mobile=mobile
 userinfo.userType=userType
+userinfo.userstate=UserState
 userinfo.picture=picture
 userinfo.familyName=familyName
 userinfo.givenName=givenName

+ 1 - 0
maxkey-web-maxkey/src/main/resources/messages/message_zh_CN.properties

@@ -84,6 +84,7 @@ userinfo.username=\u767b\u5f55\u8d26\u53f7
 userinfo.email=\u90ae\u7bb1\u5730\u5740
 userinfo.mobile=\u624b\u673a\u53f7\u7801
 userinfo.userType=\u7528\u6237\u7c7b\u578b
+userinfo.userstate=\u7528\u6237\u72B6\u6001
 userinfo.picture=\u5934\u50cf
 userinfo.familyName=\u59d3
 userinfo.givenName=\u540d

+ 6 - 0
maxkey-web-maxkey/src/main/resources/templates/views/profile/myProfile.ftl

@@ -47,6 +47,12 @@
 					</td>
 				</tr>
 				<tr>
+					<th style="width:15%;"><@locale code="userinfo.userstate" />:</th>
+					<td  style="width:35%;">
+						<input  class="form-control"  readonly type="text" id="userState" name="userState"  title="" value="${model.userState !}"/>
+					</td>
+				</tr>
+				<tr>
 					<th><@locale code="userinfo.displayName" />:</th>
 					<td>
 						<input class="form-control"  type="text" id="displayName" name="displayName"  title="" value="${model.displayName!}"  required="" />

+ 1 - 0
settings.gradle

@@ -16,6 +16,7 @@ include 'maxkey-authentications:maxkey-authentication-otp'
 //identity
 include 'maxkey-identitys:maxkey-identity-scim'
 include 'maxkey-identitys:maxkey-identity-rest'
+include 'maxkey-identitys:maxkey-synchronizers'
 
 //Protocol
 //include 'maxkey-protocols'