Bläddra i källkod

Accounts Strategy

Crystal.Sea 3 år sedan
förälder
incheckning
4be78af5e3
22 ändrade filer med 1259 tillägg och 47 borttagningar
  1. 2 2
      README.md
  2. 2 2
      README_en.md
  3. 1 1
      README_zh.md
  4. 4 1
      ReleaseNotes.txt
  5. 212 0
      maxkey-core/src/main/java/org/maxkey/entity/AccountsStrategy.java
  6. BIN
      maxkey-lib/mybatis-jpa-extra-2.6.jar
  7. 37 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/mapper/AccountsStrategyMapper.java
  8. 130 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/AccountsStrategyService.java
  9. 29 0
      maxkey-persistence/src/main/resources/org/maxkey/persistence/mapper/xml/mysql/AccountsStrategyMapper.xml
  10. 5 5
      maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/contorller/AccountsController.java
  11. 146 0
      maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/contorller/AccountsStrategyController.java
  12. 7 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message.properties
  13. 6 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_en.properties
  14. 7 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_zh_CN.properties
  15. 48 20
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/accountsAdd.ftl
  16. 12 5
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/accountsList.ftl
  17. 222 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accountsstrategy/accountsStrategyAdd.ftl
  18. 130 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accountsstrategy/accountsStrategyList.ftl
  19. 235 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accountsstrategy/accountsStrategyUpdate.ftl
  20. 8 5
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/groups/selectGroupsList.ftl
  21. 8 1
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/sidenav.ftl
  22. 8 5
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/roles/selectRolesList.ftl

+ 2 - 2
README.md

@@ -4,7 +4,7 @@
 
 # Overview
 
-<b>Maxkey </b> Single Sign On system, which means the Maximum key, <b>Leading-Edge Enterprise-Class IAM Identity and Access management product </b>, Support OAuth 2.0/OPENID CONNECT, SAML 2.0, JWT, CAS, SCIM and other standard protocols, and provide <b> Simple, Standard, Secure and Open </b> Identity management (IDM), Access management (AM), Single Sign On (SSO), RBAC permission management and Resource management.
+<b>Maxkey </b> Single Sign On system, which means the Maximum key, <b>Leading-Edge Enterprise-Class IAM Identity and Access management product </b>, Support OAuth 2.x/OPENID CONNECT, SAML 2.0, JWT, CAS, SCIM and other standard protocols, and provide <b> Simple, Standard, Secure and Open </b> Identity management (IDM), Access management (AM), Single Sign On (SSO), RBAC permission management and Resource management.
 
 Official Website <a href="https://www.maxkey.top" target="_blank"><b>Official</b></a> |  <a href="https://maxkeytop.gitee.io" target="_blank"><b>Line2</b></a>
 
@@ -89,7 +89,7 @@ App Management UI
 
 # Download
 
-Download the current version of Baidu Pan,<a href="https://maxkey.top/zh/about/download.html" target="_blank"> history version</a>
+Download the current version from Baidu Pan,<a href="https://maxkey.top/zh/about/download.html" target="_blank"> history version</a>
 
 | Version    | Date   |  Docker  |  Pan URL  |  Pan Code  |
 | --------   | :----- | :----    | :----     | :----      |

+ 2 - 2
README_en.md

@@ -4,7 +4,7 @@
 
 # Overview
 
-<b>Maxkey </b> Single Sign On system, which means the Maximum key, <b>Leading-Edge Enterprise-Class IAM Identity and Access management product </b>, Support OAuth 2.0/OPENID CONNECT, SAML 2.0, JWT, CAS, SCIM and other standard protocols, and provide <b> Simple, Standard, Secure and Open </b> Identity management (IDM), Access management (AM), Single Sign On (SSO), RBAC permission management and Resource management.
+<b>Maxkey </b> Single Sign On system, which means the Maximum key, <b>Leading-Edge Enterprise-Class IAM Identity and Access management product </b>, Support OAuth 2.x/OPENID CONNECT, SAML 2.0, JWT, CAS, SCIM and other standard protocols, and provide <b> Simple, Standard, Secure and Open </b> Identity management (IDM), Access management (AM), Single Sign On (SSO), RBAC permission management and Resource management.
 
 Official Website <a href="https://www.maxkey.top" target="_blank"><b>Official</b></a> |  <a href="https://maxkeytop.gitee.io" target="_blank"><b>Line2</b></a>
 
@@ -89,7 +89,7 @@ App Management UI
 
 # Download
 
-Download the current version of Baidu Pan,<a href="https://maxkey.top/zh/about/download.html" target="_blank"> history version</a>
+Download the current version from Baidu Pan,<a href="https://maxkey.top/zh/about/download.html" target="_blank"> history version</a>
 
 | Version    | Date   |  Docker  |  Pan URL  |  Pan Code  |
 | --------   | :----- | :----    | :----     | :----      |

+ 1 - 1
README_zh.md

@@ -5,7 +5,7 @@
 
 # 概述
 
-<b>MaxKey</b>单点登录认证系统(Single Sign On System),谐音马克思的钥匙寓意是最大钥匙,是<b>业界领先的企业级IAM身份管理和认证产品</b>,支持OAuth 2.0/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供<b>简单、标准、安全和开放</b>的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。
+<b>MaxKey</b>单点登录认证系统(Single Sign On System),谐音马克思的钥匙寓意是最大钥匙,是<b>业界领先的企业级IAM身份管理和认证产品</b>,支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议,提供<b>简单、标准、安全和开放</b>的用户身份管理(IDM)、身份认证(AM)、单点登录(SSO)、RBAC权限管理和资源管理等。
 
 官方网站  <a href="https://www.maxkey.top" target="_blank"><b>官网</b></a> |  <a href="https://maxkeytop.gitee.io" target="_blank"><b>官网二线</b></a>
 

+ 4 - 1
ReleaseNotes.txt

@@ -11,7 +11,10 @@
     *(MAXKEY-210810) 日志优化
     *(MAXKEY-210811) 密码修改问题修复
     *(MAXKEY-210812) 环境变量参数优化
-    *(MAXKEY-210813) 依赖jar引用、更新和升级
+    *(MAXKEY-210813) 管理端图标显示修复
+    *(MAXKEY-210814) 管理端‘应用管理’移动到‘配置管理’的菜单项
+    *(MAXKEY-210815) OAuth的数据库加载增加本地缓存
+    *(MAXKEY-210816) 依赖jar引用、更新和升级
          mybatis-jpa-extra   2.6
          druid               1.2.8
          caffeine            2.9.2

+ 212 - 0
maxkey-core/src/main/java/org/maxkey/entity/AccountsStrategy.java

