Jelajahi Sumber

OrganizationsCast

机构映射关系
机构namepath重组
登录错误提示信息优化
界面元素优化
HttpRequestAdapter支持JSON
Crystal.Sea 3 tahun lalu
induk
melakukan
625c2e4336
24 mengubah file dengan 749 tambahan dan 172 penghapusan
  1. 0 3
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/activedirectory/ActiveDirectoryAuthenticationRealm.java
  2. 11 1
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/jdbc/JdbcAuthenticationRealm.java
  3. 0 3
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/ldap/LdapAuthenticationRealm.java
  4. 19 0
      maxkey-core/src/main/java/org/maxkey/entity/Accounts.java
  5. 72 82
      maxkey-core/src/main/java/org/maxkey/entity/Organizations.java
  6. 219 0
      maxkey-core/src/main/java/org/maxkey/entity/OrganizationsCast.java
  7. 3 2
      maxkey-core/src/main/java/org/maxkey/persistence/db/PasswordPolicyValidator.java
  8. 47 24
      maxkey-core/src/main/java/org/maxkey/web/HttpRequestAdapter.java
  9. 4 4
      maxkey-core/src/main/resources/messages/passwordpolicy_message.properties
  10. 2 1
      maxkey-core/src/main/resources/messages/passwordpolicy_message_en.properties
  11. 3 2
      maxkey-core/src/main/resources/messages/passwordpolicy_message_zh_CN.properties
  12. 0 2
      maxkey-identitys/maxkey-synchronizers-dingding/src/main/java/org/maxkey/synchronizer/dingding/DingdingOrganizationService.java
  13. 14 0
      maxkey-identitys/maxkey-synchronizers-reorgdept/build.gradle
  14. 59 0
      maxkey-identitys/maxkey-synchronizers-reorgdept/src/main/java/org/maxkey/synchronizer/reorg/ReorgDeptSynchronizerService.java
  15. 121 0
      maxkey-identitys/maxkey-synchronizers-reorgdept/src/main/java/org/maxkey/synchronizer/workweixin/service/ReorgDeptService.java
  16. 31 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/mapper/OrganizationsCastMapper.java
  17. 12 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/AccountsService.java
  18. 51 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/OrganizationsCastService.java
  19. 22 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java
  20. 1 0
      maxkey-webs/maxkey-web-mgt/build.gradle
  21. 2 2
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/accountsAdd.ftl
  22. 33 39
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/apps/selectAppsList.ftl
  23. 22 7
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/userinfo/userinfoSelect.ftl
  24. 1 0
      settings.gradle

+ 0 - 3
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/activedirectory/ActiveDirectoryAuthenticationRealm.java

@@ -64,9 +64,6 @@ public class ActiveDirectoryAuthenticationRealm extends AbstractAuthenticationRe
             	return true;
             }
 		 }
-		if(!isAuthenticated){
-			 throw new BadCredentialsException(WebContext.getI18nValue("login.error.password"));
-		 }
 		return false;
 	}
 

+ 11 - 1
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/jdbc/JdbcAuthenticationRealm.java

@@ -120,7 +120,17 @@ public class JdbcAuthenticationRealm extends AbstractAuthenticationRealm {
         if (!passwordMatches) {
             passwordPolicyValidator.plusBadPasswordCount(userInfo);
             insertLoginHistory(userInfo, ConstantsLoginType.LOCAL, "", "xe00000004", "password error");
-            throw new BadCredentialsException(WebContext.getI18nValue("login.error.password"));
+            
+            if(userInfo.getBadPasswordCount()>=(passwordPolicyValidator.getPasswordPolicy().getAttempts()/2)) {
+                throw new BadCredentialsException(
+                        WebContext.getI18nValue("login.error.password.attempts",
+                                new Object[]{
+                                        userInfo.getBadPasswordCount() + 1,
+                                        passwordPolicyValidator.getPasswordPolicy().getAttempts(),
+                                        passwordPolicyValidator.getPasswordPolicy().getDuration()}));
+            }else {
+                throw new BadCredentialsException(WebContext.getI18nValue("login.error.password"));
+            }
         }
         return passwordMatches;
     }

+ 0 - 3
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/ldap/LdapAuthenticationRealm.java

@@ -63,9 +63,6 @@ public class LdapAuthenticationRealm  extends AbstractAuthenticationRealm{
             	return true;
             }
 		 }
-		 if(!isAuthenticated){
-			 throw new BadCredentialsException(WebContext.getI18nValue("login.error.password"));
-		 }
 		return false;
 	}
 

+ 19 - 0
maxkey-core/src/main/java/org/maxkey/entity/Accounts.java

@@ -18,6 +18,9 @@
 package org.maxkey.entity;
 
 import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
