Browse Source

synchronizer jdbc & optimize

MaxKey 2 years ago
parent
commit
7367666adc
20 changed files with 746 additions and 281 deletions
  1. 58 0
      maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.java
  2. 52 0
      maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.java
  3. 27 1
      maxkey-common/src/main/java/org/maxkey/util/JdbcUtils.java
  4. 116 98
      maxkey-core/src/main/java/org/maxkey/entity/Synchronizers.java
  5. 1 1
      maxkey-synchronizers/maxkey-synchronizer-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryOrganizationService.java
  6. 1 1
      maxkey-synchronizers/maxkey-synchronizer-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectorySynchronizerService.java
  7. 2 2
      maxkey-synchronizers/maxkey-synchronizer-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryUsersService.java
  8. 117 36
      maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/JdbcOrganizationService.java
  9. 228 40
      maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/JdbcUsersService.java
  10. 2 2
      maxkey-synchronizers/maxkey-synchronizer-ldap/src/main/java/org/maxkey/synchronizer/ldap/LdapOrganizationService.java
  11. 1 1
      maxkey-synchronizers/maxkey-synchronizer-ldap/src/main/java/org/maxkey/synchronizer/ldap/LdapSynchronizerService.java
  12. 2 2
      maxkey-synchronizers/maxkey-synchronizer-ldap/src/main/java/org/maxkey/synchronizer/ldap/LdapUsersService.java
  13. 4 2
      maxkey-synchronizers/maxkey-synchronizer/src/main/java/org/maxkey/autoconfigure/SynchronizerAutoConfiguration.java
  14. 4 2
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/entity/Synchronizers.ts
  15. 1 1
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/entity/Users.ts
  16. 112 83
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/synchronizers/synchronizer-editer/synchronizer-editer.component.html
  17. 1 1
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/shared/consts.ts
  18. 5 2
      maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/en-US.json
  19. 6 3
      maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-CN.json
  20. 6 3
      maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-TW.json