@@ -0,0 +1,212 @@
+/*
+ * 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_ACCOUNTS_STRATEGY")
+public class AccountsStrategy extends JpaBaseEntity implements Serializable {
+    
+    /**
+     * 
+     */
+    private static final long serialVersionUID = -8743329570694948718L;
+    @Id
+    @Column
+    @GeneratedValue(strategy = GenerationType.AUTO,generator = "snowflakeid")
+    private String id;
+    @Column
+    private String name;
+    @Column
+    private String appId;
+    @Column
+    private String appName;
+    @Column
+    private String mapping;
+
+    @Column
+    String filters ;
+    
+    @Column
+    String orgIdsList;
+    @Column
+    String status;
+    @Column
+    String description;
+    @Column
+    String createdBy;
+    @Column
+    String createdDate;
+    @Column
+    String modifiedBy;
+    @Column
+    String modifiedDate;
+
+    public AccountsStrategy() {
+        super();
+        // TODO Auto-generated constructor stub
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public String getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(String createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public String getModifiedBy() {
+        return modifiedBy;
+    }
+
+    public void setModifiedBy(String modifiedBy) {
+        this.modifiedBy = modifiedBy;
+    }
+
+    public String getModifiedDate() {
+        return modifiedDate;
+    }
+
+    public void setModifiedDate(String modifiedDate) {
+        this.modifiedDate = modifiedDate;
+    }
+
+    public String getFilters() {
+        return filters;
+    }
+
+    public void setFilters(String filters) {
+        this.filters = filters;
+    }
+
+    public String getOrgIdsList() {
+        return orgIdsList;
+    }
+
+    public void setOrgIdsList(String orgIdsList) {
+        this.orgIdsList = orgIdsList;
+    }
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
+    public String getAppName() {
+        return appName;
+    }
+
+    public void setAppName(String appName) {
+        this.appName = appName;
+    }
+
+    public String getMapping() {
+        return mapping;
+    }
+
+    public void setMapping(String mapping) {
+        this.mapping = mapping;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("AccountsStrategy [id=");
+        builder.append(id);
+        builder.append(", name=");
+        builder.append(name);
+        builder.append(", appId=");
+        builder.append(appId);
+        builder.append(", appName=");
+        builder.append(appName);
+        builder.append(", mapping=");
+        builder.append(mapping);
+        builder.append(", filters=");
+        builder.append(filters);
+        builder.append(", orgIdsList=");
+        builder.append(orgIdsList);
+        builder.append(", status=");
+        builder.append(status);
+        builder.append(", description=");
+        builder.append(description);
+        builder.append(", createdBy=");
+        builder.append(createdBy);
+        builder.append(", createdDate=");
+        builder.append(createdDate);
+        builder.append(", modifiedBy=");
+        builder.append(modifiedBy);
+        builder.append(", modifiedDate=");
+        builder.append(modifiedDate);
+        builder.append("]");
+        return builder.toString();
+    }
+   
+    
+
+}

BIN
maxkey-lib/mybatis-jpa-extra-2.6.jar


+ 37 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/mapper/AccountsStrategyMapper.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+/**
+ * 
+ */
+package org.maxkey.persistence.mapper;
+
+import java.util.List;
+
+import org.apache.mybatis.jpa.persistence.IJpaBaseMapper;
+import org.maxkey.entity.AccountsStrategy;
+import org.maxkey.entity.Groups;
+
+/**
+ * @author Crystal.sea
+ *
+ */
+
+public  interface AccountsStrategyMapper extends IJpaBaseMapper<AccountsStrategy> {
+
+    public List<Groups> queryDynamicGroups(Groups groups);
+}

+ 130 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/service/AccountsStrategyService.java

@@ -0,0 +1,130 @@
+/*
+ * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+package org.maxkey.persistence.service;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.List;
+
+import org.apache.mybatis.jpa.persistence.JpaBaseService;
+import org.maxkey.entity.AccountsStrategy;
+import org.maxkey.entity.Groups;
+import org.maxkey.persistence.mapper.AccountsStrategyMapper;
+import org.maxkey.persistence.mapper.GroupsMapper;
+import org.maxkey.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Repository;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+@Repository
+public class AccountsStrategyService  extends JpaBaseService<AccountsStrategy> implements Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = -921086134545225302L;
+    
+    final static Logger _logger = LoggerFactory.getLogger(AccountsStrategyService.class);
+   /*
+    @JsonIgnore
+    @Autowired
+    @Qualifier("groupMemberService")
+    GroupMemberService accountsStrategyService;
+    */
+	public AccountsStrategyService() {
+		super(AccountsStrategyMapper.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.connsec.db.service.BaseService#getMapper()
+	 */
+	@Override
+	public AccountsStrategyMapper getMapper() {
+		// TODO Auto-generated method stub
+		return (AccountsStrategyMapper)super.getMapper();
+	}
+	
+	
+	public List<Groups> queryDynamicGroups(Groups groups){
+	    return this.getMapper().queryDynamicGroups(groups);
+	}
+	
+	public boolean deleteById(String groupId) {
+	    this.remove(groupId);
+	    //groupMemberService.deleteByGroupId(groupId);
+	    return true;
+	}
+	/*
+	public void refreshDynamicGroups(Groups dynamicGroup){
+	    if(dynamicGroup.getDynamic().equals("1")) {
+	        boolean isDynamicTimeSupport = false;
+	        boolean isBetweenEffectiveTime = false;
+	        if(dynamicGroup.getResumeTime()!=null&&dynamicGroup.getResumeTime().equals("")
+	                &&dynamicGroup.getSuspendTime()!=null&&dynamicGroup.getSuspendTime().equals("")) {
+	            LocalTime currentTime = LocalDateTime.now().toLocalTime();
+	            LocalTime resumeTime = LocalTime.parse(dynamicGroup.getResumeTime());
+	            LocalTime suspendTime = LocalTime.parse(dynamicGroup.getSuspendTime());
+	            
+	            _logger.info("currentTime: " + currentTime 
+                        + " , resumeTime : " + resumeTime 
+                        + " , suspendTime: " + suspendTime);
+	            isDynamicTimeSupport = true;
+	            
+	            if(resumeTime.isBefore(currentTime) && currentTime.isBefore(suspendTime)) {
+	                isBetweenEffectiveTime = true;
+	            }
+	            
+	        }
+	        
+    	    if(dynamicGroup.getOrgIdsList()!=null && !dynamicGroup.getOrgIdsList().equals("")) {
+    	        dynamicGroup.setOrgIdsList("'"+dynamicGroup.getOrgIdsList().replace(",", "','")+"'");
+    	    }
+    	    String filters = dynamicGroup.getFilters();
+    	    if(StringUtils.filtersSQLInjection(filters.toLowerCase())) {  
+    	        _logger.info("filters include SQL Injection Attack Risk.");
+    	        return;
+    	    }
+    	    
+    	    filters = filters.replace("&", " AND ");
+    	    filters = filters.replace("|", " OR ");
+    	    
+    	    dynamicGroup.setFilters(filters);
+    	    
+    	    if(isDynamicTimeSupport) {
+    	        if(isBetweenEffectiveTime) {
+    	            groupMemberService.deleteDynamicGroupMember(dynamicGroup);
+                    groupMemberService.addDynamicGroupMember(dynamicGroup);
+    	        }else {
+    	            groupMemberService.deleteDynamicGroupMember(dynamicGroup);
+    	        }
+    	    }else{
+                groupMemberService.deleteDynamicGroupMember(dynamicGroup);
+                groupMemberService.addDynamicGroupMember(dynamicGroup);
+            }
+	    }
+    }*/
+
+  
+	
+
+	
+}

+ 29 - 0
maxkey-persistence/src/main/resources/org/maxkey/persistence/mapper/xml/mysql/AccountsStrategyMapper.xml

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

+ 5 - 5
maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/contorller/AccountsController.java

@@ -40,7 +40,7 @@ import org.springframework.web.servlet.ModelAndView;
 
 
 @Controller
-@RequestMapping(value={"/app/accounts"})
+@RequestMapping(value={"/accounts"})
 public class AccountsController {
 	final static Logger _logger = LoggerFactory.getLogger(AccountsController.class);
 
@@ -58,7 +58,7 @@ public class AccountsController {
 	
 	@RequestMapping(value={"/list"})
 	public ModelAndView appAccountsList(){
-		ModelAndView modelAndView=new ModelAndView("/accounts/appAccountsList");
+		ModelAndView modelAndView=new ModelAndView("/accounts/accountsList");
 		return modelAndView;
 	}
 
@@ -71,14 +71,14 @@ public class AccountsController {
 	
 	@RequestMapping(value = { "/forwardSelect/{appId}" })
 	public ModelAndView forwardSelect(@PathVariable("appId") String appId) {
-		ModelAndView modelAndView=new ModelAndView("/accounts/appAccountsAddSelect");
+		ModelAndView modelAndView=new ModelAndView("/accounts/accountsAddSelect");
 		modelAndView.addObject("appId",appId);
 		return modelAndView;
 	}
 	
 	@RequestMapping(value = { "/forwardAdd" })
 	public ModelAndView forwardAdd(@ModelAttribute("appAccounts") Accounts appAccounts) {
-		ModelAndView modelAndView=new ModelAndView("/accounts/appAccountsAdd");
+		ModelAndView modelAndView=new ModelAndView("/accounts/accountsAdd");
 		//Applications  app= appsService.get(appAccounts.getAppId());
 		//appAccounts.setAppName(app.getName());
 		modelAndView.addObject("model",appAccounts);
@@ -103,7 +103,7 @@ public class AccountsController {
 	
 	@RequestMapping(value = { "/forwardUpdate/{id}" })
 	public ModelAndView forwardUpdate(@PathVariable("id") String id) {
-		ModelAndView modelAndView=new ModelAndView("/accounts/appAccountsUpdate");
+		ModelAndView modelAndView=new ModelAndView("/accounts/accountsUpdate");
 		Accounts appAccounts =accountsService.get(id);
 		
 		appAccounts.setRelatedPassword(ReciprocalUtils.decoder(appAccounts.getRelatedPassword()));

+ 146 - 0
maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/contorller/AccountsStrategyController.java

@@ -0,0 +1,146 @@
+/*
+ * 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.web.contorller;
+
+import org.apache.mybatis.jpa.persistence.JpaPageResults;
+import org.maxkey.constants.ConstantsOperateMessage;
+import org.maxkey.entity.AccountsStrategy;
+import org.maxkey.entity.Roles;
+import org.maxkey.persistence.service.AccountsStrategyService;
+import org.maxkey.persistence.service.RolesService;
+import org.maxkey.web.WebContext;
+import org.maxkey.web.message.Message;
+import org.maxkey.web.message.MessageType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+
+@Controller
+@RequestMapping(value={"/accountsstrategy"})
+public class AccountsStrategyController {
+	final static Logger _logger = LoggerFactory.getLogger(AccountsStrategyController.class);
+	
+	@Autowired
+	@Qualifier("accountsStrategyService")
+	AccountsStrategyService accountsStrategyService;
+
+	
+	
+	@RequestMapping(value={"/list"})
+	public ModelAndView rolesList(){
+		return new ModelAndView("accountsstrategy/accountsstrategyList");
+	}
+
+	
+	@RequestMapping(value = { "/grid" })
+	@ResponseBody
+	public JpaPageResults<AccountsStrategy> queryDataGrid(@ModelAttribute("accountsStrategy") AccountsStrategy accountsStrategy) {
+		_logger.debug(""+accountsStrategy);
+		return accountsStrategyService.queryPageResults(accountsStrategy);
+	}
+
+	
+	@RequestMapping(value = { "/forwardAdd" })
+	public ModelAndView forwardAdd() {
+		return new ModelAndView("accountsstrategy/accountsstrategyAdd");
+	}
+	
+	@RequestMapping(value = { "/forwardUpdate/{id}" })
+	public ModelAndView forwardUpdate(@PathVariable("id") String id) {
+		ModelAndView modelAndView=new ModelAndView("accountsstrategy/accountsstrategyUpdate");
+		AccountsStrategy accountsStrategy=accountsStrategyService.get(id);
+		modelAndView.addObject("model",accountsStrategy);
+		return modelAndView;
+	}
+	
+	@ResponseBody
+	@RequestMapping(value={"/add"})
+	public Message insert(@ModelAttribute("accountsStrategy") AccountsStrategy accountsStrategy) {
+		_logger.debug("-Add  :" + accountsStrategy);
+		
+		if (accountsStrategyService.insert(accountsStrategy)) {
+		    //rolesService.refreshDynamicRoles(role);
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.INSERT_SUCCESS),MessageType.success);
+			
+		} else {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.INSERT_SUCCESS),MessageType.error);
+		}
+		
+	}
+	
+	/**
+	 * 查询
+	 * @param role
+	 * @return
+	 */
+	@ResponseBody
+	@RequestMapping(value={"/query"}) 
+	public Message query(@ModelAttribute("accountsStrategy") AccountsStrategy accountsStrategy) {
+		_logger.debug("-query  :" + accountsStrategy);
+		if (accountsStrategyService.load(accountsStrategy)!=null) {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.INSERT_SUCCESS),MessageType.success);
+			
+		} else {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.INSERT_ERROR),MessageType.error);
+		}
+		
+	}
+	
+	/**
+	 * 修改
+	 * @param role
+	 * @return
+	 */
+	@ResponseBody
+	@RequestMapping(value={"/update"})  
+	public Message update(@ModelAttribute("accountsStrategy") AccountsStrategy accountsStrategy) {
+		_logger.debug("-update  AccountsStrategy :" + accountsStrategy);
+		
+		if (accountsStrategyService.update(accountsStrategy)) {
+		   // rolesService.refreshDynamicRoles(role);
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_SUCCESS),MessageType.success);
+			
+		} else {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_ERROR),MessageType.error);
+		}
+		
+	}
+	
+
+	@ResponseBody
+	@RequestMapping(value={"/delete"})
+	public Message delete(@ModelAttribute("role") Roles role) {
+		_logger.debug("-delete  role :" + role);
+		
+		if (accountsStrategyService.deleteById(role.getId())) {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.DELETE_SUCCESS),MessageType.success);
+			
+		} else {
+			return  new Message(WebContext.getI18nValue(ConstantsOperateMessage.DELETE_SUCCESS),MessageType.error);
+		}
+		
+	}
+}