@@ -68,6 +71,8 @@ public class Accounts extends JpaBaseEntity implements Serializable {
     private int status;
     
     UserInfo userInfo;
+    
+    private HashMap<String,OrganizationsCast> orgCast =new HashMap<String,OrganizationsCast>();
 
     public Accounts() {
         super();
@@ -186,6 +191,20 @@ public class Accounts extends JpaBaseEntity implements Serializable {
     public void setStatus(int status) {
         this.status = status;
     }
+    
+    public HashMap<String, OrganizationsCast> getOrgCast() {
+        return orgCast;
+    }
+
+    public void setOrgCast(HashMap<String, OrganizationsCast> orgCast) {
+        this.orgCast = orgCast;
+    }
+    
+    public void setOrgCast(List <OrganizationsCast> listOrgCast) {
+        for(OrganizationsCast cast : listOrgCast) {
+            this.orgCast.put(cast.getProvider(), cast);
+        }
+    }
 
     @Override
     public String toString() {

+ 72 - 82
maxkey-core/src/main/java/org/maxkey/entity/Organizations.java

@@ -83,14 +83,12 @@ public class Organizations extends JpaBaseEntity implements Serializable {
     private String ldapDn;
     @Column
     private String description;
-    
-    private int status;
-    @Column
-    private String extId;
     @Column
-    private String extParentId;
+    private int status;
     
     private int isPrimary = 0;
+    
+    private boolean reorgNamePath;
 
     public Organizations() {
         //
@@ -308,22 +306,6 @@ public class Organizations extends JpaBaseEntity implements Serializable {
         this.status = status;
     }
     
-    public String getExtId() {
-		return extId;
-	}
-
-	public void setExtId(String extId) {
-		this.extId = extId;
-	}
-
-	public String getExtParentId() {
-		return extParentId;
-	}
-
-	public void setExtParentId(String extParentId) {
-		this.extParentId = extParentId;
-	}
-
 	public int getIsPrimary() {
         return isPrimary;
     }
@@ -332,68 +314,76 @@ public class Organizations extends JpaBaseEntity implements Serializable {
         this.isPrimary = isPrimary;
     }
 
+    public boolean isReorgNamePath() {
+        return reorgNamePath;
+    }
+
+    public void setReorgNamePath(boolean reorgNamePath) {
+        this.reorgNamePath = reorgNamePath;
+    }
+
     @Override
-	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		builder.append("Organizations [id=");
-		builder.append(id);
-		builder.append(", code=");
-		builder.append(code);
-		builder.append(", name=");
-		builder.append(name);
-		builder.append(", fullName=");
-		builder.append(fullName);
-		builder.append(", parentId=");
-		builder.append(parentId);
-		builder.append(", parentName=");
-		builder.append(parentName);
-		builder.append(", type=");
-		builder.append(type);
-		builder.append(", codePath=");
-		builder.append(codePath);
-		builder.append(", namePath=");
-		builder.append(namePath);
-		builder.append(", level=");
-		builder.append(level);
-		builder.append(", hasChild=");
-		builder.append(hasChild);
-		builder.append(", division=");
-		builder.append(division);
-		builder.append(", country=");
-		builder.append(country);
-		builder.append(", region=");
-		builder.append(region);
-		builder.append(", locality=");
-		builder.append(locality);
-		builder.append(", street=");
-		builder.append(street);
-		builder.append(", address=");
-		builder.append(address);
-		builder.append(", contact=");
-		builder.append(contact);
-		builder.append(", postalCode=");
-		builder.append(postalCode);
-		builder.append(", phone=");
-		builder.append(phone);
-		builder.append(", fax=");
-		builder.append(fax);
-		builder.append(", email=");
-		builder.append(email);
-		builder.append(", sortIndex=");
-		builder.append(sortIndex);
-		builder.append(", ldapDn=");
-		builder.append(ldapDn);
-		builder.append(", description=");
-		builder.append(description);
-		builder.append(", status=");
-		builder.append(status);
-		builder.append(", extId=");
-		builder.append(extId);
-		builder.append(", extParentId=");
-		builder.append(extParentId);
-		builder.append("]");
-		return builder.toString();
-	}
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Organizations [id=");
+        builder.append(id);
+        builder.append(", code=");
+        builder.append(code);
+        builder.append(", name=");
+        builder.append(name);
+        builder.append(", fullName=");
+        builder.append(fullName);
+        builder.append(", parentId=");
+        builder.append(parentId);
+        builder.append(", parentName=");
+        builder.append(parentName);
+        builder.append(", type=");
+        builder.append(type);
+        builder.append(", codePath=");
+        builder.append(codePath);
+        builder.append(", namePath=");
+        builder.append(namePath);
+        builder.append(", level=");
+        builder.append(level);
+        builder.append(", hasChild=");
+        builder.append(hasChild);
+        builder.append(", division=");
+        builder.append(division);
+        builder.append(", country=");
+        builder.append(country);
+        builder.append(", region=");
+        builder.append(region);
+        builder.append(", locality=");
+        builder.append(locality);
+        builder.append(", street=");
+        builder.append(street);
+        builder.append(", address=");
+        builder.append(address);
+        builder.append(", contact=");
+        builder.append(contact);
+        builder.append(", postalCode=");
+        builder.append(postalCode);
+        builder.append(", phone=");
+        builder.append(phone);
+        builder.append(", fax=");
+        builder.append(fax);
+        builder.append(", email=");
+        builder.append(email);
+        builder.append(", sortIndex=");
+        builder.append(sortIndex);
+        builder.append(", ldapDn=");
+        builder.append(ldapDn);
+        builder.append(", description=");
+        builder.append(description);
+        builder.append(", status=");
+        builder.append(status);
+        builder.append(", isPrimary=");
+        builder.append(isPrimary);
+        builder.append(", reorgNamePath=");
+        builder.append(reorgNamePath);
+        builder.append("]");
+        return builder.toString();
+    }
 
 
 

+ 219 - 0
maxkey-core/src/main/java/org/maxkey/entity/OrganizationsCast.java

@@ -0,0 +1,219 @@
+/*
+ * 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.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;
+@Entity
+@Table(name = "MXK_ORGANIZATIONS_CAST")
+public class OrganizationsCast extends JpaBaseEntity implements Serializable {
+
+    
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 5166920258761620856L;
+	@Id
+    @Column
+    @GeneratedValue(strategy = GenerationType.AUTO, generator = "snowflakeid")
+    private String id;
+    @Column
+    private String code;
+    @Column
+    private String name;
+    @Column
+    private String fullName;
+    @Column
+    private String parentId;
+    @Column
+    private String parentName;
+    @Column
+    private String codePath;
+    @Column
+    private String namePath;
+    
+    @Column
+    private long sortIndex;
+    @Column
+    private int status;
+    @Column
+    private String provider;
+    
+    @Column
+    private String orgId;
+    @Column
+    private String orgParentId;
+    
+    //重组标志
+    boolean reorgNamePath;
+    
+
+    public OrganizationsCast() {
+        //
+    }
+
+    public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getFullName() {
+		return fullName;
+	}
+
+	public void setFullName(String fullName) {
+		this.fullName = fullName;
+	}
+
+	public String getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(String parentId) {
+		this.parentId = parentId;
+	}
+
+	public String getParentName() {
+		return parentName;
+	}
+
+	public void setParentName(String parentName) {
+		this.parentName = parentName;
+	}
+
+	public String getCodePath() {
+		return codePath;
+	}
+
+	public void setCodePath(String codePath) {
+		this.codePath = codePath;
+	}
+
+	public String getNamePath() {
+		return namePath;
+	}
+
+	public void setNamePath(String namePath) {
+		this.namePath = namePath;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public String getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(String orgId) {
+		this.orgId = orgId;
+	}
+
+	public String getOrgParentId() {
+		return orgParentId;
+	}
+
+	public void setOrgParentId(String orgParentId) {
+		this.orgParentId = orgParentId;
+	}
+
+	public long getSortIndex() {
+		return sortIndex;
+	}
+
+	public void setSortIndex(long sortIndex) {
+		this.sortIndex = sortIndex;
+	}
+
+	public String getProvider() {
+		return provider;
+	}
+
+	public void setProvider(String provider) {
+		this.provider = provider;
+	}
+
+	public boolean isReorgNamePath() {
+		return reorgNamePath;
+	}
+
+	public void setReorgNamePath(boolean reorgNamePath) {
+		this.reorgNamePath = reorgNamePath;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("OrganizationsMapper [id=");
+		builder.append(id);
+		builder.append(", code=");
+		builder.append(code);
+		builder.append(", name=");
+		builder.append(name);
+		builder.append(", fullName=");
+		builder.append(fullName);
+		builder.append(", parentId=");
+		builder.append(parentId);
+		builder.append(", parentName=");
+		builder.append(parentName);
+		builder.append(", codePath=");
+		builder.append(codePath);
+		builder.append(", namePath=");
+		builder.append(namePath);
+		builder.append(", status=");
+		builder.append(status);
+		builder.append(", orgId=");
+		builder.append(orgId);
+		builder.append(", orgParentId=");
+		builder.append(orgParentId);
+		builder.append("]");
+		return builder.toString();
+	}
+
+
+
+}

+ 3 - 2
maxkey-core/src/main/java/org/maxkey/persistence/db/PasswordPolicyValidator.java

@@ -261,7 +261,7 @@ public class PasswordPolicyValidator {
                 lockUser(userInfo);
                 throw new BadCredentialsException(
                         WebContext.getI18nValue("login.error.attempts",
-                                new Object[]{userInfo.getUsername(),userInfo.getBadPasswordCount()}) 
+                                new Object[]{userInfo.getBadPasswordCount(),passwordPolicy.getDuration()}) 
                         );
             }
         }
@@ -404,7 +404,8 @@ public class PasswordPolicyValidator {
    
    public void plusBadPasswordCount(UserInfo userInfo) {
        if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) {
-           setBadPasswordCount(userInfo.getId(),userInfo.getBadPasswordCount() + 1);
+           userInfo.setBadPasswordCount(userInfo.getBadPasswordCount() + 1);
+           setBadPasswordCount(userInfo.getId(),userInfo.getBadPasswordCount());
            
        }
    }

+ 47 - 24
maxkey-core/src/main/java/org/maxkey/web/HttpRequestAdapter.java

@@ -33,10 +33,12 @@ import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.EntityUtils;
+import org.maxkey.util.JsonUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -45,6 +47,20 @@ import org.springframework.stereotype.Component;
 public class HttpRequestAdapter {
 	private static final Logger _logger = LoggerFactory.getLogger(HttpRequestAdapter.class);
 	 
+    private String mediaType = MediaType.FORM;
+    
+    public static class MediaType{
+        public static String JSON   =   "JSON";
+        public static String XML    =   "XML";
+        public static String FORM   =   "FORM";
+    }
+    
+    public HttpRequestAdapter(){}
+    
+    public HttpRequestAdapter(String mediaType){
+        this.mediaType = mediaType;
+    }
+    
 	public String post(String url,Map<String, Object> parameterMap) {
 		HashMap<String,String> headers = new HashMap<String,String>();
 		headers.put("Content-Type", "application/x-www-form-urlencoded");
@@ -78,26 +94,33 @@ public class HttpRequestAdapter {
         
         // 封装post请求参数
         if (null != parameterMap && parameterMap.size() > 0) {
-            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
-            // 通过map集成entrySet方法获取entity
-            Set<Entry<String, Object>> entrySet = parameterMap.entrySet();
-            // 循环遍历,获取迭代器
-            Iterator<Entry<String, Object>> iterator = entrySet.iterator();
-            while (iterator.hasNext()) {
-                Entry<String, Object> mapEntry = iterator.next();
-                _logger.debug("Name " + mapEntry.getKey() + " , Value " +mapEntry.getValue());
-                nvps.add(new BasicNameValuePair(mapEntry.getKey(), mapEntry.getValue().toString()));
-            }
+            if(mediaType.equals(MediaType.FORM)) {
+                List<NameValuePair> nvps = new ArrayList<NameValuePair>();
+                // 通过map集成entrySet方法获取entity
+                Set<Entry<String, Object>> entrySet = parameterMap.entrySet();
+                // 循环遍历,获取迭代器
+                Iterator<Entry<String, Object>> iterator = entrySet.iterator();
+                while (iterator.hasNext()) {
+                    Entry<String, Object> mapEntry = iterator.next();
+                    _logger.debug("Name " + mapEntry.getKey() + " , Value " +mapEntry.getValue());
+                    nvps.add(new BasicNameValuePair(mapEntry.getKey(), mapEntry.getValue().toString()));
+                }
+    
+                // 为httpPost设置封装好的请求参数
+                try {
+                    httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
+                } catch (UnsupportedEncodingException e) {
+                    e.printStackTrace();
+                }
+            }else if(mediaType.equals(MediaType.JSON)) {
+                String jsonString = JsonUtils.gson2Json(parameterMap);
+                StringEntity stringEntity =new StringEntity(jsonString, "UTF-8");
+                stringEntity.setContentType("text/json");
+                httpPost.setEntity(stringEntity);
 
-            // 为httpPost设置封装好的请求参数
-            try {
-                httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
-            } catch (UnsupportedEncodingException e) {
-                e.printStackTrace();
+                
             }
-            _logger.debug("Post Message \n" + 
-                    httpPost.getEntity().toString()
-                 );
+            _logger.debug("Post Message \n{} ", httpPost.getEntity().toString());
         }
         
         
@@ -107,9 +130,9 @@ public class HttpRequestAdapter {
             // 从响应对象中获取响应内容
             HttpEntity entity = httpResponse.getEntity();
             String content = EntityUtils.toString(entity);
-            _logger.debug("Http Response StatusCode " + 
-                    httpResponse.getStatusLine().getStatusCode()+
-                    " , Content " + content
+            _logger.debug("Http Response StatusCode {} , Content {}",
+                    httpResponse.getStatusLine().getStatusCode(),
+                    content
             );
             return content;
         } catch (Exception e) {
@@ -172,9 +195,9 @@ public class HttpRequestAdapter {
             // 从响应对象中获取响应内容
             HttpEntity entity = httpResponse.getEntity();
             String content = EntityUtils.toString(entity);
-            _logger.debug("Http Response StatusCode " + 
-                    httpResponse.getStatusLine().getStatusCode()+
-                    " , Content " + content
+            _logger.debug("Http Response StatusCode {} , Content {}",
+                    httpResponse.getStatusLine().getStatusCode(),
+                    content
             );
             return content;
         } catch (Exception e) {

+ 4 - 4
maxkey-core/src/main/resources/messages/passwordpolicy_message.properties

@@ -33,16 +33,16 @@ PasswordPolicy.CONFIRMPASSWORD_NOT_MATCH=\u65b0\u5bc6\u7801\u4e0e\u786e\u8ba4\u5
 PasswordPolicy.OLD_PASSWORD_MATCH=\u65b0\u5bc6\u7801\u4e0d\u80fd\u4e0e\u65e7\u5bc6\u7801\u4e00\u81f4.
 
 #\u7528\u6237\u767b\u5f55\u9519\u8bef\u63d0\u9192
-login.error.attempts={0}\u5c1d\u8bd5\u767b\u9646{1}\u6b21\u6570\u8fbe\u5230\u6700\u5927\u9650\u5236\uff0c\u8bf7\u7a0d\u540e\u518d\u767b\u9646.
+login.error.attempts=\u767B\u5F55\u9519\u8BEF\u8fbe\u6700\u5927\u9650\u5236{0}\u6b21,\u8bf7{1}\u5C0F\u65F6\u540e\u91CD\u8BD5.
 login.error.locked=\u7528\u6237\u88ab\u9501\u5b9a.
 login.error.inactive=\u7528\u6237\u975e\u6d3b\u52a8\u72b6\u6001.
 login.error.password=\u7528\u6237\u540D\u6216\u5bc6\u7801\u65e0\u6548.
+login.error.password.attempts=\u767B\u5F55\u5931\u8D25{0}\u6B21, \u5931\u8D25{1}\u6B21\u5C06\u9501\u5B9A{2}\u5C0F\u65F6.
 login.error.username=\u7528\u6237\u540D\u6216\u5bc6\u7801\u65e0\u6548.
 login.error.username.null=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a.
 login.error.email.null=\u767b\u5f55\u90ae\u7bb1\u4e0d\u80fd\u4e3a\u7a7a.
 login.error.password.null=\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a.
-login.error.captcha=\u9a8c\u8bc1\u7801\u9519\u8bef\uff0c\u8bf7\u91cd\u65b0\u767b\u9646.
+login.error.captcha=\u9a8c\u8bc1\u7801\u9519\u8bef\uff0c\u8bf7\u91cd\u65b0\u767B\u5F55.
 login.error.authtype=\u767b\u5f55\u8ba4\u8bc1\u7c7b\u578b\u9519\u8bef.
 login.error.session=\u767b\u5f55\u4f1a\u8bdd\u5931\u6548\uff0c\u8bf7\u91cd\u65b0\u767b\u9646.
-login.error.social=\u793e\u4ea4\u8d26\u53f7\u6388\u6743\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5.
-
+login.error.social=\u793e\u4ea4\u8d26\u53f7\u6388\u6743\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5.

+ 2 - 1
maxkey-core/src/main/resources/messages/passwordpolicy_message_en.properties

@@ -33,10 +33,11 @@ PasswordPolicy.CONFIRMPASSWORD_NOT_MATCH=new password not match confirm password
 PasswordPolicy.OLD_PASSWORD_MATCH=new password  match old password.
 
 #for user login
-login.error.attempts={0} login attempts the maximum number of {1} times, please login later.
+login.error.attempts=login attempts the maximum {0} times, please login {1} hours later.
 login.error.locked=The user is locked.
 login.error.inactive=User inactive state.
 login.error.password=Invalid username or password.
+login.error.password.attempts=login fail {0} times, fail {1} times will lock {2} hours.
 login.error.username=Invalid username or password.
 login.error.username.null=username cannot be empty.
 login.error.email.null=email cannot be empty.

+ 3 - 2
maxkey-core/src/main/resources/messages/passwordpolicy_message_zh_CN.properties

@@ -33,15 +33,16 @@ PasswordPolicy.CONFIRMPASSWORD_NOT_MATCH=\u65b0\u5bc6\u7801\u4e0e\u786e\u8ba4\u5
 PasswordPolicy.OLD_PASSWORD_MATCH=\u65b0\u5bc6\u7801\u4e0d\u80fd\u4e0e\u65e7\u5bc6\u7801\u4e00\u81f4.
 
 #\u7528\u6237\u767b\u5f55\u9519\u8bef\u63d0\u9192
-login.error.attempts={0}\u5c1d\u8bd5\u767b\u9646{1}\u6b21\u6570\u8fbe\u5230\u6700\u5927\u9650\u5236\uff0c\u8bf7\u7a0d\u540e\u518d\u767b\u9646.
+login.error.attempts=\u767B\u5F55\u9519\u8BEF\u8fbe\u6700\u5927\u9650\u5236{0}\u6b21,\u8bf7{1}\u5C0F\u65F6\u540e\u91CD\u8BD5.
 login.error.locked=\u7528\u6237\u88ab\u9501\u5b9a.
 login.error.inactive=\u7528\u6237\u975e\u6d3b\u52a8\u72b6\u6001.
 login.error.password=\u7528\u6237\u540D\u6216\u5bc6\u7801\u65e0\u6548.
+login.error.password.attempts=\u767B\u5F55\u5931\u8D25{0}\u6B21, \u5931\u8D25{1}\u6B21\u5C06\u9501\u5B9A{2}\u5C0F\u65F6.
 login.error.username=\u7528\u6237\u540D\u6216\u5bc6\u7801\u65e0\u6548.
 login.error.username.null=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a.
 login.error.email.null=\u767b\u5f55\u90ae\u7bb1\u4e0d\u80fd\u4e3a\u7a7a.
 login.error.password.null=\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a.
-login.error.captcha=\u9a8c\u8bc1\u7801\u9519\u8bef\uff0c\u8bf7\u91cd\u65b0\u767b\u9646.
+login.error.captcha=\u9a8c\u8bc1\u7801\u9519\u8bef\uff0c\u8bf7\u91cd\u65b0\u767B\u5F55.
 login.error.authtype=\u767b\u5f55\u8ba4\u8bc1\u7c7b\u578b\u9519\u8bef.
 login.error.session=\u767b\u5f55\u4f1a\u8bdd\u5931\u6548\uff0c\u8bf7\u91cd\u65b0\u767b\u9646.
 login.error.social=\u793e\u4ea4\u8d26\u53f7\u6388\u6743\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5.

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

@@ -78,8 +78,6 @@ public class DingdingOrganizationService   implements ISynchronizerService{
 		org.setId(dept.getDeptId()+"");
 		org.setName(dept.getName());
 		org.setParentId(dept.getParentId()+"");
-		org.setExtId(dept.getDeptId()+"");	
-		org.setExtParentId(dept.getParentId()+"");
 		return org;
 	}
 

+ 14 - 0
maxkey-identitys/maxkey-synchronizers-reorgdept/build.gradle

@@ -0,0 +1,14 @@
+description = "maxkey-synchronizers-reorgdept"
+
+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")
+	implementation project(":maxkey-identitys:maxkey-synchronizers")
+   
+}

+ 59 - 0
maxkey-identitys/maxkey-synchronizers-reorgdept/src/main/java/org/maxkey/synchronizer/reorg/ReorgDeptSynchronizerService.java

@@ -0,0 +1,59 @@
+/*
+ * 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.reorg;
+
+import org.maxkey.entity.Synchronizers;
+import org.maxkey.synchronizer.ISynchronizerService;
+import org.maxkey.synchronizer.workweixin.service.ReorgDeptService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ReorgDeptSynchronizerService implements ISynchronizerService {
+    final static Logger _logger = LoggerFactory.getLogger(ReorgDeptSynchronizerService.class);
+    Synchronizers synchronizer;
+
+    @Autowired
+    ReorgDeptService reorgDeptService;
+
+
+    public ReorgDeptSynchronizerService() {
+        super();
+    }
+
+    public void sync() throws Exception {
+        _logger.info("Sync ...");
+        reorgDeptService.sync();
+
+    }
+
+ 
+
+    public void setReorgDeptService(ReorgDeptService reorgDeptService) {
+		this.reorgDeptService = reorgDeptService;
+	}
+
+	@Override
+    public void setSynchronizer(Synchronizers synchronizer) {
+        this.synchronizer = synchronizer;
+
+    }
+
+}

+ 121 - 0
maxkey-identitys/maxkey-synchronizers-reorgdept/src/main/java/org/maxkey/synchronizer/workweixin/service/ReorgDeptService.java

@@ -0,0 +1,121 @@
+/*
+ * 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.service;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.maxkey.constants.ConstantsStatus;
+import org.maxkey.entity.Organizations;
+import org.maxkey.entity.Synchronizers;
+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 ReorgDeptService implements ISynchronizerService{
+	final static Logger _logger = LoggerFactory.getLogger(ReorgDeptService.class);
+	
+	@Autowired
+	OrganizationsService organizationsService;
+	
+	
+	String rootParentOrgId = "-1";
+
+	public void sync() {
+		_logger.info("Sync Organizations ...");
+
+		try {
+			long responseCount =0;
+			HashMap<String,Organizations>orgCastMap =new HashMap<String,Organizations>();
+			List<Organizations> listOrg = organizationsService.findAll();
+
+			buildNamePath(orgCastMap,listOrg);
+			
+			for(Organizations org :listOrg) {
+				_logger.info("Dept "+(++responseCount)+" : " + org);
+				org.setStatus(ConstantsStatus.ACTIVE);
+				organizationsService.update(org);
+			}
+			
+			
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+
+	
+	/**
+	 * Reorganization name path & code path	
+	 * @param orgCastMap
+	 * @param listOrgCast
+	 */
+	public void buildNamePath(HashMap<String,Organizations>orgMap,
+			List<Organizations> listOrg) {
+			Organizations tempOrg = null;
+		//root org
+		for(int i=0;i<listOrg.size();i++) {
+			if(listOrg.get(i).getParentId().equals(rootParentOrgId)){
+				tempOrg = listOrg.get(i); 
+				tempOrg.setReorgNamePath(true);
+				tempOrg.setNamePath("/"+tempOrg.getName());
+				tempOrg.setCodePath("/"+tempOrg.getId());
+				tempOrg.setParentId("-1");
+				tempOrg.setParentName("");
+				orgMap.put(tempOrg.getId(), tempOrg);
+	        }
+		}
+		
+ 	   	do {
+ 	   		for(int i=0;i<listOrg.size();i++) {
+ 	   			if(!listOrg.get(i).isReorgNamePath()) {
+ 	   				Organizations parentOrg = orgMap.get(listOrg.get(i).getParentId());
+	 	   			tempOrg = listOrg.get(i); 
+	 	   			if(!tempOrg.isReorgNamePath() && parentOrg != null){
+	 	   				tempOrg.setReorgNamePath(true);
+	 	   				tempOrg.setParentName(parentOrg.getName());
+	 	   				tempOrg.setCodePath(parentOrg.getCodePath()+"/"+tempOrg.getId());
+	 	   				tempOrg.setNamePath(parentOrg.getNamePath()+"/"+tempOrg.getName());
+	 	   				orgMap.put(tempOrg.getId(), tempOrg);
+						_logger.info("reorg : " + tempOrg);
+	 	   			}
+ 	   			}
+ 	   		}
+ 	   	}while(listOrg.size()>listOrg.size());
+	}
+
+	public OrganizationsService getOrganizationsService() {
+		return organizationsService;
+	}
+
+	public void setOrganizationsService(OrganizationsService organizationsService) {
+		this.organizationsService = organizationsService;
+	}
+
+	@Override
+	public void setSynchronizer(Synchronizers synchronizer) {
+		
+		
+	}
+
+}

+ 31 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/mapper/OrganizationsCastMapper.java

@@ -0,0 +1,31 @@
+/*
+ * 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.mapper;
+import java.util.List;
+
+import org.apache.mybatis.jpa.persistence.IJpaBaseMapper;
+import org.maxkey.entity.Organizations;
+import org.maxkey.entity.OrganizationsCast;
+
+public interface OrganizationsCastMapper extends IJpaBaseMapper<OrganizationsCast> {
+
+	public List<Organizations> queryOrgs(Organizations organization);
+	
+	
+	public long updateCast(OrganizationsCast organizationsCast);
+}

+ 12 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/service/AccountsService.java

@@ -24,6 +24,7 @@ import org.maxkey.constants.ConstantsStatus;
 import org.maxkey.crypto.ReciprocalUtils;
 import org.maxkey.entity.Accounts;
 import org.maxkey.entity.AccountsStrategy;
+import org.maxkey.entity.OrganizationsCast;
 import org.maxkey.entity.UserInfo;
 import org.maxkey.persistence.kafka.KafkaIdentityAction;
 import org.maxkey.persistence.kafka.KafkaIdentityTopic;
@@ -45,6 +46,9 @@ public class AccountsService  extends JpaBaseService<Accounts>{
     @Autowired
     AccountsStrategyService accountsStrategyService;
     
+    @Autowired
+    OrganizationsCastService organizationsCastService;
+    
 	public AccountsService() {
 		super(AccountsMapper.class);
 	}
@@ -64,6 +68,10 @@ public class AccountsService  extends JpaBaseService<Accounts>{
 	            if(kafkaPersistService.getApplicationConfig().isKafkaSupport()) {
 	                UserInfo loadUserInfo = userInfoService.loadUserRelated(account.getUserId());
 	                account.setUserInfo(loadUserInfo);
+	                OrganizationsCast cast = new OrganizationsCast();
+                    cast.setProvider(account.getAppId());
+                    cast.setOrgId(loadUserInfo.getDepartmentId());
+                    account.setOrgCast(organizationsCastService.query(cast));
 	                kafkaPersistService.send(
 	                        KafkaIdentityTopic.ACCOUNT_TOPIC, 
 	                        account,
@@ -80,6 +88,10 @@ public class AccountsService  extends JpaBaseService<Accounts>{
                 if(kafkaPersistService.getApplicationConfig().isKafkaSupport()) {
                     UserInfo loadUserInfo = userInfoService.loadUserRelated(account.getUserId());
                     account.setUserInfo(loadUserInfo);
+                    OrganizationsCast cast = new OrganizationsCast();
+                    cast.setProvider(account.getAppId());
+                    cast.setOrgId(loadUserInfo.getDepartmentId());
+                    account.setOrgCast(organizationsCastService.query(cast));
                     kafkaPersistService.send(
                             KafkaIdentityTopic.ACCOUNT_TOPIC, 
                             account,

+ 51 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/service/OrganizationsCastService.java

@@ -0,0 +1,51 @@
+/*
+ * 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.OrganizationsCast;
+import org.maxkey.persistence.mapper.OrganizationsCastMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Repository;
+
+
+@Repository
+public class OrganizationsCastService  extends JpaBaseService<OrganizationsCast>{
+
+	final static Logger _logger = LoggerFactory.getLogger(OrganizationsCastService.class);
+
+    
+	public OrganizationsCastService() {
+		super(OrganizationsCastMapper.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.connsec.db.service.BaseService#getMapper()
+	 */
+	@Override
+	public OrganizationsCastMapper getMapper() {
+		// TODO Auto-generated method stub
+		return (OrganizationsCastMapper)super.getMapper();
+	}
+
+	public boolean updateCast(OrganizationsCast organizationsCast) {
+		return getMapper().updateCast(organizationsCast) > 0;
+	}
+
+}

+ 22 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java

@@ -40,6 +40,7 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.maxkey.constants.ConstantsStatus;
 import org.maxkey.crypto.ReciprocalUtils;
 import org.maxkey.crypto.password.PasswordReciprocal;
+import org.maxkey.entity.Accounts;
 import org.maxkey.entity.ChangePassword;
 import org.maxkey.entity.UserInfo;
 import org.maxkey.persistence.db.PasswordPolicyValidator;
@@ -81,6 +82,8 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
 	
 	 @Autowired
 	 protected JdbcTemplate jdbcTemplate;
+	 
+	 AccountsService accountsService;
 	
 	public UserInfoService() {
 		super(UserInfoMapper.class);
@@ -116,11 +119,13 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
         if (super.update(userInfo)) {
             if(kafkaPersistService.getApplicationConfig().isKafkaSupport()) {
                 UserInfo loadUserInfo = loadUserRelated(userInfo.getId());
+                accountUpdate(loadUserInfo);
                 kafkaPersistService.send(
                         KafkaIdentityTopic.USERINFO_TOPIC, 
                         loadUserInfo,
                         KafkaIdentityAction.UPDATE_ACTION);
             }
+            
             changePasswordProvisioning(userInfo);
             return true;
         }
@@ -138,10 +143,27 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
 		            KafkaIdentityTopic.USERINFO_TOPIC, 
 		            loadUserInfo, 
 		            KafkaIdentityAction.DELETE_ACTION);
+			accountUpdate(loadUserInfo);
 			 return true;
 		}
 		return false;
 	}
+	
+    //更新账号状态
+    public void accountUpdate(UserInfo userInfo) {
+        if(userInfo.getStatus() != ConstantsStatus.ACTIVE) {
+            if(accountsService==null) {
+                accountsService = 
+                        (AccountsService)WebContext.getBean("accountsService"); 
+            }
+            Accounts queryAcount =new Accounts();
+            queryAcount.setUserId(userInfo.getId());
+            for (Accounts acount : accountsService.query(queryAcount)) {
+                acount.setStatus(ConstantsStatus.INACTIVE);
+                accountsService.update(acount);
+            }
+        }
+    }
 
 	public UserInfo loadUserRelated(String userId) {
 	    UserInfo loadUserInfo =this.get(userId);

+ 1 - 0
maxkey-webs/maxkey-web-mgt/build.gradle

@@ -19,6 +19,7 @@ dependencies {
    	implementation project(":maxkey-identitys:maxkey-identity-scim")   
    	implementation project(":maxkey-identitys:maxkey-identity-rest")	
    	implementation project(":maxkey-identitys:maxkey-synchronizers")
+   	implementation project(":maxkey-identitys:maxkey-synchronizers-reorgdept")
    	implementation project(":maxkey-identitys:maxkey-synchronizers-activedirectory")
    	implementation project(":maxkey-identitys:maxkey-synchronizers-ldap")
    	implementation project(":maxkey-identitys:maxkey-synchronizers-workweixin")

+ 2 - 2
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/accountsAdd.ftl

@@ -51,7 +51,7 @@
 				    <input class="button btn btn-primary mr-3 window"  type="button"    id="selectUserinfoBtn" value="<@locale code="button.text.select" />"
                      wurl="<@base/>/userinfo/select"
                                     wwidth="800"
-                                    wheight="500"
+                                    wheight="620"
                                     target="window"/>
                 </td>
 			</tr>
@@ -75,7 +75,7 @@
 				    <input class="button btn btn-primary mr-3 window"  type="button"    id="selectAppsubmitBtn" value="<@locale code="button.text.select" />"
                       wurl="<@base/>/apps/select"
                                     wwidth="800"
-                                    wheight="500"
+                                    wheight="620"
                                     target="window"/>
                 </td>
 			</tr>

+ 33 - 39
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/apps/selectAppsList.ftl

@@ -24,60 +24,54 @@
 </script>
 </head>
 <body>	
-	<div id="tool_box">
+	<div>
 			<table    class="table table-bordered" >
  				<tr>
 		 			<td width="120px"><@locale code="apps.name"/>:</td>
-		 			<td  width="374px" nowrap>
+		 			<td  width="300px" nowrap>
 		 				<form id="basic_search_form">
-			 				<input type="text" class="form-control" name="name" style ="width:150px;float: left;">
-			 				<input class="button button btn btn-primary mr-3"  id="searchBtn" type="button" size="50" value="<@locale code="button.text.search"/>">
-		 					
+			 				<input type="text" class="form-control" name="name" >
 		 				 </form>
 		 			</td>
 		 			<td colspan="2">
-		 				<div >
-		 					<input class="button btn btn-primary mr-3"   id="selectBtn" type="button" value="<@locale code="button.text.select"/>" />
+		 				<div id="tool_box_right">    
+		 				    <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-success mr-3"   id="selectBtn" type="button" value="<@locale code="button.text.select"/>" />
 				 		</div>
 		 			</td>
 		 		</tr>
 		 	</table>
-		
-		 		
  	</div>
  	
  	<div id="advanced_search">
  		
  	</div>
- 	
-		<div class="mainwrap" id="main">
-			<table  data-url="<@base/>/apps/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="[5, 10, 25, 50]"
-						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="id" data-formatter="iconFormatter"><@locale code="apps.icon"/></th>
-						<th data-field="name"><@locale code="apps.name"/></th>
-						<th data-field="protocol"  data-visible="false"><@locale code="apps.protocol"/></th>
-						<th data-field="category"><@locale code="apps.category"/></th>
-						<th data-field="vendor"  data-visible="false"><@locale code="apps.vendor"/></th>
-						<th data-field="loginUrl" data-visible="false"><@locale code="log.loginhistory.loginUrl"/></th>
-			
-					</tr>
-				</thead>
-			</table>
-		</div>
+	<table  data-url="<@base/>/apps/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="[5, 10, 25, 50]"
+				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="id" data-formatter="iconFormatter"><@locale code="apps.icon"/></th>
+				<th data-field="name"><@locale code="apps.name"/></th>
+				<th data-field="protocol"  data-visible="false"><@locale code="apps.protocol"/></th>
+				<th data-field="category"><@locale code="apps.category"/></th>
+				<th data-field="vendor"  data-visible="false"><@locale code="apps.vendor"/></th>
+				<th data-field="loginUrl" data-visible="false"><@locale code="log.loginhistory.loginUrl"/></th>
+	
+			</tr>
+		</thead>
+	</table>
 </body>
 </html>

+ 22 - 7
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/userinfo/userinfoSelect.ftl

@@ -149,20 +149,35 @@
 </head>
 <body>
  <div>
-    <form id="basic_search_form" style="display:none">
-        <input id="departmentId"  class="form-control" name="departmentId" type="text" >
-        <input  class="button btn mr-3 btn-primary"    id="searchBtn" type="button" size="50" value="<@locale code="button.text.search"/>">
-    </form>
- 	<input class="button btn btn-primary mr-3"  style="float: right;" id="winClose" type="button" value="<@locale code="button.text.select" />" >
+    <table   class="table table-bordered">
+            <tr>
+                <td  width="120px">
+                     <@locale code="userinfo.username"/>:
+                </td>
+                <td  width="300px">
+                    <form id="basic_search_form">
+                        <input  class="form-control"   id="departmentId"  name="departmentId" type="hidden">
+                        <input  class="form-control"  name="username" type="text" >
+                    </form>
+                </td>
+                <td colspan="2"> 
+                     <div id="tool_box_right">    
+                         <input  class="button btn mr-3 btn-primary"    id="searchBtn" type="button" value="<@locale code="button.text.search"/>">
+                         <input  class="button btn mr-3 btn-success"    id="winClose" type="button" value="<@locale code="button.text.select" />" >
+                    </div>
+                </td>
+            </tr>
+        </table>
+ 	
  </div>
      <!-- content -->  
   <table class="table table-bordered"   width="100%" >
    <tr>
-      <td valign="top"  class="td_1" style="vertical-align: top;">
+      <td valign="top"  style="vertical-align: top;min-width: 200px;">
       	<div id="orgsTree" class="ztree"></div>
          
       </td>
-      <td  valign="top"  class="td_1" style="vertical-align: top;">
+      <td  valign="top"  style="vertical-align: top;">
 	 	<table  data-url="<@base/>/userinfo/grid"
 				id="datagrid"
 				data-toggle="table"

+ 1 - 0
settings.gradle

@@ -34,6 +34,7 @@ include (
 'maxkey-identitys:maxkey-identity-scim',
 'maxkey-identitys:maxkey-identity-rest',
 'maxkey-identitys:maxkey-synchronizers',
+'maxkey-identitys:maxkey-synchronizers-reorgdept',
 'maxkey-identitys:maxkey-synchronizers-activedirectory',
 'maxkey-identitys:maxkey-synchronizers-ldap',
 'maxkey-identitys:maxkey-synchronizers-dingding',