+ 58 - 0
maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2022, MaxKey and/or its affiliates. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Please contact MaxKey, visit www.maxkey.top if you need additional
+ * information or have any questions.
+ */
+
+package org.maxkey.entity;
+
+public class DbTableColumn {
+	String column;
+	String type;
+	int precision;
+	int scale;
+
+	public DbTableColumn(String column, String type, int precision, int scale) {
+		super();
+		this.column = column;
+		this.type = type;
+		this.precision = precision;
+		this.scale = scale;
+	}
+
+	public String getColumn() {
+		return column;
+	}
+
+	public void setColumn(String column) {
+		this.column = column;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public int getPrecision() {
+		return precision;
+	}
+
+	public void setPrecision(int precision) {
+		this.precision = precision;
+	}
+
+	public int getScale() {
+		return scale;
+	}
+
+	public void setScale(int scale) {
+		this.scale = scale;
+	}
+
+}

+ 52 - 0
maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022, MaxKey and/or its affiliates. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Please contact MaxKey, visit www.maxkey.top if you need additional
+ * information or have any questions.
+ */
+
+package org.maxkey.entity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class DbTableMetaData {
+	String tableName;
+	
+	ArrayList<DbTableColumn> columns = new ArrayList<DbTableColumn>();
+	
+	HashMap<String,DbTableColumn> columnsMap = new HashMap<String,DbTableColumn>();
+
+	public DbTableMetaData(String tableName) {
+		super();
+		this.tableName = tableName;
+	}
+
+	public String getTableName() {
+		return tableName;
+	}
+
+	public void setTableName(String tableName) {
+		this.tableName = tableName;
+	}
+
+	public ArrayList<DbTableColumn> getColumns() {
+		return columns;
+	}
+
+	public void setColumns(ArrayList<DbTableColumn> columns) {
+		this.columns = columns;
+	}
+
+	public HashMap<String, DbTableColumn> getColumnsMap() {
+		return columnsMap;
+	}
+
+	public void setColumnsMap(HashMap<String, DbTableColumn> columnsMap) {
+		this.columnsMap = columnsMap;
+	}
+	
+	
+}

+ 27 - 1
maxkey-common/src/main/java/org/maxkey/util/JdbcUtils.java

@@ -20,9 +20,14 @@ package org.maxkey.util;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 
+import org.maxkey.entity.DbTableColumn;
+import org.maxkey.entity.DbTableMetaData;
+
+
 public class JdbcUtils {
 
 	public static Connection connect(String url, String user, String pwd, String driverClass) {
@@ -106,6 +111,27 @@ public class JdbcUtils {
 			}
 		}
 	}
-
+	
+	public static DbTableMetaData getMetaData(ResultSet rs) {
+		try {
+			ResultSetMetaData metaData = rs.getMetaData();
+			DbTableMetaData meta = new DbTableMetaData(metaData.getTableName(1));
+			int count = metaData.getColumnCount();
+			for (int i = 1; i <= count; i++) {
+				DbTableColumn column = new DbTableColumn(
+						metaData.getColumnName(i).toLowerCase(),
+						metaData.getColumnTypeName(i),
+						metaData.getPrecision(i),
+						metaData.getScale(i)
+						);
+				meta.getColumns().add(column);
+				meta.getColumnsMap().put(column.getColumn(), column);
+			}
+			return meta;
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
 	
 }

+ 116 - 98
maxkey-core/src/main/java/org/maxkey/entity/Synchronizers.java

@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
- 
 
 package org.maxkey.entity;
 
@@ -26,80 +25,85 @@ 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;
-
-    //同步时间范围(单位天)
+	private static final long serialVersionUID = 4660258495864814777L;
+	
+	@Id
+	@Column
+	@GeneratedValue(strategy = GenerationType.AUTO, generator = "snowflakeid")
+	String id;
+
+	@Length(max = 60)
+	@Column
+	String name;
+	@Column
+	String sourceType;
+	@Column
+	String resumeTime;
+	@Column
+	String suspendTime;
+	@Column
+	String scheduler;
+
+	// 同步时间范围(单位天)
 	@Column
 	Integer syncStartTime;
 
-    @Column
-    String providerUrl;
-    @Column
-    String driverClass;
-    @Column
-    String principal;
-    @Column
-    String credentials;
-    @Column
-    String basedn;
-    @Column
-    String msadDomain;
-    @Column
-    String sslSwitch;
-    @Column
-    String trustStore;
-    @Column
-    String trustStorePassword;
-    @Column
-    String description;
-    @Column
-    String createdBy;
-    @Column
-    String createdDate;
-    @Column
-    String modifiedBy;
-    @Column
-    String modifiedDate;
-    @Column
-    String status;
-    @Column
-    String service;
-    
+	@Column
+	String providerUrl;
+	@Column
+	String driverClass;
+	@Column
+	String principal;
+	@Column
+	String credentials;
+	@Column
+	String userBasedn;
+	@Column
+	String userFilters;
+	@Column
+	String orgBasedn;
+	@Column
+	String orgFilters;
+	@Column
+	String msadDomain;
+	@Column
+	String sslSwitch;
+	@Column
+	String trustStore;
+	@Column
+	String trustStorePassword;
+	@Column
+	String description;
+	@Column
+	String createdBy;
+	@Column
+	String createdDate;
+	@Column
+	String modifiedBy;
+	@Column
+	String modifiedDate;
+	@Column
+	String status;
+	@Column
+	String service;
+
 	@Column
 	private String instId;
 
 	private String instName;
 
-    public Synchronizers() {
-    }
-
-
+	public Synchronizers() {
+	}
 
-    public Synchronizers(String id) {
-        this.id = id;
-    }
+	public Synchronizers(String id) {
+		this.id = id;
+	}
 
 	public Integer getSyncStartTime() {
 		return syncStartTime;
@@ -125,14 +129,6 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		this.name = name;
 	}
 
-	public String getFilters() {
-		return filters;
-	}
-
-	public void setFilters(String filters) {
-		this.filters = filters;
-	}
-
 	public String getSourceType() {
 		return sourceType;
 	}
@@ -197,12 +193,36 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		this.credentials = credentials;
 	}
 
-	public String getBasedn() {
-		return basedn;
+	public String getUserBasedn() {
+		return userBasedn;
+	}
+
+	public void setUserBasedn(String userBasedn) {
+		this.userBasedn = userBasedn;
+	}
+
+	public String getUserFilters() {
+		return userFilters;
+	}
+
+	public void setUserFilters(String userFilters) {
+		this.userFilters = userFilters;
+	}
+
+	public String getOrgBasedn() {
+		return orgBasedn;
+	}
+
+	public void setOrgBasedn(String orgBasedn) {
+		this.orgBasedn = orgBasedn;
+	}
+
+	public String getOrgFilters() {
+		return orgFilters;
 	}
 
-	public void setBasedn(String basedn) {
-		this.basedn = basedn;
+	public void setOrgFilters(String orgFilters) {
+		this.orgFilters = orgFilters;
 	}
 
 	public String getMsadDomain() {
@@ -213,17 +233,15 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		this.msadDomain = msadDomain;
 	}
 
-	
-
 	public String getSslSwitch() {
-        return sslSwitch;
-    }
+		return sslSwitch;
+	}
 
-    public void setSslSwitch(String sslSwitch) {
-        this.sslSwitch = sslSwitch;
-    }
+	public void setSslSwitch(String sslSwitch) {
+		this.sslSwitch = sslSwitch;
+	}
 
-    public String getTrustStore() {
+	public String getTrustStore() {
 		return trustStore;
 	}
 
@@ -291,38 +309,26 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		return service;
 	}
 
-
-
 	public void setService(String service) {
 		this.service = service;
 	}
 
-
-
 	public String getInstId() {
 		return instId;
 	}
 
-
-
 	public void setInstId(String instId) {
 		this.instId = instId;
 	}
 
-
-
 	public String getInstName() {
 		return instName;
 	}
 
-
-
 	public void setInstName(String instName) {
 		this.instName = instName;
 	}
 
-
-
 	@Override
 	public String toString() {
 		StringBuilder builder = new StringBuilder();
@@ -330,8 +336,6 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		builder.append(id);
 		builder.append(", name=");
 		builder.append(name);
-		builder.append(", filters=");
-		builder.append(filters);
 		builder.append(", sourceType=");
 		builder.append(sourceType);
 		builder.append(", resumeTime=");
@@ -340,6 +344,8 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		builder.append(suspendTime);
 		builder.append(", scheduler=");
 		builder.append(scheduler);
+		builder.append(", syncStartTime=");
+		builder.append(syncStartTime);
 		builder.append(", providerUrl=");
 		builder.append(providerUrl);
 		builder.append(", driverClass=");
@@ -348,8 +354,14 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		builder.append(principal);
 		builder.append(", credentials=");
 		builder.append(credentials);
-		builder.append(", basedn=");
-		builder.append(basedn);
+		builder.append(", userBasedn=");
+		builder.append(userBasedn);
+		builder.append(", userFilters=");
+		builder.append(userFilters);
+		builder.append(", orgBasedn=");
+		builder.append(orgBasedn);
+		builder.append(", orgFilters=");
+		builder.append(orgFilters);
 		builder.append(", msadDomain=");
 		builder.append(msadDomain);
 		builder.append(", sslSwitch=");
@@ -370,6 +382,12 @@ public class Synchronizers extends JpaBaseEntity implements Serializable {
 		builder.append(modifiedDate);
 		builder.append(", status=");
 		builder.append(status);
+		builder.append(", service=");
+		builder.append(service);
+		builder.append(", instId=");
+		builder.append(instId);
+		builder.append(", instName=");
+		builder.append(instName);
 		builder.append("]");
 		return builder.toString();
 	}

+ 1 - 1
maxkey-synchronizers/maxkey-synchronizer-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryOrganizationService.java

@@ -121,7 +121,7 @@ public class ActiveDirectoryOrganizationService  extends AbstractSynchronizerSer
 		SearchControls constraints = new SearchControls();
 		constraints.setSearchScope(ldapUtils.getSearchScope());
 		String filter = "(&(objectClass=OrganizationalUnit))";
-		if(StringUtils.isNotBlank(this.getSynchronizer().getFilters())) {
+		if(StringUtils.isNotBlank(this.getSynchronizer().getOrgFilters())) {
 			//filter = this.getSynchronizer().getFilters();
 		}
 		

+ 1 - 1
maxkey-synchronizers/maxkey-synchronizer-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectorySynchronizerService.java

@@ -47,7 +47,7 @@ public class ActiveDirectorySynchronizerService   implements ISynchronizerServic
 		        synchronizer.getProviderUrl(),
 		        synchronizer.getPrincipal(),
 		        synchronizer.getCredentials(),
-		        synchronizer.getBasedn(),
+		        synchronizer.getUserBasedn(),
 		        synchronizer.getMsadDomain());
 		ldapUtils.openConnection();
 		

+ 2 - 2
maxkey-synchronizers/maxkey-synchronizer-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryUsersService.java

@@ -52,8 +52,8 @@ public class ActiveDirectoryUsersService extends AbstractSynchronizerService
 		try {
 			SearchControls constraints = new SearchControls();
 			constraints.setSearchScope(ldapUtils.getSearchScope());
-			String filter = StringUtils.isNotBlank(this.getSynchronizer().getFilters())?
-								getSynchronizer().getFilters() : "(&(objectClass=User))";
+			String filter = StringUtils.isNotBlank(this.getSynchronizer().getUserFilters())?
+								getSynchronizer().getUserFilters() : "(&(objectClass=User))";
 			NamingEnumeration<SearchResult> results = 
 					ldapUtils.getConnection().search(ldapUtils.getBaseDN(), filter, constraints);
 			

+ 117 - 36
maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/JdbcOrganizationService.java

@@ -1,5 +1,5 @@
 /*
- * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * Copyright [2022] [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.
@@ -19,8 +19,12 @@ package org.maxkey.synchronizer.jdbc;
 
 import java.sql.Connection;
 import java.sql.ResultSet;
+import java.sql.SQLException;
 import java.sql.Statement;
+
+import org.apache.commons.lang3.StringUtils;
 import org.maxkey.constants.ConstsStatus;
+import org.maxkey.entity.DbTableMetaData;
 import org.maxkey.entity.Organizations;
 import org.maxkey.synchronizer.AbstractSynchronizerService;
 import org.maxkey.synchronizer.ISynchronizerService;
@@ -32,54 +36,131 @@ import org.springframework.stereotype.Service;
 @Service
 public class JdbcOrganizationService  extends AbstractSynchronizerService  implements ISynchronizerService{
 	final static Logger _logger = LoggerFactory.getLogger(JdbcOrganizationService.class);
-
-	 Connection conn = null;
-     Statement  stmt = null;
-     ResultSet  rs 	 = null;
-	
-     String querySql = "select * from org";
 	
 	public void sync() {
+		Connection conn = null;
+	    Statement  stmt = null;
+	    ResultSet  rs 	 = null;
 		try {
-			conn = JdbcUtils.connect(
-					synchronizer.getProviderUrl(), 
-					synchronizer.getPrincipal(), 
-					synchronizer.getCredentials(), 
-					synchronizer.getDriverClass());
-			
-			stmt = conn.createStatement();
-			rs = stmt.executeQuery(querySql);
-			while(rs.next()) {
-				Organizations queryOrg = this.organizationsService.get(rs.getString("id"));
-				if(queryOrg == null) {
+			if(StringUtils.isNotBlank(synchronizer.getOrgFilters())){
+				_logger.info("Sync Org Filters {}",synchronizer.getOrgFilters());
+				conn = JdbcUtils.connect(
+						synchronizer.getProviderUrl(), 
+						synchronizer.getPrincipal(), 
+						synchronizer.getCredentials(), 
+						synchronizer.getDriverClass());
+				
+				stmt = conn.createStatement();
+				rs = stmt.executeQuery(synchronizer.getOrgFilters());
+				while(rs.next()) {
 					Organizations org = buildOrganization(rs);
-					organizationsService.insert(org);
-				}else{
-					//this.organizationsService.update(org);
+					Organizations queryOrg = this.organizationsService.get(org.getId());
+					if(queryOrg == null) {
+						organizationsService.insert(org);
+					}else{
+						this.organizationsService.update(org);
+					}
 				}
 			}
-			JdbcUtils.release(conn, stmt, rs);
 		} catch (Exception e) {
 			_logger.error("Exception " , e);
+		}finally {
+			JdbcUtils.release(conn, stmt, rs);
 		}
 	}
 	
 	
-	public Organizations buildOrganization(ResultSet  rs) {
-		try {
-		    Organizations org = new Organizations();
-		
-			org.setId(org.generateId());
-			org.setOrgCode(rs.getString("code"));
-			org.setOrgName(rs.getString("orgname"));
-			org.setInstId(this.synchronizer.getInstId());
+	public Organizations buildOrganization(ResultSet  rs) throws SQLException {
+		DbTableMetaData meta = JdbcUtils.getMetaData(rs);
+	    Organizations org = new Organizations();
+	    if(meta.getColumnsMap().containsKey("id")) {
+	    	org.setId(org.generateId());
+	    }
+	    if(meta.getColumnsMap().containsKey("orgcode")) {
+	    	org.setOrgCode(rs.getString("orgcode"));
+	    }
+	    if(meta.getColumnsMap().containsKey("orgname")) {
+	    	org.setOrgName(rs.getString("orgname"));
+	    }
+	    if(meta.getColumnsMap().containsKey("fullname")) {
+	    	org.setFullName(rs.getString("fullname"));
+	    }
+		//parent
+	    if(meta.getColumnsMap().containsKey("parentid")) {
+	    	org.setParentId(rs.getString("parentid"));
+	    }
+	    if(meta.getColumnsMap().containsKey("parentcode")) {
+	    	org.setParentCode(rs.getString("parentcode"));
+	    }
+	    if(meta.getColumnsMap().containsKey("parentname")) {
+	    	org.setParentName(rs.getString("parentname"));
+	    }
+		//ex attr
+	    if(meta.getColumnsMap().containsKey("type")) {
+	    	org.setType(rs.getString("type"));
+	    }
+	    if(meta.getColumnsMap().containsKey("codepath")) {
+	    	org.setCodePath(rs.getString("codepath"));
+	    }
+	    if(meta.getColumnsMap().containsKey("namepath")) {
+	    	org.setNamePath(rs.getString("namepath"));
+	    }
+	    if(meta.getColumnsMap().containsKey("level")) {
+	    	org.setLevel(rs.getInt("level"));
+	    }
+	    if(meta.getColumnsMap().containsKey("haschild")) {
+	    	org.setHasChild(rs.getString("haschild"));
+	    }
+	    if(meta.getColumnsMap().containsKey("division")) {
+	    	org.setDivision(rs.getString("division"));
+	    }
+	    if(meta.getColumnsMap().containsKey("country")) {
+	    	org.setCountry(rs.getString("country"));
+	    }
+	    if(meta.getColumnsMap().containsKey("region")) {
+	    	org.setRegion(rs.getString("region"));
+	    }
+	    if(meta.getColumnsMap().containsKey("locality")) {
+	    	org.setLocality(rs.getString("locality"));
+	    }
+	    if(meta.getColumnsMap().containsKey("street")) {
+	    	org.setStreet(rs.getString("street"));
+	    }
+	    if(meta.getColumnsMap().containsKey("address")) {
+	    	org.setAddress(rs.getString("address"));
+	    }
+	    if(meta.getColumnsMap().containsKey("contact")) {
+	    	org.setContact(rs.getString("contact"));
+	    }
+	    if(meta.getColumnsMap().containsKey("postalcode")) {
+	    	org.setPostalCode(rs.getString("postalcode"));
+	    }
+	    if(meta.getColumnsMap().containsKey("phone")) {
+	    	org.setPhone(rs.getString("phone"));
+	    }
+	    if(meta.getColumnsMap().containsKey("fax")) {
+	    	org.setFax(rs.getString("fax"));
+	    }
+	    if(meta.getColumnsMap().containsKey("email")) {
+	    	org.setEmail(rs.getString("email"));
+	    }
+	    if(meta.getColumnsMap().containsKey("sortindex")) {
+	    	org.setSortIndex(rs.getInt("sortindex"));
+	    }
+	    if(meta.getColumnsMap().containsKey("ldapdn")) {
+	    	org.setLdapDn(rs.getString("ldapdn"));
+	    }
+	    if(meta.getColumnsMap().containsKey("description")) {
+	    	org.setDescription(rs.getString("description"));
+	    }
+		org.setInstId(this.synchronizer.getInstId());
+		if(meta.getColumnsMap().containsKey("status")) {
+			org.setStatus(rs.getInt("status"));
+		}else {
 			org.setStatus(ConstsStatus.ACTIVE);
-			
-			_logger.debug("Organization " + org);
-			return org;
-		} catch (Exception e) {
-			_logger.error("NamingException " , e);
 		}
-		return null;
+		
+		_logger.debug("Organization {}" , org);
+		return org;
 	}
 }

+ 228 - 40
maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/JdbcUsersService.java

@@ -23,6 +23,7 @@ import java.sql.SQLException;
 import java.sql.Statement;
 
 import org.maxkey.constants.ConstsStatus;
+import org.maxkey.entity.DbTableMetaData;
 import org.maxkey.entity.UserInfo;
 import org.maxkey.synchronizer.AbstractSynchronizerService;
 import org.maxkey.synchronizer.ISynchronizerService;
@@ -36,61 +37,248 @@ import org.springframework.stereotype.Service;
 public class JdbcUsersService extends AbstractSynchronizerService    implements ISynchronizerService{
 	final static Logger _logger = LoggerFactory.getLogger(JdbcUsersService.class);
 
-	 Connection conn = null;
-     Statement  stmt = null;
-     ResultSet  rs 	 = null;
-	
-     String querySql = "select * from account";
 	public void sync() {
 		_logger.info("Sync Jdbc Users...");
+		Connection conn = null;
+		Statement  stmt = null;
+		ResultSet  rs 	 = null;
+		
 		try {
-			conn = JdbcUtils.connect(
-					synchronizer.getProviderUrl(), 
-					synchronizer.getPrincipal(), 
-					synchronizer.getCredentials(), 
-					synchronizer.getDriverClass());
-			
-			stmt = conn.createStatement();
-			rs = stmt.executeQuery(querySql);
-			long insertCount = 0;
-			long updateCount = 0;
-			long readCount 	 = 0;
-			while(rs.next()) {
-				UserInfo queryUser = this.userInfoService.findByUsername(rs.getString("mobile"));
-				readCount ++;
-				if(queryUser == null) {
+			if(StringUtils.isNotBlank(synchronizer.getOrgFilters())){
+				_logger.info("Sync User Filters {}",synchronizer.getOrgFilters());
+				conn = JdbcUtils.connect(
+						synchronizer.getProviderUrl(), 
+						synchronizer.getPrincipal(), 
+						synchronizer.getCredentials(), 
+						synchronizer.getDriverClass());
+				
+				stmt = conn.createStatement();
+				rs = stmt.executeQuery(synchronizer.getUserFilters());
+				long insertCount = 0;
+				long updateCount = 0;
+				long readCount 	 = 0;
+				while(rs.next()) {
 					UserInfo user = buildUserInfo(rs);
-					if(StringUtils.isBlank(rs.getString("password"))) {
-						user.setPassword(rs.getString("mobile"));
-						userInfoService.insert(user,true);
-					}else {
-						user.setPassword("{bcrypt}"+rs.getString("password"));
-						userInfoService.insert(user,false);
+					UserInfo queryUser = this.userInfoService.findByUsername(user.getUsername());
+					readCount ++;
+					if(queryUser == null) {
+						if(user.getPassword().indexOf("{") > -1 && user.getPassword().indexOf("}") > -1) {
+							userInfoService.insert(user,false);
+						}else {
+							//passwordEncoder
+							userInfoService.insert(user,true);
+						}
+						user.setBadPasswordCount(1);
+						insertCount++;
+					}else{
+						//no need update password , set null
+						user.setPassword(null);
+						userInfoService.update(user);
+						updateCount++;
 					}
-					insertCount++;
-				}else{
-					//userInfoService.update(queryUser);
-					updateCount++;
+					_logger.trace("read Count {} , insert Count {} , updateCount {} " , readCount,insertCount,updateCount);
 				}
-				_logger.debug("read Count {} , insert Count {} , updateCount {} " , readCount,insertCount,updateCount);
+				_logger.info("read Count {} , insert Count {} , updateCount {} " , readCount,insertCount,updateCount);
 			}
-			_logger.info("read Count {} , insert Count {} , updateCount {} " , readCount,insertCount,updateCount);
-			JdbcUtils.release(conn, stmt, rs);
 		} catch (Exception e) {
 			_logger.error("Exception " , e);
+		}finally {
+			JdbcUtils.release(conn, stmt, rs);
 		}
 	}
 	
 	public UserInfo buildUserInfo(ResultSet  rs) throws SQLException {
+		DbTableMetaData meta = JdbcUtils.getMetaData(rs);
 		UserInfo user = new UserInfo();
-		user.setId(rs.getString("id"));
-		user.setUsername(rs.getString("mobile"));
-		user.setDisplayName(rs.getString("nickname"));
-		user.setNickName(rs.getString("nickname"));
-		user.setMobile(rs.getString("mobile"));
-		user.setEmail(rs.getString("email"));
+		//basic
+		if(meta.getColumnsMap().containsKey("id")) {
+			user.setId(rs.getString("id"));
+		}
+		if(meta.getColumnsMap().containsKey("username")) {
+			user.setUsername(rs.getString("username"));
+		}
+		if(meta.getColumnsMap().containsKey("picture")) {
+			user.setPicture(rs.getBytes("picture"));
+		}
+		if(meta.getColumnsMap().containsKey("displayname")) {
+			user.setDisplayName(rs.getString("displayname"));
+		}
+		if(meta.getColumnsMap().containsKey("nickname")) {
+			user.setNickName(rs.getString("nickname"));
+		}
+		if(meta.getColumnsMap().containsKey("mobile")) {
+			user.setMobile(rs.getString("mobile"));
+		}
+		if(meta.getColumnsMap().containsKey("email")) {
+			user.setEmail(rs.getString("email"));
+		}
+		if(meta.getColumnsMap().containsKey("birthdate")) {
+			user.setBirthDate(rs.getString("birthdate"));
+		}
+		if(meta.getColumnsMap().containsKey("usertype")) {
+			user.setUserType(rs.getString("usertype"));
+		}
+		if(meta.getColumnsMap().containsKey("userstate")) {
+			user.setUserState(rs.getString("userstate"));
+		}
+		if(meta.getColumnsMap().containsKey("windowsaccount")) {
+			user.setWindowsAccount(rs.getString("windowsaccount"));
+		}
+		if(meta.getColumnsMap().containsKey("givenname")) {
+			user.setGivenName(rs.getString("givenname"));
+		}
+		if(meta.getColumnsMap().containsKey("middlename")) {
+			user.setMiddleName(rs.getString("middlename"));
+		}
+		if(meta.getColumnsMap().containsKey("married")) {
+			user.setMarried(rs.getInt("married"));
+		}
+		if(meta.getColumnsMap().containsKey("gender")) {
+			user.setGender(rs.getInt("gender"));
+		}
+		if(meta.getColumnsMap().containsKey("idtype")) {
+			user.setIdType(rs.getInt("idtype"));
+		}
+		if(meta.getColumnsMap().containsKey("idcardno")) {
+			user.setIdCardNo(rs.getString("idcardno"));
+		}
+		if(meta.getColumnsMap().containsKey("website")) {
+			user.setWebSite(rs.getString("website"));
+		}
+		if(meta.getColumnsMap().containsKey("startworkdate")) {
+			user.setStartWorkDate(rs.getString("startworkdate"));
+		}
+		//work
+		if(meta.getColumnsMap().containsKey("workcountry")) {
+			user.setWorkCountry(rs.getString("workcountry"));
+		}
+		if(meta.getColumnsMap().containsKey("workregion")) {
+			user.setWorkRegion(rs.getString("workregion"));
+		}
+		if(meta.getColumnsMap().containsKey("worklocality")) {
+			user.setWorkLocality(rs.getString("worklocality"));
+		}
+		if(meta.getColumnsMap().containsKey("workstreetaddress")) {
+			user.setWorkStreetAddress(rs.getString("workstreetaddress"));
+		}
+		if(meta.getColumnsMap().containsKey("workaddressformatted")) {
+			user.setWorkAddressFormatted(rs.getString("workaddressformatted"));
+		}
+		if(meta.getColumnsMap().containsKey("workemail")) {
+			user.setWorkEmail(rs.getString("workemail"));
+		}
+		if(meta.getColumnsMap().containsKey("workphonenumber")) {
+			user.setWorkPhoneNumber(rs.getString("workphonenumber"));
+		}
+		if(meta.getColumnsMap().containsKey("workpostalcode")) {
+			user.setWorkPostalCode(rs.getString("workpostalcode"));
+		}
+		if(meta.getColumnsMap().containsKey("workfax")) {
+			user.setWorkFax(rs.getString("workfax"));
+		}
+		if(meta.getColumnsMap().containsKey("workofficename")) {
+			user.setWorkOfficeName(rs.getString("workofficename"));
+		}
+		//home
+		if(meta.getColumnsMap().containsKey("homecountry")) {
+			user.setHomeCountry(rs.getString("homecountry"));
+		}
+		if(meta.getColumnsMap().containsKey("homeregion")) {
+			user.setHomeRegion(rs.getString("homeregion"));
+		}
+		if(meta.getColumnsMap().containsKey("homelocality")) {
+			user.setHomeLocality(rs.getString("homelocality"));
+		}
+		if(meta.getColumnsMap().containsKey("homestreetaddress")) {
+			user.setHomeStreetAddress(rs.getString("homestreetaddress"));
+		}
+		if(meta.getColumnsMap().containsKey("homeaddressformatted")) {
+			user.setHomeAddressFormatted(rs.getString("homeaddressformatted"));
+		}
+		if(meta.getColumnsMap().containsKey("homeemail")) {
+			user.setHomeEmail(rs.getString("homeemail"));
+		}
+		if(meta.getColumnsMap().containsKey("homephonenumber")) {
+			user.setHomePhoneNumber(rs.getString("homephonenumber"));
+		}
+		if(meta.getColumnsMap().containsKey("homepostalcode")) {
+			user.setHomePostalCode(rs.getString("homepostalcode"));
+		}
+		if(meta.getColumnsMap().containsKey("homefax")) {
+			user.setHomeFax(rs.getString("homefax"));
+		}
+		//company
+		if(meta.getColumnsMap().containsKey("employeenumber")) {
+			user.setEmployeeNumber(rs.getString("employeenumber"));
+		}
+		if(meta.getColumnsMap().containsKey("costcenter")) {
+			user.setCostCenter(rs.getString("costcenter"));
+		}
+		if(meta.getColumnsMap().containsKey("organization")) {
+			user.setOrganization(rs.getString("organization"));
+		}
+		if(meta.getColumnsMap().containsKey("division")) {
+			user.setDivision(rs.getString("division"));
+		}
+		if(meta.getColumnsMap().containsKey("departmentid")) {
+			user.setDepartmentId(rs.getString("departmentid"));
+		}
+		if(meta.getColumnsMap().containsKey("department")) {
+			user.setDepartment(rs.getString("department"));
+		}
+		if(meta.getColumnsMap().containsKey("jobtitle")) {
+			user.setJobTitle(rs.getString("jobtitle"));
+		}
+		if(meta.getColumnsMap().containsKey("joblevel")) {
+			user.setJobLevel(rs.getString("joblevel"));
+		}
+		if(meta.getColumnsMap().containsKey("managerid")) {
+			user.setManagerId(rs.getString("managerid"));
+		}
+		if(meta.getColumnsMap().containsKey("manager")) {
+			user.setManager(rs.getString("manager"));
+		}
+		if(meta.getColumnsMap().containsKey("assistantid")) {
+			user.setAssistantId(rs.getString("assistantid"));
+		}
+		if(meta.getColumnsMap().containsKey("assistant")) {
+			user.setAssistant(rs.getString("assistant"));
+		}
+		if(meta.getColumnsMap().containsKey("entrydate")) {
+			user.setEntryDate(rs.getString("entrydate"));
+		}
+		if(meta.getColumnsMap().containsKey("quitdate")) {
+			user.setQuitDate(rs.getString("quitdate"));
+		}
+		//common
+		if(meta.getColumnsMap().containsKey("ldapdn")) {
+			user.setLdapDn(rs.getString("ldapdn"));
+		}
+		if(meta.getColumnsMap().containsKey("status")) {
+			user.setStatus(rs.getInt("status"));
+		}else {
+			user.setStatus(ConstsStatus.ACTIVE);
+		}
+		if(meta.getColumnsMap().containsKey("description")) {
+			user.setDescription(rs.getString("description"));
+		}
+		//password
+		if(meta.getColumnsMap().containsKey("password")) {
+			user.setPassword(rs.getString("password"));
+		}else {
+			//后4位
+			String last4Char = "6666";
+			if(StringUtils.isNotBlank(user.getIdCardNo())) {
+				last4Char = user.getIdCardNo().substring(user.getIdCardNo().length() - 4);
+			}else if(StringUtils.isNotBlank(user.getMobile())) {
+				last4Char = user.getMobile().substring(user.getMobile().length() - 4);
+			}else if(StringUtils.isNotBlank(user.getEmployeeNumber())) {
+				last4Char = user.getEmployeeNumber().substring(user.getEmployeeNumber().length() - 4);
+			}
+			user.setPassword(user.getUsername()+"@M"+last4Char);
+		}
 		user.setInstId(this.synchronizer.getInstId());
-		user.setStatus(ConstsStatus.ACTIVE);
+		_logger.debug("User {} " , user);
 		return user;
 	}
 

+ 2 - 2
maxkey-synchronizers/maxkey-synchronizer-ldap/src/main/java/org/maxkey/synchronizer/ldap/LdapOrganizationService.java

@@ -119,8 +119,8 @@ public class LdapOrganizationService extends AbstractSynchronizerService  implem
 		SearchControls constraints = new SearchControls();
 		constraints.setSearchScope(ldapUtils.getSearchScope());
 		String filter = "(&(objectClass=OrganizationalUnit))";
-		if(StringUtils.isNotBlank(this.getSynchronizer().getFilters())) {
-			//filter = this.getSynchronizer().getFilters();
+		if(StringUtils.isNotBlank(this.getSynchronizer().getOrgFilters())) {
+			filter = this.getSynchronizer().getOrgFilters();
 		}
 		NamingEnumeration<SearchResult> results = 
 				ldapUtils.getConnection().search(ldapUtils.getBaseDN(), filter , constraints);

+ 1 - 1
maxkey-synchronizers/maxkey-synchronizer-ldap/src/main/java/org/maxkey/synchronizer/ldap/LdapSynchronizerService.java

@@ -47,7 +47,7 @@ public class LdapSynchronizerService  implements ISynchronizerService{
 		        synchronizer.getProviderUrl(),
 		        synchronizer.getPrincipal(),
 		        synchronizer.getCredentials(),
-		        synchronizer.getBasedn());
+		        synchronizer.getUserBasedn());
 		ldapUtils.openConnection();
 		
 		ldapOrganizationService.setSynchronizer(synchronizer);

+ 2 - 2
maxkey-synchronizers/maxkey-synchronizer-ldap/src/main/java/org/maxkey/synchronizer/ldap/LdapUsersService.java

@@ -50,8 +50,8 @@ public class LdapUsersService extends AbstractSynchronizerService  implements IS
 		try {
 			SearchControls constraints = new SearchControls();
 			constraints.setSearchScope(ldapUtils.getSearchScope());
-			String filter = StringUtils.isNotBlank(this.getSynchronizer().getFilters()) ? 
-								getSynchronizer().getFilters() : "(&(objectClass=inetOrgPerson))";
+			String filter = StringUtils.isNotBlank(this.getSynchronizer().getUserFilters()) ? 
+								getSynchronizer().getUserFilters() : "(&(objectClass=inetOrgPerson))";
 			_logger.debug(" User filter {} ",filter);
 			NamingEnumeration<SearchResult> results = 
 					ldapUtils.getConnection().search(ldapUtils.getBaseDN(), filter, constraints);

+ 4 - 2
maxkey-synchronizers/maxkey-synchronizer/src/main/java/org/maxkey/autoconfigure/SynchronizerAutoConfiguration.java

@@ -110,8 +110,10 @@ public class SynchronizerAutoConfiguration   implements InitializingBean {
 	        					 rs.getString("credentials")));
 	        	 synchronizer.setResumeTime( rs.getString("resumetime"));
 	        	 synchronizer.setSuspendTime(rs.getString("suspendtime"));
-	        	 synchronizer.setFilters(	 rs.getString("filters"));
-	        	 synchronizer.setBasedn(     rs.getString("basedn"));
+	        	 synchronizer.setUserFilters(	 rs.getString("userfilters"));
+	        	 synchronizer.setUserBasedn(     rs.getString("userbasedn"));
+	        	 synchronizer.setOrgFilters(	 rs.getString("orgfilters"));
+	        	 synchronizer.setOrgBasedn(     rs.getString("orgbasedn"));
 	        	 synchronizer.setMsadDomain( rs.getString("msaddomain"));
 	        	 synchronizer.setSslSwitch(  rs.getString("sslswitch"));
 	        	 synchronizer.setTrustStore( rs.getString("truststore"));

+ 4 - 2
maxkey-web-frontend/maxkey-web-mgt-app/src/app/entity/Synchronizers.ts

@@ -20,7 +20,6 @@ import { BaseEntity } from './BaseEntity';
 
 export class Synchronizers extends BaseEntity {
   name!: String;
-  filters!: String;
   sourceType!: String;
   service!: String;
   resumeTime!: String;
@@ -32,7 +31,10 @@ export class Synchronizers extends BaseEntity {
   principal!: String;
   credentials!: String;
   sslSwitch!: Number;
-  basedn!: String;
+  userBasedn!: String;
+  userFilters!: String;
+  orgBasedn!: String;
+  orgFilters!: String;
   msadDomain!: String;
   trustStore!: String;
   trustStorePassword!: String;

+ 1 - 1
maxkey-web-frontend/maxkey-web-mgt-app/src/app/entity/Users.ts

@@ -154,7 +154,7 @@ export class Users extends BaseEntity {
     this.gender_select = '1';
     this.str_married = '0';
     this.str_idType = '0';
-    this.str_status='1';
+    this.str_status = '1';
   }
 
   override init(data: any): void {

+ 112 - 83
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/synchronizers/synchronizer-editer/synchronizer-editer.component.html

@@ -4,11 +4,13 @@
     <nz-form-item>
       <nz-form-label [nzMd]="6" nzFor="id">{{ 'mxk.text.id' | i18n }}</nz-form-label>
       <nz-form-control [nzMd]="18" nzErrorTip="The input is not valid id!">
-        <input [(ngModel)]="form.model.id" disabled="{{ isEdit }}" [ngModelOptions]="{ standalone: true }" nz-input name="id" id="id" />
+        <input [(ngModel)]="form.model.id" disabled="{{ isEdit }}" [ngModelOptions]="{ standalone: true }" nz-input
+          name="id" id="id" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="sourceType">{{ 'mxk.synchronizers.sourceType' | i18n }} </nz-form-label>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="sourceType">{{ 'mxk.synchronizers.sourceType' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid sourceType!">
         <nz-select [(ngModel)]="form.model.sourceType" disabled="{{ isEdit }}" [ngModelOptions]="{ standalone: true }">
           <nz-option nzValue="SCIMV20" nzLabel="SCIM v2.0"> </nz-option>
@@ -20,142 +22,169 @@
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="service">{{ 'mxk.synchronizers.service' | i18n }} </nz-form-label>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="service">{{ 'mxk.synchronizers.service' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid icon!">
-        <input [(ngModel)]="form.model.service" [ngModelOptions]="{ standalone: true }" nz-input name="service" id="service" />
+        <input [(ngModel)]="form.model.service" [ngModelOptions]="{ standalone: true }" nz-input name="service"
+          id="service" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
       <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="scheduler">{{ 'mxk.synchronizers.scheduler' | i18n }}</nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid icon!">
-        <input
-          [(ngModel)]="form.model.scheduler"
-          [ngModelOptions]="{ standalone: true }"
-          nz-input
-          name="scheduler"
-          placeholder="0 0 12 * * ?"
-          id="scheduler"
-        />
+        <input [(ngModel)]="form.model.scheduler" [ngModelOptions]="{ standalone: true }" nz-input name="scheduler"
+          placeholder="0 0 12 * * ?" id="scheduler" />
       </nz-form-control>
     </nz-form-item>
-    <nz-form-item
-      *ngIf="
-        form.model.sourceType == 'DB' ||
-        form.model.sourceType == 'LDAP' ||
-        form.model.sourceType == 'MSAD' ||
-        form.model.sourceType == 'SCIMV20'
-      "
-    >
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="providerUrl">{{ 'mxk.synchronizers.providerUrl' | i18n }} </nz-form-label>
+    <nz-form-item *ngIf="form.model.sourceType == 'DB'">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="driverClass">{{ 'mxk.synchronizers.driverClass' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid driverClass!">
+        <input [(ngModel)]="form.model.driverClass" [ngModelOptions]="{ standalone: true }" nz-input name="driverClass"
+          id="driverClass" />
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="providerUrl">{{ 'mxk.synchronizers.providerUrl' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid providerUrl!">
-        <input [(ngModel)]="form.model.providerUrl" [ngModelOptions]="{ standalone: true }" nz-input name="providerUrl" id="providerUrl" />
+        <input [(ngModel)]="form.model.providerUrl" [ngModelOptions]="{ standalone: true }" nz-input name="providerUrl"
+          id="providerUrl" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="principal">{{ 'mxk.synchronizers.principal' | i18n }} </nz-form-label>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="principal">{{ 'mxk.synchronizers.principal' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid principal!">
-        <input [(ngModel)]="form.model.principal" [ngModelOptions]="{ standalone: true }" nz-input name="principal" id="principal" />
+        <input [(ngModel)]="form.model.principal" [ngModelOptions]="{ standalone: true }" nz-input name="principal"
+          id="principal" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="credentials">{{ 'mxk.synchronizers.credentials' | i18n }} </nz-form-label>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="credentials">{{ 'mxk.synchronizers.credentials' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid credentials!">
-        <input
-          type="password"
-          [(ngModel)]="form.model.credentials"
-          [ngModelOptions]="{ standalone: true }"
-          nz-input
-          name="credentials"
-          id="credentials"
-        />
+        <input type="password" [(ngModel)]="form.model.credentials" [ngModelOptions]="{ standalone: true }" nz-input
+          name="credentials" id="credentials" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item *ngIf="form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD'">
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="basedn">{{ 'mxk.synchronizers.basedn' | i18n }} </nz-form-label>
-      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid basedn!">
-        <input [(ngModel)]="form.model.basedn" [ngModelOptions]="{ standalone: true }" nz-input name="basedn" id="basedn" />
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="userBasedn">{{ 'mxk.synchronizers.userBasedn' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid userBasedn!">
+        <input [(ngModel)]="form.model.userBasedn" [ngModelOptions]="{ standalone: true }" nz-input name="userBasedn"
+          id="userBasedn" />
       </nz-form-control>
     </nz-form-item>
-    <nz-form-item *ngIf="form.model.sourceType == 'DB' || form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD'">
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="filters">{{ 'mxk.synchronizers.filters' | i18n }}</nz-form-label>
-      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid filters!">
-        <input [(ngModel)]="form.model.filters" [ngModelOptions]="{ standalone: true }" nz-input name="filters" id="filters" />
+    <nz-form-item *ngIf="form.model.sourceType == 'DB'">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="userFilters">{{ 'mxk.synchronizers.userFilters' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid userFilters!">
+        <textarea [(ngModel)]="form.model.userFilters" [ngModelOptions]="{ standalone: true }" nz-input
+          name="userFilters" id="userFilters">
+        </textarea>
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item *ngIf="form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD'">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="userFilters">{{ 'mxk.synchronizers.userFilters' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid userFilters!">
+        <input [(ngModel)]="form.model.userFilters" [ngModelOptions]="{ standalone: true }" nz-input name="userFilters"
+          id="userFilters" />
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item *ngIf="form.model.sourceType == 'LDAP'">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="orgBasedn">{{ 'mxk.synchronizers.orgBasedn' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid orgBasedn!">
+        <input [(ngModel)]="form.model.orgBasedn" [ngModelOptions]="{ standalone: true }" nz-input name="orgBasedn"
+          id="orgBasedn" />
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item *ngIf="form.model.sourceType == 'DB'">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="orgFilters">{{ 'mxk.synchronizers.orgFilters' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid orgFilters!">
+        <textarea [(ngModel)]="form.model.orgFilters" [ngModelOptions]="{ standalone: true }" nz-input name="orgFilters"
+          id="orgFilters"></textarea>
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item *ngIf="form.model.sourceType == 'LDAP'">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="orgFilters">{{ 'mxk.synchronizers.orgFilters' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid orgFilters!">
+        <input [(ngModel)]="form.model.orgFilters" [ngModelOptions]="{ standalone: true }" nz-input name="orgFilters"
+          id="orgFilters" />
       </nz-form-control>
     </nz-form-item>
-
     <nz-form-item *ngIf="form.model.sourceType == 'MSAD'">
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="msadDomain">{{ 'mxk.synchronizers.msadDomain' | i18n }} </nz-form-label>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="msadDomain">{{ 'mxk.synchronizers.msadDomain' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid msadDomain!">
-        <input [(ngModel)]="form.model.msadDomain" [ngModelOptions]="{ standalone: true }" nz-input name="msadDomain" id="msadDomain" />
+        <input [(ngModel)]="form.model.msadDomain" [ngModelOptions]="{ standalone: true }" nz-input name="msadDomain"
+          id="msadDomain" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item *ngIf="form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD'">
       <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="sslSwitch">{{ 'mxk.synchronizers.sslSwitch' | i18n }}</nz-form-label>
       <nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid sslSwitch!">
-        <nz-switch
-          [(ngModel)]="form.model.switch_sslSwitch"
-          [ngModelOptions]="{ standalone: true }"
-          name="sslSwitch"
-          [nzCheckedChildren]="checkedTemplate"
-          [nzUnCheckedChildren]="unCheckedTemplate"
-        ></nz-switch>
+        <nz-switch [(ngModel)]="form.model.switch_sslSwitch" [ngModelOptions]="{ standalone: true }" name="sslSwitch"
+          [nzCheckedChildren]="checkedTemplate" [nzUnCheckedChildren]="unCheckedTemplate"></nz-switch>
         <ng-template #checkedTemplate><i nz-icon nzType="check"></i></ng-template>
         <ng-template #unCheckedTemplate><i nz-icon nzType="close"></i></ng-template>
       </nz-form-control>
     </nz-form-item>
-    <nz-form-item *ngIf="(form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD') && form.model.switch_sslSwitch">
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="trustStore">{{ 'mxk.synchronizers.trustStore' | i18n }} </nz-form-label>
+    <nz-form-item
+      *ngIf="(form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD') && form.model.switch_sslSwitch">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="trustStore">{{ 'mxk.synchronizers.trustStore' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid trustStore!">
-        <input [(ngModel)]="form.model.trustStore" [ngModelOptions]="{ standalone: true }" nz-input name="trustStore" id="trustStore" />
+        <input [(ngModel)]="form.model.trustStore" [ngModelOptions]="{ standalone: true }" nz-input name="trustStore"
+          id="trustStore" />
       </nz-form-control>
     </nz-form-item>
-    <nz-form-item *ngIf="(form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD') && form.model.switch_sslSwitch">
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="trustStorePassword">{{ 'mxk.synchronizers.trustStorePassword' | i18n }}</nz-form-label>
+    <nz-form-item
+      *ngIf="(form.model.sourceType == 'LDAP' || form.model.sourceType == 'MSAD') && form.model.switch_sslSwitch">
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="trustStorePassword">{{ 'mxk.synchronizers.trustStorePassword' | i18n
+        }}</nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid sortIndex!">
-        <input
-          [(ngModel)]="form.model.trustStorePassword"
-          [ngModelOptions]="{ standalone: true }"
-          nz-input
-          name="trustStorePassword"
-          id="trustStorePassword"
-        />
+        <input [(ngModel)]="form.model.trustStorePassword" [ngModelOptions]="{ standalone: true }" nz-input
+          name="trustStorePassword" id="trustStorePassword" />
       </nz-form-control>
     </nz-form-item>
 
     <nz-form-item *ngIf="form.model.sourceType == 'DB' || form.model.sourceType == 'API'">
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="syncStartTime">{{ 'mxk.synchronizers.syncStartTime' | i18n }} </nz-form-label>
-      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid syncStartTime!">
-        <input
-          [(ngModel)]="form.model.syncStartTime"
-          [ngModelOptions]="{ standalone: true }"
-          nz-input
-          name="syncStartTime"
-          id="syncStartTime"
-        />
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="syncStartTime">{{ 'mxk.synchronizers.syncStartTime' | i18n }}
+      </nz-form-label>
+      <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48"
+        nzErrorTip="The input is not valid syncStartTime!">
+        <nz-input-group nzAddOnAfter="{{ 'mxk.text.day' | i18n }}">
+          <input [(ngModel)]="form.model.syncStartTime" [ngModelOptions]="{ standalone: true }" nz-input
+            name="syncStartTime" id="syncStartTime" />
+        </nz-input-group>
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="resumeTime">{{ 'mxk.synchronizers.resumeTime' | i18n }} </nz-form-label>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="resumeTime">{{ 'mxk.synchronizers.resumeTime' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid resumeTime!">
-        <input [(ngModel)]="form.model.resumeTime" [ngModelOptions]="{ standalone: true }" nz-input name="resumeTime" id="resumeTime" />
+        <input [(ngModel)]="form.model.resumeTime" [ngModelOptions]="{ standalone: true }" nz-input name="resumeTime"
+          id="resumeTime" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
-      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="suspendTime">{{ 'mxk.synchronizers.suspendTime' | i18n }} </nz-form-label>
+      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="suspendTime">{{ 'mxk.synchronizers.suspendTime' | i18n }}
+      </nz-form-label>
       <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid suspendTime!">
-        <input [(ngModel)]="form.model.suspendTime" [ngModelOptions]="{ standalone: true }" nz-input name="suspendTime" id="suspendTime" />
+        <input [(ngModel)]="form.model.suspendTime" [ngModelOptions]="{ standalone: true }" nz-input name="suspendTime"
+          id="suspendTime" />
       </nz-form-control>
     </nz-form-item>
     <nz-form-item>
       <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="status">{{ 'mxk.text.status' | i18n }}</nz-form-label>
       <nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid status!">
-        <nz-switch
-          [(ngModel)]="form.model.switch_status"
-          [ngModelOptions]="{ standalone: true }"
-          name="status"
-          [nzCheckedChildren]="checkedTemplate"
-          [nzUnCheckedChildren]="unCheckedTemplate"
-        ></nz-switch>
+        <nz-switch [(ngModel)]="form.model.switch_status" [ngModelOptions]="{ standalone: true }" name="status"
+          [nzCheckedChildren]="checkedTemplate" [nzUnCheckedChildren]="unCheckedTemplate"></nz-switch>
         <ng-template #checkedTemplate><i nz-icon nzType="check"></i></ng-template>
         <ng-template #unCheckedTemplate><i nz-icon nzType="close"></i></ng-template>
       </nz-form-control>
@@ -166,4 +195,4 @@
 <div *nzModalFooter>
   <button nz-button nzType="default" (click)="onClose($event)">{{ 'mxk.text.close' | i18n }}</button>
   <button nz-button nzType="primary" (click)="onSubmit($event)">{{ 'mxk.text.submit' | i18n }}</button>
-</div>
+</div>

+ 1 - 1
maxkey-web-frontend/maxkey-web-mgt-app/src/app/shared/consts.ts

@@ -19,5 +19,5 @@ export const CONSTS = {
   INST: 'inst',
   REDIRECT_URI: 'redirect_uri',
   REMEMBER: 'remember',
-  VERSION: 'v3.5.10 GA'
+  VERSION: 'v3.5.11 GA'
 };

+ 5 - 2
maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/en-US.json

@@ -546,15 +546,18 @@
 			"sourceType": "SourceType",
 			"service": "Service",
 			"scheduler": "Scheduler",
+			"driverClass":"Driver Class",
 			"providerUrl": "ProviderUrl",
 			"principal": "Principal",
 			"credentials": "Credentials",
-			"basedn": "Base DN",
+			"userBasedn": "User Base DN",
+			"userFilters": "User Filters",
+			"orgBasedn": "Org Base DN",
+			"orgFilters": "Org Filters",
 			"msadDomain": "Active Directory Domain",
 			"sslSwitch": "SSL",
 			"trustStore": "TrustStore",
 			"trustStorePassword": "TrustStorePassword",
-			"filters": "Filters",
 			"syncStartTime": "SyncStartTime",
 			"resumeTime": "Join Time",
 			"suspendTime": "Suspend Time"

+ 6 - 3
maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-CN.json

@@ -542,17 +542,20 @@
 		"synchronizers": {
 			"name": "名称",
 			"sourceType": "数据源类型",
-			"service": "执行服务",
+			"service": "同步服务",
 			"scheduler": "定时任务",
+			"driverClass":"数据库驱动",
 			"providerUrl": "地址",
 			"principal": "账号",
 			"credentials": "凭证",
-			"basedn": "基本DN",
+			"userBasedn": "用户基本DN",
+			"userFilters": "用户过滤条件",
+			"orgBasedn": "组织基本DN",
+			"orgFilters": "组织过滤条件",
 			"msadDomain": "Active Directory域",
 			"sslSwitch": "SSL",
 			"trustStore": "证书路径",
 			"trustStorePassword": "证书密钥",
-			"filters": "过滤条件",
 			"syncStartTime": "间隔区间",
 			"resumeTime": "执行时间",
 			"suspendTime": "挂起时间"

+ 6 - 3
maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-TW.json

@@ -543,17 +543,20 @@
 		"synchronizers": {
 			"name": "名稱",
 			"sourceType": "數據源類型",
-			"service": "執行服務",
+			"service": "同步服務",
 			"scheduler": "定時任務",
+			"driverClass":"資料庫驅動",
 			"providerUrl": "地址",
 			"principal": "賬號",
 			"credentials": "憑證",
-			"basedn": "基本DN",
+			"userBasedn": "用戶基本DN",
+			"userFilters": "用户過濾條件",
+			"orgBasedn": "組織基本DN",
+			"orgFilters": "组织過濾條件",
 			"msadDomain": "Active Directory域",
 			"sslSwitch": "SSL",
 			"trustStore": "證書路徑",
 			"trustStorePassword": "證書密鑰",
-			"filters": "過濾條件",
 			"syncStartTime": "間隔區間",
 			"resumeTime": "執行時間",
 			"suspendTime": "掛起時間"