+ 7 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message.properties

@@ -447,6 +447,12 @@ account.appId=\u5e94\u7528\u7f16\u53f7
 account.appName=\u5e94\u7528\u540d\u79f0
 account.relatedUsername=\u7528\u6237\u8d26\u53f7
 account.relatedPassword=\u8d26\u53f7\u5bc6\u7801
+#accounts.strategy
+accounts.strategy.id=\u7B56\u7565\u7f16\u7801
+accounts.strategy.name=\u7B56\u7565\u540D\u79F0
+accounts.strategy.mapping=\u8D26\u53F7\u6620\u5C04
+accounts.strategy.filters=\u7528\u6237\u6761\u4EF6
+accounts.strategy.orgidslist=\u673A\u6784\u5217\u8868
 #synchronizers
 synchronizers.id=\u7F16\u53F7
 synchronizers.name=\u540C\u6B65\u5668\u540D\u79F0
@@ -566,6 +572,7 @@ navs.orgs=\u673a\u6784\u7ba1\u7406
 navs.users=\u7528\u6237\u7ba1\u7406
 navs.apps=\u5e94\u7528\u7ba1\u7406
 navs.accounts=\u8d26\u53f7\u7ba1\u7406
+navs.accounts.strategy=\u8D26\u53F7\u7B56\u7565
 navs.privileges=\u8BBF\u95EE\u63A7\u5236\u7BA1\u7406
 navs.groups=\u7ec4\u7ba1\u7406
 navs.groups.member=\u6210\u5458\u7ba1\u7406

+ 6 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_en.properties

@@ -456,6 +456,11 @@ account.appName=appName
 account.relatedUsername=relatedUsername
 account.relatedPassword=relatedPassword
 
+accounts.strategy.id=Id
+accounts.strategy.name=name
+accounts.strategy.mapping=mapping
+accounts.strategy.filters=filters
+accounts.strategy.orgidslist=orgIdsList
 
 #synchronizers
 synchronizers.id=id
@@ -577,6 +582,7 @@ navs.orgs=Orgs
 navs.users=Users
 navs.apps=Apps
 navs.accounts=Accounts
+navs.accounts.strategy=AccountsStrategy
 navs.privileges=Access Control
 navs.groups=Groups
 navs.groups.member=Groups Member

+ 7 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_zh_CN.properties

@@ -453,6 +453,12 @@ account.appId=\u5e94\u7528\u7f16\u53f7
 account.appName=\u5e94\u7528\u540d\u79f0
 account.relatedUsername=\u7528\u6237\u8d26\u53f7
 account.relatedPassword=\u8d26\u53f7\u5bc6\u7801
+#accounts.strategy
+accounts.strategy.id=\u7B56\u7565\u7f16\u7801
+accounts.strategy.name=\u7B56\u7565\u540D\u79F0
+accounts.strategy.mapping=\u8D26\u53F7\u6620\u5C04
+accounts.strategy.filters=\u7528\u6237\u6761\u4EF6
+accounts.strategy.orgidslist=\u673A\u6784\u5217\u8868
 #synchronizers
 synchronizers.id=\u7F16\u53F7
 synchronizers.name=\u540C\u6B65\u5668\u540D\u79F0
@@ -571,6 +577,7 @@ navs.orgs=\u673a\u6784\u7ba1\u7406
 navs.users=\u7528\u6237\u7ba1\u7406
 navs.apps=\u5e94\u7528\u7ba1\u7406
 navs.accounts=\u8d26\u53f7\u7ba1\u7406
+navs.accounts.strategy=\u8D26\u53F7\u7B56\u7565
 navs.privileges=\u8BBF\u95EE\u63A7\u5236\u7BA1\u7406
 navs.groups=\u7ec4\u7ba1\u7406
 navs.groups.member=\u6210\u5458\u7ba1\u7406

+ 48 - 20
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/appAccountsAdd.ftl → maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/accountsAdd.ftl

@@ -11,6 +11,19 @@
             $("#relatedUsername").val($("#username").val());
           }
         });
+        
+        $("#generateSecret").on("click",function(){
+            $.post("<@base/>/userinfo/randomPassword/", {_method:"post",currTime:(new Date()).getTime()}, function(data) {
+                $("#relatedPassword").val(data+"");
+            }); 
+        });
+        $("#view").on("click",function(){
+            if($("#relatedPassword").attr("type")=="text"){
+                $("#relatedPassword").attr("type","password");
+            }else{
+                $("#relatedPassword").attr("type","text");
+            }
+        });
     });
     </script>
 </head>
@@ -25,54 +38,69 @@
 					<input  required="" type="text" id="id" name="id" readonly  class="form-control" title="" value="${model.id!}"/>
 	
 				</td>
+				<td>
+				</td>
 			</tr>
 			<tr>
 				<th><@locale code="userinfo.username" />:</th>
-				<td nowrap>
-					<input  required="" readonly type="text" id="username" name="username"  class="form-control username" title="" value="${model.username!}" style="float:left;width:70%;"  required="" />
-					<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"
-					 		    	target="window"/>
-
+				<td nowrap  style="width:60%">
+					<input  required="" readonly type="text" id="username" name="username"  class="form-control username" title="" value="${model.username!}"   required="" />
+					
 				</td>
+				<td style="width: 20%;">
+				    <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"
+                                    target="window"/>
+                </td>
 			</tr>
 			<tr>
 				<th><@locale code="userinfo.displayName" />:</th>
 				<td nowrap>
-					<input  required="" readonly  type="text" id="displayName" name="displayName"  class="form-control displayName"  title="" value="${model.displayName!}" style="width:70%;"  required="" />
+					<input  required="" readonly  type="text" id="displayName" name="displayName"  class="form-control displayName"  title="" value="${model.displayName!}"   required="" />
 			
 				</td>
+				<td>
+                </td>
 			</tr>
 			<tr>
 				<th><@locale code="apps.name" />:</th>
 				<td nowrap>
-					<input  required="" readonly  type="text" id="appName" name="appName"  class="form-control appName" title="" value="${model.appName!}"  style="float:left;width:70%;"  required="" />
-					<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"
-					 		    	target="window"/>
+					<input  required="" readonly  type="text" id="appName" name="appName"  class="form-control appName" title="" value="${model.appName!}"   required="" />
+					
 					
 				</td>
+				<td>
+				    <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"
+                                    target="window"/>
+                </td>
 			</tr>
 			<tr>
 				<th><@locale code="account.relatedUsername" />:</th>
 				<td nowrap>
-					<input type="text" id="relatedUsername" name="relatedUsername"  class="form-control" title="" value="${model.relatedUsername!}"  style="width:70%;"  required="" />
+					<input type="text" id="relatedUsername" name="relatedUsername"  class="form-control" title="" value="${model.relatedUsername!}"    required="" />
 
 				</td>
+				<td>
+                </td>
 			</tr>
 			<tr>
 				<th><@locale code="account.relatedPassword" />:</th>
 				<td nowrap>
-					<input type="password" id="relatedPassword" name="relatedPassword"  class="form-control" title="" value="${model.relatedPassword!}"  style="width:70%;"  required="" />
-		
-				</td>
+					<input type="password" id="relatedPassword" name="relatedPassword"  class="form-control" title="" value="${model.relatedPassword!}"  required="" />
+		        </td>
+				<td>
+				    <input id="generateSecret" type="button" class="button btn btn-warning mr-3" style="width:75px"  value="<@locale code="button.text.generate"/>"/>
+                    <input id="view" type="button" class="button btn btn-info mr-3" style="width:75px"  value="<@locale code="button.text.view"/>"/>
+                
+                </td>
 			</tr>
 			<tr>
-				<td colspan="2"  class="center">
+				<td colspan="3"  class="center">
 					<input id="_method" type="hidden" name="_method"  value="post"/>
 					<input id="status" type="hidden" name="status"  value="1"/>
 					<input type="hidden" id="userId" name="userId" class="userId" title="" value="${model.userId!}"/>

+ 12 - 5
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/appAccountsList.ftl → maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accounts/accountsList.ftl

@@ -54,13 +54,13 @@
 				<td colspan="2"> 
 					 <div id="tool_box_right">	    
 						 <input class="button btn btn-success mr-3" id="addBtn" type="button" value="<@locale code="button.text.add"/>" 
-						 		    wurl="<@base/>/app/accounts/forwardAdd"
+						 		    wurl="<@base/>/accounts/forwardAdd"
 						 		    wwidth="960"
 						 		    wheight="600"
 					 		    	target="window">	   
 					 		    	
 					 	<input class="button btn btn-danger mr-3 "  id="deleteBtn" type="button" value="<@locale code="button.text.delete"/>"
-					 				wurl="<@base/>/app/accounts/delete" />
+					 				wurl="<@base/>/accounts/delete" />
 					</div>
 				</td>
 			</tr>
@@ -73,9 +73,16 @@
  		<form id="advanced_search_form">
  			<table    class="table table-bordered">
 	 			<tr>
-	 				<td width="120px"><@locale code="account.displayName"/></td>
+	 				<td width="120px"><@locale code="apps.name"/></td>
 		 			<td width="360px">
-		 				<input  class="form-control"  name="displayName" type="text" >
+		 				<input class="form-control d-none appId" id="appId" name="appId" value="" type="text"  >
+                        <input class="form-control d-none" id="parentId" name="parentId" value="" type="text"  >
+                        <input class="form-control appName"    style="width:200px;float: left;" value=""    id="appName" name="appName" type="text" >
+                        <input class="button btn btn-success mr-3 window" style="float: left;" id="selectBtn" type="button" value="<@locale code="button.text.select"/>" 
+                                wurl="<@base/>/apps/select"
+                                wwidth="700"
+                                wheight="500"
+                                target="window">
 		 			</td>
 		 			<td width="120px"><@locale code="account.relatedUsername"/></td>
 		 			<td width="360px">
@@ -86,7 +93,7 @@
 			</table>
  		</form>
  	</div>
- 	<table  data-url="<@base/>/app/accounts/grid"
+ 	<table  data-url="<@base/>/accounts/grid"
 			id="datagrid"
 			data-toggle="table"
 			data-classes="table table-bordered table-hover table-striped"

+ 222 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accountsstrategy/accountsStrategyAdd.ftl

@@ -0,0 +1,222 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<#include  "../layout/header.ftl"/>
+	<#include  "../layout/common.cssjs.ftl"/>
+<style   type="text/css">
+  .table th, .table td {
+    padding: .2rem;
+    vertical-align: middle;
+  }
+</style>
+<script type="text/javascript">
+function onClick (event, treeId, treeNode) {
+	var zTree = $.fn.zTree.getZTreeObj("orgsTree");
+	nodes = zTree.getCheckedNodes(true);
+	var orgsName = "";
+	var orgsId = "";
+	for (var i=0, l=nodes.length; i<l; i++) {
+		orgsName += nodes[i].name + ",";
+		orgsId += nodes[i].id + ",";
+	} 
+	
+	$("#orgIdsListName").val(orgsName);
+	$("#orgIdsList").val(orgsId);
+}
+
+$(function () {
+
+		var treeSettings={
+			element  :  "orgsTree",
+			rootId  :  "1",
+		 	checkbox  :  true,
+		 	onClick  :  onClick,
+		 	onDblClick  :  null,
+		 	url  :  "<@base/>/orgs/tree"
+		};
+			
+		function singlePath(newNode) {
+			if (newNode === curExpandNode) return;
+			if (curExpandNode && curExpandNode.open==true) {
+				var zTree = $.fn.zTree.getZTreeObj(treeSettings.element);
+				if (newNode.parentTId === curExpandNode.parentTId) {
+					zTree.expandNode(curExpandNode, false);
+				} else {
+					var newParents = [];
+					while (newNode) {
+						newNode = newNode.getParentNode();
+						if (newNode === curExpandNode) {
+							newParents = null;
+							break;
+						} else if (newNode) {
+							newParents.push(newNode);
+						}
+					}
+					if (newParents!=null) {
+						var oldNode = curExpandNode;
+						var oldParents = [];
+						while (oldNode) {
+							oldNode = oldNode.getParentNode();
+							if (oldNode) {
+								oldParents.push(oldNode);
+							}
+						}
+						if (newParents.length>0) {
+							for (var i = Math.min(newParents.length, oldParents.length)-1; i>=0; i--) {
+								if (newParents[i] !== oldParents[i]) {
+									zTree.expandNode(oldParents[i], false);
+									break;
+								}
+							}
+						} else {
+							zTree.expandNode(oldParents[oldParents.length-1], false);
+						}
+					}
+				}
+			}
+			curExpandNode = newNode;
+		};
+
+
+		function beforeExpand(treeId, treeNode) {
+			var pNode = curExpandNode ? curExpandNode.getParentNode():null;
+			var treeNodeP = treeNode.parentTId ? treeNode.getParentNode():null;
+			var zTree = $.fn.zTree.getZTreeObj(""+treeSettings.element);
+			for(var i=0, l=!treeNodeP ? 0:treeNodeP.children.length; i<l; i++ ) {
+				if (treeNode !== treeNodeP.children[i]) {
+					zTree.expandNode(treeNodeP.children[i], false);
+				}
+			}
+			while (pNode) {
+				if (pNode === treeNode) {
+					break;
+				}
+				pNode = pNode.getParentNode();
+			}
+			if (!pNode) {
+				singlePath(treeNode);
+			}
+
+		};
+		
+	    $.fn.zTree.init(
+	    		$("#"+treeSettings.element), //element
+	    		{//json object 
+					check	: 	{
+						enable		: 	treeSettings.checkbox
+					},
+					async	: 	{
+						enable		: 	true,
+						url			:	treeSettings.url,
+						autoParam	:	["id", "name=n", "level=lv"],
+						otherParam	:	{"otherParam":"zTreeAsyncTest",id:treeSettings.rootId},
+						dataFilter	: 	function (treeId, parentNode, childNodes) {
+											if (!childNodes) return null;
+											for (var i=0, l=childNodes.length; i<l; i++) {
+												childNodes[i].name = childNodes[i].name.replace(/\.n/g, '.');
+											}
+											return childNodes;
+										}
+					},
+					data			: 	{
+						simpleData	: 	{
+							enable	: 	true
+						}
+					},
+					callback: {
+						onClick			: 	treeSettings.onClick,
+						onDblClick		: 	treeSettings.onDblClick,
+						beforeAsync		: 	function(treeId, treeNode){
+							$.loading();
+						},
+						onAsyncSuccess	: 	function(event, treeId, treeNode, msg){
+							$.unloading();
+						},
+						//beforeExpand	: 	beforeExpand,
+						onExpand		: 	function onExpand(event, treeId, treeNode) {
+							curExpandNode = treeNode;
+						}
+					}
+	    		}
+	    	);//end tree
+	
+});
+function onBodyDown(event) {
+	if (!(event.target.id == "menuBtn" || event.target.id == "orgIdsListName" || event.target.id == "orgContent" || $(event.target).parents("#orgContent").length>0)) {
+		$("#orgContent").fadeOut("fast");
+		$("body").unbind("mousedown", onBodyDown);
+	}
+}
+		
+function showOrgsTree() {
+	var treeObj = $("#orgIdsListName");
+	var treeOffset = $("#orgIdsListName").offset();
+	$("#orgContent").css({left:treeOffset.left + "px", top:treeOffset.top + treeObj.outerHeight() + "px"}).slideDown("fast");
+
+	$("body").bind("mousedown", onBodyDown);
+}
+
+
+</script>
+</head>
+<body>
+<form id="actionForm"  method="post" type="label" autoclose="true"  action="<@base/>/roles/add"  class="needs-validation" novalidate>
+	<table border="0" cellpadding="0" cellspacing="0" class="table table-bordered" >
+		<tbody>
+			<tr>
+				<th><@locale code="role.id" />:</th>
+				<td nowrap>
+					<input type="text" id="id" name="id" class="form-control" title="" value=""  />
+				</td>
+			</tr>
+			<tr>
+				<th><@locale code="role.name" />:</th>
+				<td nowrap>
+					<input type="text" id="name" name="name" class="form-control" title="" value=""  required="" />
+				</td>
+			</tr>
+			<tr>
+				<th><@locale code="role.dynamic" />:</th>
+				<td nowrap>
+					<select id="dynamic" name="dynamic"  class="form-control  form-select">
+						<option value="0" selected ><@locale code="common.text.no" /></option>
+						<option value="1"          ><@locale code="common.text.yes" /></option>
+					</select>
+				</td>
+			</tr>
+			<tr>
+				<th><@locale code="role.orgidslist" />:</th>
+				<td nowrap>
+					<input type="text" id="orgIdsListName" name="orgIdsListName"   readonly  class="form-control" title="" value=""   onclick="showOrgsTree();"/>
+					<input type="hidden" id="orgIdsList" name="orgIdsList"   readonly  class="form-control" title="" value=""   />
+				</td>
+			</tr>
+			<tr>
+				<th><@locale code="role.filters" />:</th>
+				<td nowrap>
+					<textarea id="filters" name="filters" class="form-control"  rows="7" cols="20"></textarea>
+				</td>
+			</tr>
+			<tr>
+                <th><@locale code="common.text.description" />:</th>
+                <td nowrap>
+                    <textarea id="description" name="description" class="form-control"  rows="6" cols="20"></textarea>
+                </td>
+            </tr>
+			
+			<tr>
+				<td nowrap colspan="2" class="center">
+					<input id="_method" type="hidden" name="_method"  value="post"/>
+					<input  id="status" type="hidden" name="status"  value="1"/>
+		    		<input class="button btn btn-primary mr-3"  id="submitBtn" type="submit" value="<@locale code="button.text.save" />">
+	  				<input class="button btn btn-secondary mr-3"  id="closeBtn"   type="button" value="<@locale code="button.text.cancel" />"> 
+				</td>
+			</tr>
+		</tbody>
+	</table>
+</form>
+<div id="orgContent" class="menuContent" style="display:none; position: absolute;">
+	<ul id="orgsTree" class="ztree" style="margin-top:0; width:180px; height: 300px;"></ul>
+</div>
+</body>
+</html>

+ 130 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accountsstrategy/accountsStrategyList.ftl

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

+ 235 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/accountsstrategy/accountsStrategyUpdate.ftl

@@ -0,0 +1,235 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<#include  "../layout/header.ftl"/>
+	<#include  "../layout/common.cssjs.ftl"/>
+<style   type="text/css">
+  .table th, .table td {
+    padding: .2rem;
+    vertical-align: middle;
+  }
+</style>
+<script type="text/javascript">
+function onClick (event, treeId, treeNode) {
+	var zTree = $.fn.zTree.getZTreeObj("orgsTree");
+	nodes = zTree.getCheckedNodes(true);
+	var orgsName = "";
+	var orgsId = "";
+	for (var i=0; i<nodes.length; i++) {
+		orgsName += nodes[i].name + ",";
+		orgsId += nodes[i].id + ",";
+	} 
+	
+	$("#orgIdsListName").val(orgsName);
+	$("#orgIdsList").val(orgsId);
+}
+
+$(function () {
+
+	var treeSettings={
+		element  :  "orgsTree",
+		rootId  :  "1",
+	 	checkbox  :  true,
+	 	onClick  :  onClick,
+	 	onDblClick  :  null,
+	 	url  :  "<@base/>/orgs/tree"
+	};
+		
+	function singlePath(newNode) {
+		if (newNode === curExpandNode) return;
+		if (curExpandNode && curExpandNode.open==true) {
+			var zTree = $.fn.zTree.getZTreeObj(treeSettings.element);
+			if (newNode.parentTId === curExpandNode.parentTId) {
+				zTree.expandNode(curExpandNode, false);
+			} else {
+				var newParents = [];
+				while (newNode) {
+					newNode = newNode.getParentNode();
+					if (newNode === curExpandNode) {
+						newParents = null;
+						break;
+					} else if (newNode) {
+						newParents.push(newNode);
+					}
+				}
+				if (newParents!=null) {
+					var oldNode = curExpandNode;
+					var oldParents = [];
+					while (oldNode) {
+						oldNode = oldNode.getParentNode();
+						if (oldNode) {
+							oldParents.push(oldNode);
+						}
+					}
+					if (newParents.length>0) {
+						for (var i = Math.min(newParents.length, oldParents.length)-1; i>=0; i--) {
+							if (newParents[i] !== oldParents[i]) {
+								zTree.expandNode(oldParents[i], false);
+								break;
+							}
+						}
+					} else {
+						zTree.expandNode(oldParents[oldParents.length-1], false);
+					}
+				}
+			}
+		}
+		curExpandNode = newNode;
+	};
+	
+	
+	function beforeExpand(treeId, treeNode) {
+		var pNode = curExpandNode ? curExpandNode.getParentNode():null;
+		var treeNodeP = treeNode.parentTId ? treeNode.getParentNode():null;
+		var zTree = $.fn.zTree.getZTreeObj(""+treeSettings.element);
+		for(var i=0, l=!treeNodeP ? 0:treeNodeP.children.length; i<l; i++ ) {
+			if (treeNode !== treeNodeP.children[i]) {
+				zTree.expandNode(treeNodeP.children[i], false);
+			}
+		}
+		while (pNode) {
+			if (pNode === treeNode) {
+				break;
+			}
+			pNode = pNode.getParentNode();
+		}
+		if (!pNode) {
+			singlePath(treeNode);
+		}
+	
+	};
+	function onLoadSuccessed(){
+		var zTree = $.fn.zTree.getZTreeObj("orgsTree");
+		var orgsIdValues = $("#orgIdsList").val().split(",") ;
+		var orgsName="";
+		for (var i=0; i<orgsIdValues.length; i++) {
+			var node = zTree.getNodeByParam("id",orgsIdValues[i] );
+			if(node != null){
+				zTree.checkNode(node, true, false);//将指定ID的节点选中
+				orgsName +=  node.name;
+			}
+		} 
+		$("#orgIdsListName").val(orgsName);
+	}
+	
+	$.fn.zTree.init(
+		$("#"+treeSettings.element), //element
+		{//json object 
+			check	: 	{
+				enable		: 	treeSettings.checkbox
+			},
+			async	: 	{
+				enable		: 	true,
+				url			:	treeSettings.url,
+				autoParam	:	["id", "name=n", "level=lv"],
+				otherParam	:	{"otherParam":"zTreeAsyncTest",id:treeSettings.rootId},
+				dataFilter	: 	function (treeId, parentNode, childNodes) {
+									if (!childNodes) return null;
+									for (var i=0, l=childNodes.length; i<l; i++) {
+										childNodes[i].name = childNodes[i].name.replace(/\.n/g, '.');
+									}
+									return childNodes;
+								}
+			},
+			data			: 	{
+				simpleData	: 	{
+					enable	: 	true
+				}
+			},
+			callback: {
+				onClick			: 	treeSettings.onClick,
+				onDblClick		: 	treeSettings.onDblClick,
+				beforeAsync		: 	function(treeId, treeNode){
+					$.loading();
+				},
+				onAsyncSuccess	: 	function(event, treeId, treeNode, msg){
+					$.unloading();
+					onLoadSuccessed();
+				},
+				//beforeExpand	: 	beforeExpand,
+				onExpand		: 	function onExpand(event, treeId, treeNode) {
+					curExpandNode = treeNode;
+				}
+			}
+		}
+	);//end tree
+	
+});
+function onBodyDown(event) {
+	if (!(event.target.id == "menuBtn" || event.target.id == "orgIdsListName" || event.target.id == "orgContent" || $(event.target).parents("#orgContent").length>0)) {
+		$("#orgContent").fadeOut("fast");
+		$("body").unbind("mousedown", onBodyDown);
+	}
+}
+		
+function showOrgsTree() {
+	var treeObj = $("#orgIdsListName");
+	var treeOffset = $("#orgIdsListName").offset();
+	$("#orgContent").css({left:treeOffset.left + "px", top:treeOffset.top + treeObj.outerHeight() + "px"}).slideDown("fast");
+
+	$("body").bind("mousedown", onBodyDown);
+}
+
+
+</script>
+</head>
+<body>
+<form id="actionForm"  method="post" type="label" autoclose="true"  action="<@base/>/roles/update"  class="needs-validation" novalidate>
+	 <table  border="0" cellpadding="0" cellspacing="0" class="table table-bordered">
+		<tbody>
+		<tr>
+			<th><@locale code="role.id" />:</th>
+			<td nowrap>
+				<input id="id" type="text" readonly name="id"  class="form-control"   value="${model.id}"/>
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="role.name" />:</th>
+			<td nowrap>
+				<input type="text" id="name" name="name" class="form-control" title="" value="${model.name!}"  required="" />
+			</td>
+		</tr>
+		<tr>
+			<th><@locale code="role.dynamic" />:</th>
+			<td nowrap>
+				<select id="dynamic" name="dynamic"  class="form-control  form-select">
+					<option value="0" <#if '0'==model.dynamic>selected</#if> ><@locale code="common.text.no" /></option>
+					<option value="1" <#if '1'==model.dynamic>selected</#if> ><@locale code="common.text.yes" /></option>
+				</select>
+			</td>
+		</tr>
+		<tr>
+				<th><@locale code="role.orgidslist" />:</th>
+				<td nowrap>
+					<input type="text" id="orgIdsListName" name="orgIdsListName"   readonly  class="form-control" title="" value=""   onclick="showOrgsTree();"/>
+					<input type="hidden" id="orgIdsList" name="orgIdsList"   readonly  class="form-control" title="" value="${model.orgIdsList!}"   />
+				</td>
+		</tr>
+		<tr>
+			<th><@locale code="role.filters" />:</th>
+			<td nowrap>
+				<textarea id="filters" name="filters" class="form-control"  rows="7" cols="20">${model.filters!}</textarea>
+			</td>
+		</tr>
+		<tr>
+                <th><@locale code="common.text.description" />:</th>
+                <td nowrap>
+                	<textarea id="description" name="description" class="form-control"  rows="6" cols="20">${model.description!}</textarea>
+                </td>
+            </tr>
+		<tr>
+			<td nowrap colspan="2"  class="center">
+				<input id="_method" type="hidden" name="_method"  value="post"/>
+				<input id="status" type="hidden" name="status"  value="1"/>
+	    		<input class="button btn btn-primary mr-3"  id="submitBtn" type="submit" value="<@locale code="button.text.save" />">
+  				<input class="button btn btn-secondary mr-3"  id="closeBtn"   type="button" value="<@locale code="button.text.cancel" />">	 
+			</td>
+		</tr>
+		</tbody>
+	  </table>
+</form>
+<div id="orgContent" class="menuContent" style="display:none; position: absolute;">
+	<ul id="orgsTree" class="ztree" style="margin-top:0; width:180px; height: 300px;"></ul>
+</div>
+</body>
+</html>

+ 8 - 5
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/groups/selectGroupsList.ftl

@@ -5,7 +5,9 @@
 	<#include  "../layout/common.cssjs.ftl"/>
     <link type="text/css" rel="stylesheet"  href="<@base />/static/css/minitable.css"/>
 <script type="text/javascript">	
-	
+    function dynamicFormatter(value, row, index){
+        return value=='0'? '<@locale code="common.text.no" />':'<@locale code="common.text.yes" />';
+    };
 	$(function () {
 		$("#selectBtn").on("click",function(){
 			var seldata=$.dataGridSelRowsData("#datagrid"); 
@@ -58,11 +60,12 @@
 				<th data-checkbox="true"></th>
 				<th data-sortable="true" data-field="id"   data-visible="false">Id</th>
 				<th data-field="name"><@locale code="group.name"/></th>
+				<th data-field="dynamic"  data-formatter="dynamicFormatter"><@locale code="group.dynamic"/></th>
 				<th data-field="description"><@locale code="common.text.description"/></th>
-				<th data-field="createdBy"><@locale code="common.text.createdby"/></th>
-				<th data-field="createdDate"><@locale code="common.text.createddate"/></th>
-				<th data-field="modifiedBy"><@locale code="common.text.modifiedby"/></th>
-				<th data-field="modifiedDate"><@locale code="common.text.modifieddate"/></th>
+				<th data-field="createdBy"    data-visible="false"><@locale code="common.text.createdby"/></th>
+				<th data-field="createdDate"  data-visible="false"><@locale code="common.text.createddate"/></th>
+				<th data-field="modifiedBy"   data-visible="false"><@locale code="common.text.modifiedby"/></th>
+				<th data-field="modifiedDate" data-visible="false"><@locale code="common.text.modifieddate"/></th>
 	
 			</tr>
 		</thead>

+ 8 - 1
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/sidenav.ftl

@@ -21,7 +21,7 @@
      	</a>
    	</li>
  	<li>
-     	<a class="side-nav-menu" href="<@base />/app/accounts/list/">
+     	<a class="side-nav-menu" href="<@base />/accounts/list/">
       		
        		<@locale code="navs.accounts"/>
        		<span class="fa fa-fw fa-vcard  fa-lg"></span>
@@ -99,6 +99,13 @@
                     <span class="fa fa-fw fa-globe fa-lg"></span>
                 </a>
             </li>
+            <li>
+                <a class="side-nav-menu" href="<@base />/accountsstrategy/list/">
+                    <@locale code="navs.accounts.strategy"/>
+                    <span class="fa fa-fw fa-globe fa-lg"></span>
+                </a>
+            </li>
+            
            <li>
              <a class="side-nav-menu" href="<@base />/synchronizers/list/">
                 <@locale code="navs.synchronizers"/>  

+ 8 - 5
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/roles/selectRolesList.ftl

@@ -5,7 +5,9 @@
 	<#include  "../layout/common.cssjs.ftl"/>
     <link type="text/css" rel="stylesheet"  href="<@base />/static/css/minitable.css"/>
 <script type="text/javascript">	
-	
+	function dynamicFormatter(value, row, index){
+        return value=='0'? '<@locale code="common.text.no" />':'<@locale code="common.text.yes" />';
+    };
 	$(function () {
 		$("#selectBtn").on("click",function(){
 			var seldata=$.dataGridSelRowsData("#datagrid"); 
@@ -58,11 +60,12 @@
 				<th data-checkbox="true"></th>
 				<th data-sortable="true" data-field="id"   data-visible="false">Id</th>
 				<th data-field="name"><@locale code="role.name"/></th>
+				<th data-field="dynamic"  data-formatter="dynamicFormatter"><@locale code="role.dynamic"/></th>
 				<th data-field="description"><@locale code="common.text.description"/></th>
-				<th data-field="createdBy"><@locale code="common.text.createdby"/></th>
-				<th data-field="createdDate"><@locale code="common.text.createddate"/></th>
-				<th data-field="modifiedBy"><@locale code="common.text.modifiedby"/></th>
-				<th data-field="modifiedDate"><@locale code="common.text.modifieddate"/></th>
+				<th data-field="createdBy"    data-visible="false"><@locale code="common.text.createdby"/></th>
+				<th data-field="createdDate"  data-visible="false"><@locale code="common.text.createddate"/></th>
+				<th data-field="modifiedBy"   data-visible="false"><@locale code="common.text.modifiedby"/></th>
+				<th data-field="modifiedDate" data-visible="false"><@locale code="common.text.modifieddate"/></th>
 	
 			</tr>
 		</thead>