浏览代码

new version provision

MaxKey 2 年之前
父节点
当前提交
8e5fc93d13
共有 43 个文件被更改,包括 1676 次插入390 次删除
  1. 8 0
      maxkey-common/src/main/java/org/maxkey/util/AuthorizationHeaderUtils.java
  2. 15 15
      maxkey-core/src/main/java/org/maxkey/configuration/ApplicationConfig.java
  3. 210 0
      maxkey-core/src/main/java/org/maxkey/entity/Connectors.java
  4. 119 149
      maxkey-core/src/main/java/org/maxkey/entity/HistoryConnector.java
  5. 64 1
      maxkey-core/src/main/java/org/maxkey/web/HttpRequestAdapter.java
  6. 33 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/mapper/ConnectorsMapper.java
  7. 3 3
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/AccountsService.java
  8. 42 0
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/ConnectorsService.java
  9. 3 3
      maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java
  10. 10 1
      maxkey-persistence/src/main/java/org/maxkey/provision/ProvisionMessage.java
  11. 2 2
      maxkey-persistence/src/main/java/org/maxkey/provision/ProvisionService.java
  12. 205 0
      maxkey-persistence/src/main/java/org/maxkey/provision/thread/ProvisioningRunner.java
  13. 23 0
      maxkey-persistence/src/main/java/org/maxkey/provision/thread/ProvisioningRunnerThread.java
  14. 20 3
      maxkey-persistence/src/main/java/org/maxkey/provision/thread/ProvisioningThread.java
  15. 25 0
      maxkey-persistence/src/main/resources/org/maxkey/persistence/mapper/xml/mysql/ConnectorsMapper.xml
  16. 0 4
      maxkey-persistence/src/main/resources/org/maxkey/persistence/mapper/xml/mysql/HistoryConnectorMapper.xml
  17. 66 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/entity/Connectors.ts
  18. 27 59
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-connector/audit-connector.component.html
  19. 7 8
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-connector/audit-connector.component.ts
  20. 21 51
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-logins/audit-logins.component.html
  21. 23 53
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-synchronizer/audit-synchronizer.component.html
  22. 7 8
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-synchronizer/audit-synchronizer.component.ts
  23. 12 5
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/config.module.ts
  24. 81 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.html
  25. 0 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.less
  26. 25 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.spec.ts
  27. 91 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.ts
  28. 75 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.html
  29. 0 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.less
  30. 25 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.spec.ts
  31. 203 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.ts
  32. 33 0
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/service/connectors.service.ts
  33. 3 4
      maxkey-web-frontend/maxkey-web-mgt-app/src/app/service/synchronizers.service.ts
  34. 21 7
      maxkey-web-frontend/maxkey-web-mgt-app/src/assets/app-data.json
  35. 12 1
      maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/en-US.json
  36. 12 1
      maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-CN.json
  37. 12 1
      maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-TW.json
  38. 3 3
      maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java
  39. 2 2
      maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties
  40. 22 0
      maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtListenerConfig.java
  41. 4 4
      maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtMvcConfig.java
  42. 105 0
      maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/config/contorller/ConnectorsController.java
  43. 2 2
      maxkey-webs/maxkey-web-mgt/src/main/resources/application-http.properties

+ 8 - 0
maxkey-common/src/main/java/org/maxkey/util/AuthorizationHeaderUtils.java

@@ -17,6 +17,8 @@
 
 package org.maxkey.util;
 
+import java.util.HashMap;
+
 import javax.servlet.http.HttpServletRequest;
 
 import org.maxkey.crypto.Base64Utils;
@@ -89,5 +91,11 @@ public class AuthorizationHeaderUtils {
     	}
     	return null;
     }
+    
+    public static HashMap<String,String> authorization(String authorization) {
+    	HashMap<String,String> authorizationMap = new HashMap<String,String>();
+    	authorizationMap.put(HEADER_Authorization, authorization);
+    	return authorizationMap;
+    }
 
 }

+ 15 - 15
maxkey-core/src/main/java/org/maxkey/configuration/ApplicationConfig.java

@@ -67,8 +67,8 @@ public class ApplicationConfig {
     @Value("${server.servlet.session.timeout:1800}")
     private int sessionTimeout;
 
-    @Value("${maxkey.server.message.queue:none}")
-    private String messageQueue;
+    @Value("${maxkey.server.provision:false}")
+    private boolean provision;
     
     @Value("${maxkey.notices.visible:false}")
     private boolean noticesVisible;
@@ -192,21 +192,21 @@ public class ApplicationConfig {
         this.defaultUri = defaultUri;
     }
 
-    public String getMessageQueue() {
-		return messageQueue;
+    public boolean isProvision() {
+		return provision;
 	}
-    
-    public boolean isMessageQueueSupport() {
-    	if(StringUtils.isBlank(messageQueue)||messageQueue.equalsIgnoreCase("none")) {
-    		return false;
+
+	public void setProvision(boolean provision) {
+		this.provision = provision;
+	}
+
+	public boolean isProvisionSupport() {
+    	if(provision) {
+    		return true;
     	}
-		return true;
+		return false;
 	}
     
-	public void setMessageQueue(String messageQueue) {
-		this.messageQueue = messageQueue;
-	}
-
 	public String getMgtUri() {
 		return mgtUri;
 	}
@@ -262,8 +262,8 @@ public class ApplicationConfig {
         builder.append(mgtUri);
         builder.append(", port=");
         builder.append(port);
-        builder.append(", kafkaSupport=");
-        builder.append(messageQueue);
+        builder.append(", provision=");
+        builder.append(provision);
         builder.append(", maxKeyUri=");
         builder.append(authzUri);
         builder.append("]");

+ 210 - 0
maxkey-core/src/main/java/org/maxkey/entity/Connectors.java

@@ -0,0 +1,210 @@
+/*
+ * Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.maxkey.entity;
+
+import java.io.Serializable;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import org.apache.mybatis.jpa.persistence.JpaBaseEntity;
+import org.maxkey.pretty.impl.JsonPretty;
+
+@Entity
+@Table(name = "MXK_CONNECTORS")
+public class Connectors extends JpaBaseEntity implements Serializable {
+
+	private static final long serialVersionUID = 4660258495864814777L;
+	@Id
+	@Column
+	@GeneratedValue(strategy = GenerationType.AUTO, generator = "snowflakeid")
+	String id;
+	@Column
+	String connName;
+	@Column
+	String scheduler;
+	@Column
+	int justInTime;
+	@Column
+	String providerUrl;
+	@Column
+	String principal;
+	@Column
+	String credentials;
+	@Column
+	String filters;
+	@Column
+	String description;
+	@Column
+	String createdBy;
+	@Column
+	String createdDate;
+	@Column
+	String modifiedBy;
+	@Column
+	String modifiedDate;
+	@Column
+	String status;
+
+	@Column
+	private String instId;
+
+	private String instName;
+
+	public Connectors() {
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getConnName() {
+		return connName;
+	}
+
+	public void setConnName(String connName) {
+		this.connName = connName;
+	}
+
+	public int getJustInTime() {
+		return justInTime;
+	}
+
+	public void setJustInTime(int justInTime) {
+		this.justInTime = justInTime;
+	}
+
+	public String getScheduler() {
+		return scheduler;
+	}
+
+	public void setScheduler(String scheduler) {
+		this.scheduler = scheduler;
+	}
+
+	public String getProviderUrl() {
+		return providerUrl;
+	}
+
+	public void setProviderUrl(String providerUrl) {
+		this.providerUrl = providerUrl;
+	}
+
+	public String getPrincipal() {
+		return principal;
+	}
+
+	public void setPrincipal(String principal) {
+		this.principal = principal;
+	}
+
+	public String getCredentials() {
+		return credentials;
+	}
+
+	public void setCredentials(String credentials) {
+		this.credentials = credentials;
+	}
+
+	public String getFilters() {
+		return filters;
+	}
+
+	public void setFilters(String filters) {
+		this.filters = filters;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getCreatedBy() {
+		return createdBy;
+	}
+
+	public void setCreatedBy(String createdBy) {
+		this.createdBy = createdBy;
+	}
+
+	public String getCreatedDate() {
+		return createdDate;
+	}
+
+	public void setCreatedDate(String createdDate) {
+		this.createdDate = createdDate;
+	}
+
+	public String getModifiedBy() {
+		return modifiedBy;
+	}
+
+	public void setModifiedBy(String modifiedBy) {
+		this.modifiedBy = modifiedBy;
+	}
+
+	public String getModifiedDate() {
+		return modifiedDate;
+	}
+
+	public void setModifiedDate(String modifiedDate) {
+		this.modifiedDate = modifiedDate;
+	}
+
+	public String getStatus() {
+		return status;
+	}
+
+	public void setStatus(String status) {
+		this.status = status;
+	}
+
+	public String getInstId() {
+		return instId;
+	}
+
+	public void setInstId(String instId) {
+		this.instId = instId;
+	}
+
+	public String getInstName() {
+		return instName;
+	}
+
+	public void setInstName(String instName) {
+		this.instName = instName;
+	}
+
+	public Connectors(String id) {
+		this.id = id;
+	}
+
+	@Override
+	public String toString() {
+		return new JsonPretty().format(this);
+	}
+
+}

+ 119 - 149
maxkey-core/src/main/java/org/maxkey/entity/HistoryConnector.java

@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
- 
 
 package org.maxkey.entity;
 
@@ -29,209 +28,177 @@ import javax.persistence.Table;
 import org.apache.mybatis.jpa.persistence.JpaBaseEntity;
 
 @Entity
-@Table(name = "MXK_HISTORY_CONNECTOR")  
-public class HistoryConnector  extends JpaBaseEntity  implements Serializable{
-
-
-    /**
-     * 
-     */
-    private static final long serialVersionUID = 3465459057253994386L;
-    
-    @Id
-    @Column
-    @GeneratedValue(strategy=GenerationType.AUTO,generator="snowflakeid")
-    String id;
-    
-    @Column
-    String conName;
-    
-    @Column
-    String conType;
-    
-    @Column
-    String conAction;
-    
-    @Column
-    String sourceId;
-    
-    @Column
-    String sourceName;
-    
-    @Column
-    String objectId;
-    
-    @Column
-    String objectName;
-    
-    @Column
-    String description;
-    
-    
-    String syncTime;
-    
-    @Column
-    String result;
-    
-    String startDate;
-    
-    String endDate;
-    
-	@Column
-	private String instId;
-
-	private String instName;
-   
-    public String getId() {
-        return id;
-    }
-
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
+@Table(name = "MXK_HISTORY_CONNECTOR")
+public class HistoryConnector extends JpaBaseEntity implements Serializable {
 
-    public String getConName() {
-        return conName;
-    }
-
-
-    public void setConName(String conName) {
-        this.conName = conName;
-    }
-
-
-    public String getConType() {
-        return conType;
-    }
-
-
-    public void setConType(String conType) {
-        this.conType = conType;
-    }
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3465459057253994386L;
 
+	@Id
+	@Column
+	@GeneratedValue(strategy = GenerationType.AUTO, generator = "snowflakeid")
+	String id;
 
-    public String getSourceId() {
-        return sourceId;
-    }
+	@Column
+	String conName;
 
+	@Column
+	String topic;
 
-    public void setSourceId(String sourceId) {
-        this.sourceId = sourceId;
-    }
+	@Column
+	String actionType;
 
+	@Column
+	String sourceId;
 
-    public String getSourceName() {
-        return sourceName;
-    }
+	@Column
+	String sourceName;
 
+	@Column
+	String objectId;
 
-    public void setSourceName(String sourceName) {
-        this.sourceName = sourceName;
-    }
+	@Column
+	String objectName;
 
+	@Column
+	String description;
 
-    public String getObjectId() {
-        return objectId;
-    }
+	String syncTime;
 
+	@Column
+	String result;
 
-    public void setObjectId(String objectId) {
-        this.objectId = objectId;
-    }
+	String startDate;
 
+	String endDate;
 
-    public String getObjectName() {
-        return objectName;
-    }
+	@Column
+	private String instId;
 
+	private String instName;
 
-    public void setObjectName(String objectName) {
-        this.objectName = objectName;
-    }
+	public String getId() {
+		return id;
+	}
 
+	public void setId(String id) {
+		this.id = id;
+	}
 
-    public String getDescription() {
-        return description;
-    }
+	public String getConName() {
+		return conName;
+	}
 
+	public void setConName(String conName) {
+		this.conName = conName;
+	}
 
-    public void setDescription(String description) {
-        this.description = description;
-    }
+	public String getSourceId() {
+		return sourceId;
+	}
 
+	public void setSourceId(String sourceId) {
+		this.sourceId = sourceId;
+	}
 
-    public String getSyncTime() {
-        return syncTime;
-    }
+	public String getSourceName() {
+		return sourceName;
+	}
 
+	public void setSourceName(String sourceName) {
+		this.sourceName = sourceName;
+	}
 
-    public void setSyncTime(String syncTime) {
-        this.syncTime = syncTime;
-    }
+	public String getObjectId() {
+		return objectId;
+	}
 
+	public void setObjectId(String objectId) {
+		this.objectId = objectId;
+	}
 
-    public String getResult() {
-        return result;
-    }
+	public String getObjectName() {
+		return objectName;
+	}
 
+	public void setObjectName(String objectName) {
+		this.objectName = objectName;
+	}
 
-    public void setResult(String result) {
-        this.result = result;
-    }
+	public String getDescription() {
+		return description;
+	}
 
+	public void setDescription(String description) {
+		this.description = description;
+	}
 
-    public String getStartDate() {
-        return startDate;
-    }
+	public String getSyncTime() {
+		return syncTime;
+	}
 
+	public void setSyncTime(String syncTime) {
+		this.syncTime = syncTime;
+	}
 
-    public void setStartDate(String startDate) {
-        this.startDate = startDate;
-    }
+	public String getResult() {
+		return result;
+	}
 
+	public String getTopic() {
+		return topic;
+	}
 
-    public String getEndDate() {
-        return endDate;
-    }
+	public void setTopic(String topic) {
+		this.topic = topic;
+	}
 
+	public String getActionType() {
+		return actionType;
+	}
 
-    public void setEndDate(String endDate) {
-        this.endDate = endDate;
-    }
+	public void setActionType(String actionType) {
+		this.actionType = actionType;
+	}
 
+	public void setResult(String result) {
+		this.result = result;
+	}
 
-    public String getConAction() {
-		return conAction;
+	public String getStartDate() {
+		return startDate;
 	}
 
+	public void setStartDate(String startDate) {
+		this.startDate = startDate;
+	}
 
-	public void setConAction(String conAction) {
-		this.conAction = conAction;
+	public String getEndDate() {
+		return endDate;
 	}
 
+	public void setEndDate(String endDate) {
+		this.endDate = endDate;
+	}
 
 	public String getInstId() {
 		return instId;
 	}
 
-
 	public void setInstId(String instId) {
 		this.instId = instId;
 	}
 
-
 	public String getInstName() {
 		return instName;
 	}
 
-
 	public void setInstName(String instName) {
 		this.instName = instName;
 	}
 
-
 	@Override
 	public String toString() {
 		StringBuilder builder = new StringBuilder();
@@ -239,10 +206,10 @@ public class HistoryConnector  extends JpaBaseEntity  implements Serializable{
 		builder.append(id);
 		builder.append(", conName=");
 		builder.append(conName);
-		builder.append(", conType=");
-		builder.append(conType);
-		builder.append(", conAction=");
-		builder.append(conAction);
+		builder.append(", topic=");
+		builder.append(topic);
+		builder.append(", actionType=");
+		builder.append(actionType);
 		builder.append(", sourceId=");
 		builder.append(sourceId);
 		builder.append(", sourceName=");
@@ -261,9 +228,12 @@ public class HistoryConnector  extends JpaBaseEntity  implements Serializable{
 		builder.append(startDate);
 		builder.append(", endDate=");
 		builder.append(endDate);
+		builder.append(", instId=");
+		builder.append(instId);
+		builder.append(", instName=");
+		builder.append(instName);
 		builder.append("]");
 		return builder.toString();
 	}
-    
-    
+
 }

+ 64 - 1
maxkey-core/src/main/java/org/maxkey/web/HttpRequestAdapter.java

@@ -156,7 +156,70 @@ public class HttpRequestAdapter {
         }
         return null;
     }
-    
+ 
+    public String postJson(String url,String jsonString,HashMap<String,String> headers) {
+        // 创建httpClient实例
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        CloseableHttpResponse httpResponse = null;
+        // 创建httpPost远程连接实例
+        HttpPost httpPost = new HttpPost(url);
+        // 配置请求参数实例
+        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(35000)// 设置连接主机服务超时时间
+                .setConnectionRequestTimeout(35000)// 设置连接请求超时时间
+                .setSocketTimeout(60000)// 设置读取数据连接超时时间
+                .build();
+        // 为httpPost实例设置配置
+        httpPost.setConfig(requestConfig);
+        // 设置请求头
+        if (null != headers && headers.size() > 0) {
+        	  Set<Entry<String, String>> entrySet = headers.entrySet();
+              // 循环遍历,获取迭代器
+              Iterator<Entry<String, String>> iterator = entrySet.iterator();
+              while (iterator.hasNext()) {
+                  Entry<String, String> mapEntry = iterator.next();
+                  _logger.trace("Name " + mapEntry.getKey() + " , Value " +mapEntry.getValue());
+                  httpPost.addHeader(mapEntry.getKey(), mapEntry.getValue());
+              }
+        }
+        
+        // 封装post请求参数
+        StringEntity stringEntity =new StringEntity(jsonString, "UTF-8");
+        stringEntity.setContentType("application/json");
+        httpPost.setEntity(stringEntity);
+        
+        
+        try {
+            // httpClient对象执行post请求,并返回响应参数对象
+            httpResponse = httpClient.execute(httpPost);
+            // 从响应对象中获取响应内容
+            HttpEntity entity = httpResponse.getEntity();
+            String content = EntityUtils.toString(entity);
+            _logger.debug("Http Response StatusCode {} , Content {}",
+                    httpResponse.getStatusLine().getStatusCode(),
+                    content
+            );
+            return content;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            // 关闭资源
+            if (null != httpResponse) {
+                try {
+                    httpResponse.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (null != httpClient) {
+                try {
+                    httpClient.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return null;
+    }
     
 	public String get(String url) {
 		HashMap<String,String> headers = new HashMap<String,String>();

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

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

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

@@ -71,7 +71,7 @@ public class AccountsService  extends JpaBaseService<Accounts>{
 	
 	 public boolean insert(Accounts account) {
 	     if (super.insert(account)) {
-	            if(mqPersistService.getApplicationConfig().isMessageQueueSupport()) {
+	            if(mqPersistService.getApplicationConfig().isProvisionSupport()) {
 	                UserInfo loadUserInfo = userInfoService.findUserRelated(account.getUserId());
 	                account.setUserInfo(loadUserInfo);
 	                OrganizationsCast cast = new OrganizationsCast();
@@ -91,7 +91,7 @@ public class AccountsService  extends JpaBaseService<Accounts>{
 	 
    public boolean update(Accounts account) {
          if (super.update(account)) {
-        	 if(mqPersistService.getApplicationConfig().isMessageQueueSupport()) {
+        	 if(mqPersistService.getApplicationConfig().isProvisionSupport()) {
                     UserInfo loadUserInfo = userInfoService.findUserRelated(account.getUserId());
                     account.setUserInfo(loadUserInfo);
                     OrganizationsCast cast = new OrganizationsCast();
@@ -116,7 +116,7 @@ public class AccountsService  extends JpaBaseService<Accounts>{
        Accounts account = this.get(id);
        if (super.remove(id)) {
               UserInfo loadUserInfo = null;
-              if(mqPersistService.getApplicationConfig().isMessageQueueSupport()) {
+              if(mqPersistService.getApplicationConfig().isProvisionSupport()) {
                   loadUserInfo = userInfoService.findUserRelated(account.getUserId());
                   account.setUserInfo(loadUserInfo);
                   mqPersistService.send(

+ 42 - 0
maxkey-persistence/src/main/java/org/maxkey/persistence/service/ConnectorsService.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.Connectors;
+import org.maxkey.persistence.mapper.ConnectorsMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class ConnectorsService  extends JpaBaseService<Connectors>{
+    final static Logger _logger = LoggerFactory.getLogger(ConnectorsService.class);
+    
+	public ConnectorsService() {
+		super(ConnectorsMapper.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.connsec.db.service.BaseService#getMapper()
+	 */
+	@Override
+	public ConnectorsMapper getMapper() {
+		return (ConnectorsMapper)super.getMapper();
+	}
+}

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

@@ -75,7 +75,7 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
     public boolean insert(UserInfo userInfo) {
     	this.passwordEncoder(userInfo);
         if (super.insert(userInfo)) {
-        	if(messageQueueService.getApplicationConfig().isMessageQueueSupport()) {
+        	if(messageQueueService.getApplicationConfig().isProvisionSupport()) {
                 UserInfo loadUserInfo = findUserRelated(userInfo.getId());
                 messageQueueService.send(
                         ProvisionTopic.USERINFO_TOPIC, 
@@ -92,7 +92,7 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
     public boolean update(UserInfo userInfo) {
     	ChangePassword changePassword = this.passwordEncoder(userInfo);
         if (super.update(userInfo)) {
-        	if(messageQueueService.getApplicationConfig().isMessageQueueSupport()) {
+        	if(messageQueueService.getApplicationConfig().isProvisionSupport()) {
                 UserInfo loadUserInfo = findUserRelated(userInfo.getId());
                 accountUpdate(loadUserInfo);
                 messageQueueService.send(
@@ -110,7 +110,7 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
 	
 	public boolean delete(UserInfo userInfo) {
 	    UserInfo loadUserInfo = null;
-	    if(messageQueueService.getApplicationConfig().isMessageQueueSupport()) {
+	    if(messageQueueService.getApplicationConfig().isProvisionSupport()) {
 	        loadUserInfo = findUserRelated(userInfo.getId());
 	    }
 	    

+ 10 - 1
maxkey-persistence/src/main/java/org/maxkey/provision/ProvisionMessage.java

@@ -25,6 +25,7 @@ public class ProvisionMessage {
     String 	sendTime;
     String  content;
     int     connected;
+    int     instId;
     
     Object  sourceObject;
 
@@ -60,7 +61,7 @@ public class ProvisionMessage {
 		this.id = id;
 	}
 
-	public Object getContent() {
+	public String getContent() {
         return content;
     }
 
@@ -84,6 +85,14 @@ public class ProvisionMessage {
 		this.sourceObject = sourceObject;
 	}
 
+	public int getInstId() {
+		return instId;
+	}
+
+	public void setInstId(int instId) {
+		this.instId = instId;
+	}
+
 	public ProvisionMessage() {
     }
 

+ 2 - 2
maxkey-persistence/src/main/java/org/maxkey/provision/ProvisionService.java

@@ -46,7 +46,7 @@ public class ProvisionService {
      */
     public void send(String topic,Object content,String actionType) {
         //maxkey.server.message.queue , if not none
-        if(applicationConfig.isMessageQueueSupport()) {
+        if(applicationConfig.isProvisionSupport()) {
             ProvisionMessage message = 
             		new ProvisionMessage(
             				UUID.randomUUID().toString(),	//message id as uuid
@@ -58,7 +58,7 @@ public class ProvisionService {
             				);
             //sand msg to provision topic
             Thread thread = null;
-            if(applicationConfig.getMessageQueue().equalsIgnoreCase("provision")) {
+            if(applicationConfig.isProvisionSupport()) {
             	_logger.trace("message...");
             	thread = new  ProvisioningThread(jdbcTemplate,message);
             	thread.start();

+ 205 - 0
maxkey-persistence/src/main/java/org/maxkey/provision/thread/ProvisioningRunner.java

@@ -0,0 +1,205 @@
+package org.maxkey.provision.thread;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.mybatis.jpa.query.Query;
+import org.maxkey.crypto.password.PasswordReciprocal;
+import org.maxkey.entity.ChangePassword;
+import org.maxkey.entity.Connectors;
+import org.maxkey.entity.Message;
+import org.maxkey.entity.Organizations;
+import org.maxkey.entity.UserInfo;
+import org.maxkey.persistence.service.ConnectorsService;
+import org.maxkey.provision.ProvisionAction;
+import org.maxkey.provision.ProvisionMessage;
+import org.maxkey.provision.ProvisionTopic;
+import org.maxkey.util.AuthorizationHeaderUtils;
+import org.maxkey.util.DateUtils;
+import org.maxkey.util.JsonUtils;
+import org.maxkey.util.ObjectTransformer;
+import org.maxkey.web.HttpRequestAdapter;
+import org.maxkey.web.WebContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+
+public class ProvisioningRunner {
+	private static final Logger _logger = LoggerFactory.getLogger(ProvisioningRunner.class);
+	
+	static final String PROVISION_SELECT_STATEMENT = "select * from mxk_history_provisions where connected = 0 order by sendtime asc limit 500";
+	
+	static final String PROVISION_UPDATE_STATEMENT = "update mxk_history_provisions set connected = connected + 1  where  id = ?";
+	
+	static final String PROVISION_LOG_INSERT_STATEMENT = "insert into mxk_history_connector(id,conname,topic,actiontype,sourceid,sourcename,synctime,result,instid) values (? , ? , ? , ? , ? , ?  , ?  , ?  , ? )";
+	
+	
+    JdbcTemplate jdbcTemplate;
+    
+    ConnectorsService connectorsService;
+
+	public ProvisioningRunner(ConnectorsService connectorsService,JdbcTemplate jdbcTemplate) {
+		this.connectorsService = connectorsService;
+		this.jdbcTemplate = jdbcTemplate;
+	}
+    
+	
+	public void provisions() {
+		List<Connectors> listConnectors = connectorsService.query(new Query().eq("status", 1).eq("justintime", 1));
+		List<ProvisionMessage> listProvisionMessage = jdbcTemplate.query(PROVISION_SELECT_STATEMENT, new ProvisionMessageRowMapper());
+		for(ProvisionMessage msg : listProvisionMessage) {
+			for(Connectors connector: listConnectors) {
+				provision(msg,connector);
+			}
+		}
+	}
+	
+	public void provision(ProvisionMessage provisionMessage,Connectors connector) {
+		if(Integer.parseInt(connector.getInstId()) == provisionMessage.getInstId()) {
+			String url = connector.getProviderUrl();
+			if(!url.endsWith("/")) {
+				url = url + "/";
+			}
+			String resultMessage = "";
+			String objectId = "";
+			String objectName = "";
+			if(provisionMessage.getTopic().equalsIgnoreCase(ProvisionTopic.USERINFO_TOPIC)) {
+				UserInfo user = (UserInfo)ObjectTransformer.deserialize(provisionMessage.getContent());
+				user.setPassword(null);
+				user.setDecipherable(null);
+				objectId = user.getId();
+				objectName = user.getDisplayName()+"("+user.getUsername()+")";
+				resultMessage = provisionUser(user,url,provisionMessage.getActionType(),connector);
+				provisionLog(	connector.getConnName(),
+						"Users",
+						provisionMessage.getActionType(),
+						objectId,
+						objectName,
+						resultMessage,
+						provisionMessage.getInstId()
+				);
+			}else if(provisionMessage.getTopic().equalsIgnoreCase(ProvisionTopic.PASSWORD_TOPIC)) {
+				ChangePassword changePassword = (ChangePassword)ObjectTransformer.deserialize(provisionMessage.getContent());
+				objectId = changePassword.getUserId();
+				objectName = changePassword.getDisplayName()+"("+changePassword.getUsername()+")";
+				resultMessage = provisionChangePassword(changePassword,url,provisionMessage.getActionType(),connector);
+				provisionLog(	connector.getConnName(),
+						"Password",
+						provisionMessage.getActionType(),
+						objectId,
+						objectName,
+						resultMessage,
+						provisionMessage.getInstId()
+				);
+			}else if(provisionMessage.getTopic().equalsIgnoreCase(ProvisionTopic.ORG_TOPIC)) {
+				Organizations organization = (Organizations)ObjectTransformer.deserialize(provisionMessage.getContent());
+				objectId = organization.getId();
+				objectName = organization.getOrgName();
+				resultMessage = provisionOrganization(organization,url,provisionMessage.getActionType(),connector);
+				provisionLog(	connector.getConnName(),
+								"Organizations",
+								provisionMessage.getActionType(),
+								objectId,
+								objectName,
+								resultMessage,
+								provisionMessage.getInstId()
+						);
+			}
+			
+			jdbcTemplate.update(PROVISION_UPDATE_STATEMENT,provisionMessage.getId());
+		}
+	}
+	
+	public void provisionLog(String conName,String topic,String actionType,String sourceId,String sourceName,String resultMessage,int instid) {
+		Message<?> resultMsg = JsonUtils.json2Object(resultMessage, Message.class);
+		String result = "success";
+		if(resultMsg == null || resultMsg.getCode() != 0) {
+			result = "fail";
+		}
+		
+		jdbcTemplate.update(PROVISION_LOG_INSERT_STATEMENT, 
+				WebContext.genId(),
+				conName,
+				topic,
+				actionType.replace("_ACTION", "").toLowerCase(),
+				sourceId,
+				sourceName,
+				DateUtils.getCurrentDateTimeAsString(),
+				result,
+				instid
+				);
+	}
+	
+	public String getActionType(String actionType) {
+		if(actionType.equalsIgnoreCase(ProvisionAction.CREATE_ACTION)) {
+			return "create";
+		}else if(actionType.equalsIgnoreCase(ProvisionAction.UPDATE_ACTION)) {
+			return "update";
+		}else if(actionType.equalsIgnoreCase(ProvisionAction.DELETE_ACTION)) {
+			return "delete";
+		}
+		return "";
+	}
+	
+	String provisionUser(UserInfo user,String baseUrl,String actionType,Connectors connector){
+		baseUrl = baseUrl + "Users/" + getActionType(actionType);
+		_logger.debug("URL {} ", baseUrl);
+		HashMap<String,String> authorizationMap = AuthorizationHeaderUtils.authorization(
+				AuthorizationHeaderUtils.createBasic(
+						connector.getPrincipal(), 
+						PasswordReciprocal.getInstance().decoder(connector.getCredentials()))
+			);
+		
+		return new HttpRequestAdapter().postJson(	baseUrl, 
+											JsonUtils.gson2Json(user),
+											authorizationMap
+											);
+	}
+	
+	String provisionOrganization(Organizations organizations,String baseUrl,String actionType,Connectors connector){
+		baseUrl = baseUrl + "Organizations/"+ getActionType(actionType);
+		_logger.debug("URL {} ", baseUrl);
+		HashMap<String,String> authorizationMap = AuthorizationHeaderUtils.authorization(
+				AuthorizationHeaderUtils.createBasic(
+						connector.getPrincipal(), 
+						PasswordReciprocal.getInstance().decoder(connector.getCredentials()))
+			);
+		
+		return new HttpRequestAdapter().postJson(	baseUrl, 
+											JsonUtils.gson2Json(organizations),
+											authorizationMap
+											);
+	}
+	
+	String provisionChangePassword(ChangePassword changePassword,String baseUrl,String actionType,Connectors connector){
+		baseUrl = baseUrl + "Users/changePassword";
+		_logger.debug("URL {} ", baseUrl);
+		HashMap<String,String> authorizationMap = AuthorizationHeaderUtils.authorization(
+				AuthorizationHeaderUtils.createBasic(
+						connector.getPrincipal(), 
+						PasswordReciprocal.getInstance().decoder(connector.getCredentials()))
+			);
+		
+		return new HttpRequestAdapter().postJson(	baseUrl, 
+											JsonUtils.gson2Json(changePassword),
+											authorizationMap
+											);
+	}
+	
+    public class ProvisionMessageRowMapper implements RowMapper<ProvisionMessage> {
+        @Override
+        public ProvisionMessage mapRow(ResultSet rs, int rowNum) throws SQLException {
+        	ProvisionMessage msg = new ProvisionMessage();
+        	msg.setId(rs.getString("id"));
+        	msg.setActionType(rs.getString("actiontype"));
+        	msg.setTopic(rs.getString("topic"));
+        	msg.setContent(rs.getString("content"));
+        	msg.setConnected(rs.getInt("connected"));
+        	msg.setInstId(rs.getInt("instid"));
+            return msg;
+        }
+    }
+}

+ 23 - 0
maxkey-persistence/src/main/java/org/maxkey/provision/thread/ProvisioningRunnerThread.java

@@ -0,0 +1,23 @@
+package org.maxkey.provision.thread;
+
+public class ProvisioningRunnerThread  extends Thread{
+
+	ProvisioningRunner runner;
+
+	public ProvisioningRunnerThread(ProvisioningRunner runner) {
+		super();
+		this.runner = runner;
+	}
+
+	@Override
+	public void run() {
+		while(true) {
+			try {
+				Thread.sleep(60 * 1000);
+				runner.provisions();
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+}

+ 20 - 3
maxkey-persistence/src/main/java/org/maxkey/provision/thread/ProvisioningThread.java

@@ -21,6 +21,7 @@ import java.sql.Types;
 
 import org.maxkey.pretty.impl.JsonPretty;
 import org.maxkey.provision.ProvisionMessage;
+import org.maxkey.util.JsonUtils;
 import org.maxkey.util.ObjectTransformer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,7 +34,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
 public class ProvisioningThread extends Thread{
 	private static final Logger _logger = LoggerFactory.getLogger(ProvisioningThread.class);
     
-	static final String PROVISION_INSERT_STATEMENT = "insert into mxk_history_provisions(`id`,`topic`,`actiontype`,`content`,`sendtime`,`connected`) values (? , ? , ? , ? , ? , ? )";
+	static final String PROVISION_INSERT_STATEMENT = "insert into mxk_history_provisions(id,topic,actiontype,content,sendtime,connected,instid) values (? , ? , ? , ? , ? , ?  , ? )";
 	
 	JdbcTemplate jdbcTemplate;
     
@@ -49,15 +50,31 @@ public class ProvisioningThread extends Thread{
     public void run() {
     	_logger.debug("send message \n{}" ,new JsonPretty().jacksonFormat(msg.getSourceObject()));
     	msg.setContent(ObjectTransformer.serialize((Serializable)msg.getSourceObject()));
+    	Inst inst = JsonUtils.gson2Object(JsonUtils.gson2Json(msg.getSourceObject()), Inst.class);
     	jdbcTemplate.update(PROVISION_INSERT_STATEMENT,
                 new Object[] { 
                 		msg.getId(), msg.getTopic(), msg.getActionType(), msg.getContent(),
-                		msg.getSendTime(),msg.getConnected()
+                		msg.getSendTime(),msg.getConnected(),inst.getInstId()
                         },
                 new int[] { 
                         Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, 
-                        Types.TINYINT
+                        Types.TINYINT,Types.TINYINT
                         });
         _logger.debug("send to Message Queue finished .");
     }
+    
+    class Inst{
+    	
+    	int instId;
+
+		public int getInstId() {
+			return instId;
+		}
+
+		public void setInstId(int instId) {
+			this.instId = instId;
+		}
+
+		public Inst() {}
+    }
 }

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

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

+ 0 - 4
maxkey-persistence/src/main/resources/org/maxkey/persistence/mapper/xml/mysql/HistoryConnectorMapper.xml

@@ -11,10 +11,6 @@
             and conName  =   #{conName}
         </if> 
         
-		<if test="conType != null and conType != ''">
-            and conType  =   #{conType}
-        </if> 
-        
 		<if test="sourceId != null and sourceId != ''">
             and sourceId  =   #{sourceId}
         </if> 

+ 66 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/entity/Connectors.ts

@@ -0,0 +1,66 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.
+ */
+
+import format from 'date-fns/format';
+
+import { BaseEntity } from './BaseEntity';
+
+export class Connectors extends BaseEntity {
+    connName!: String;
+    justInTime!: Number;
+    filters!: String;
+    scheduler!: String;
+    providerUrl!: String;
+    principal!: String;
+    credentials!: String;
+
+    switch_justInTime: boolean = true;
+
+    constructor() {
+        super();
+        this.status = 1;
+        this.justInTime = 1;
+    }
+
+    override init(data: any): void {
+        Object.assign(this, data);
+        if (this.status == 1) {
+            this.switch_status = true;
+        } else {
+            this.switch_status = false;
+        }
+
+        if (this.justInTime == 1) {
+            this.switch_justInTime = true;
+        } else {
+            this.switch_justInTime = false;
+        }
+    }
+
+    override trans(): void {
+        if (this.switch_status) {
+            this.status = 1;
+        } else {
+            this.status = 0;
+        }
+
+        if (this.switch_justInTime) {
+            this.justInTime = 1;
+        } else {
+            this.justInTime = 0;
+        }
+    }
+}

+ 27 - 59
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-connector/audit-connector.component.html

@@ -5,23 +5,19 @@
     <div nz-row [nzGutter]="{ xs: 8, sm: 8, md: 8, lg: 24, xl: 48, xxl: 48 }">
       <div nz-col nzMd="8" nzSm="24">
         <nz-form-item>
-          <nz-form-label nzFor="username">{{ 'mxk.users.username' | i18n }}</nz-form-label>
+          <nz-form-label nzFor="conName">{{ 'mxk.history.connector.conName' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input nz-input [(ngModel)]="query.params.username" [ngModelOptions]="{ standalone: true }" name="username" id="username" />
+            <input nz-input [(ngModel)]="query.params.conName" [ngModelOptions]="{ standalone: true }" name="conName"
+              id="conName" />
           </nz-form-control>
         </nz-form-item>
       </div>
       <div nz-col nzMd="8" nzSm="24">
         <nz-form-item>
-          <nz-form-label nzFor="displayName">{{ 'mxk.users.displayName' | i18n }}</nz-form-label>
+          <nz-form-label nzFor="sourceName">{{ 'mxk.history.connector.sourceName' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input
-              nz-input
-              [(ngModel)]="query.params.displayName"
-              [ngModelOptions]="{ standalone: true }"
-              id="displayName"
-              name="displayName"
-            />
+            <input nz-input [(ngModel)]="query.params.sourceName" [ngModelOptions]="{ standalone: true }"
+              id="sourceName" name="sourceName" />
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -29,13 +25,8 @@
         <nz-form-item>
           <nz-form-label nzFor="employeeNumber">{{ 'mxk.users.employeeNumber' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input
-              nz-input
-              [(ngModel)]="query.params.employeeNumber"
-              [ngModelOptions]="{ standalone: true }"
-              id="employeeNumber"
-              name="employeeNumber"
-            />
+            <input nz-input [(ngModel)]="query.params.employeeNumber" [ngModelOptions]="{ standalone: true }"
+              id="employeeNumber" name="employeeNumber" />
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -43,14 +34,9 @@
         <nz-form-item>
           <nz-form-label nzFor="startDatePicker">{{ 'mxk.text.startDate' | i18n }}</nz-form-label>
           <nz-form-control>
-            <nz-date-picker
-              nzShowTime
-              nzFormat="yyyy-MM-dd HH:mm:ss"
-              [(ngModel)]="query.params.startDatePicker"
-              [ngModelOptions]="{ standalone: true }"
-              name="startDatePicker"
-              nzPlaceHolder="startDatePicker"
-            ></nz-date-picker>
+            <nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="query.params.startDatePicker"
+              [ngModelOptions]="{ standalone: true }" name="startDatePicker" nzPlaceHolder="startDatePicker">
+            </nz-date-picker>
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -58,54 +44,37 @@
         <nz-form-item>
           <nz-form-label nzFor="endDatePicker">{{ 'mxk.text.endDate' | i18n }}</nz-form-label>
           <nz-form-control>
-            <nz-date-picker
-              nzShowTime
-              nzFormat="yyyy-MM-dd HH:mm:ss"
-              [(ngModel)]="query.params.endDatePicker"
-              [ngModelOptions]="{ standalone: true }"
-              name="endDatePicker"
-              nzPlaceHolder="endDatePicker"
-            ></nz-date-picker>
+            <nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="query.params.endDatePicker"
+              [ngModelOptions]="{ standalone: true }" name="endDatePicker" nzPlaceHolder="endDatePicker">
+            </nz-date-picker>
           </nz-form-control>
         </nz-form-item>
       </div>
 
       <div nz-col [nzSpan]="query.expandForm ? 24 : 8" [class.text-right]="query.expandForm">
-        <button nz-button type="submit" [nzType]="'primary'" [nzLoading]="query.submitLoading">{{ 'mxk.text.query' | i18n }}</button>
+        <button nz-button type="submit" [nzType]="'primary'" [nzLoading]="query.submitLoading">{{ 'mxk.text.query' |
+          i18n }}</button>
         <button nz-button type="reset" (click)="onReset()" class="mx-sm">{{ 'mxk.text.reset' | i18n }}</button>
         <button nz-button (click)="query.expandForm = !query.expandForm" class="mx-sm">
-          {{ query.expandForm ? ('mxk.text.collapse' | i18n) : ('mxk.text.expand' | i18n) }}</button
-        >
+          {{ query.expandForm ? ('mxk.text.collapse' | i18n) : ('mxk.text.expand' | i18n) }}</button>
       </div>
     </div>
   </form>
 </nz-card>
 <nz-card>
-  <nz-table
-    #dynamicTable
-    nzTableLayout="auto"
-    nzSize="small"
-    nzShowSizeChanger
-    [nzBordered]="true"
-    [nzData]="query.results.rows"
-    [nzFrontPagination]="false"
-    [nzTotal]="query.results.records"
-    [nzPageSizeOptions]="query.params.pageSizeOptions"
-    [nzPageSize]="query.params.pageSize"
-    [nzPageIndex]="query.params.pageNumber"
-    [nzLoading]="this.query.tableLoading"
-    (nzQueryParams)="onQueryParamsChange($event)"
-    [nzScroll]="{ x: '100%', y: '100%' }"
-  >
+  <nz-table #dynamicTable nzTableLayout="auto" nzSize="small" nzShowSizeChanger [nzBordered]="true"
+    [nzData]="query.results.rows" [nzFrontPagination]="false" [nzTotal]="query.results.records"
+    [nzPageSizeOptions]="query.params.pageSizeOptions" [nzPageSize]="query.params.pageSize"
+    [nzPageIndex]="query.params.pageNumber" [nzLoading]="this.query.tableLoading"
+    (nzQueryParams)="onQueryParamsChange($event)">
     <thead>
       <tr>
         <th nzAlign="center">{{ 'mxk.history.connector.id' | i18n }}</th>
         <th nzAlign="center">{{ 'mxk.history.connector.conName' | i18n }}</th>
-        <th nzAlign="center">{{ 'mxk.history.connector.conType' | i18n }}</th>
+        <th nzAlign="center">{{ 'mxk.history.connector.topic' | i18n }}</th>
+        <th nzAlign="center">{{ 'mxk.history.connector.actionType' | i18n }}</th>
         <th nzAlign="center">{{ 'mxk.history.connector.sourceId' | i18n }}</th>
         <th nzAlign="center">{{ 'mxk.history.connector.sourceName' | i18n }}</th>
-        <th nzAlign="center">{{ 'mxk.history.connector.objectId' | i18n }}</th>
-        <th nzAlign="center">{{ 'mxk.history.connector.objectName' | i18n }}</th>
         <th nzAlign="center">{{ 'mxk.history.connector.syncTime' | i18n }}</th>
         <th nzAlign="center">{{ 'mxk.history.connector.result' | i18n }}</th>
       </tr>
@@ -116,14 +85,13 @@
           <span>{{ data.id }}</span>
         </td>
         <td nzAlign="left">{{ data.conName }}</td>
-        <td nzAlign="left">{{ data.conType }}</td>
+        <td nzAlign="left">{{ data.topic }}</td>
+        <td nzAlign="left">{{ data.actionType }}</td>
         <td nzAlign="left">{{ data.sourceId }}</td>
-        <td nzAlign="left">{{ data.syncName }}</td>
         <td nzAlign="left">{{ data.sourceName }}</td>
-        <td nzAlign="left">{{ data.objectName }}</td>
         <td nzAlign="left">{{ data.syncTime }}</td>
         <td nzAlign="left">{{ data.result }}</td>
       </tr>
     </tbody>
   </nz-table>
-</nz-card>
+</nz-card>

+ 7 - 8
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-connector/audit-connector.component.ts

@@ -1,19 +1,18 @@
 /*
  * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * 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.
  */
- 
 
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@@ -33,8 +32,8 @@ import { HistoryService } from '../../../service/history.service';
 export class AuditConnectorComponent implements OnInit {
   query: {
     params: {
-      username: String;
-      displayName: String;
+      conName: String;
+      sourceName: String;
       employeeNumber: String;
       startDate: String;
       endDate: String;
@@ -53,8 +52,8 @@ export class AuditConnectorComponent implements OnInit {
     tableLoading: boolean;
   } = {
       params: {
-        username: '',
-        displayName: '',
+        conName: '',
+        sourceName: '',
         employeeNumber: '',
         startDate: '',
         endDate: '',

+ 21 - 51
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-logins/audit-logins.component.html

@@ -7,7 +7,8 @@
         <nz-form-item>
           <nz-form-label nzFor="username">{{ 'mxk.users.username' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input nz-input [(ngModel)]="query.params.username" [ngModelOptions]="{ standalone: true }" name="username" id="username" />
+            <input nz-input [(ngModel)]="query.params.username" [ngModelOptions]="{ standalone: true }" name="username"
+              id="username" />
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -15,13 +16,8 @@
         <nz-form-item>
           <nz-form-label nzFor="displayName">{{ 'mxk.users.displayName' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input
-              nz-input
-              [(ngModel)]="query.params.displayName"
-              [ngModelOptions]="{ standalone: true }"
-              id="displayName"
-              name="displayName"
-            />
+            <input nz-input [(ngModel)]="query.params.displayName" [ngModelOptions]="{ standalone: true }"
+              id="displayName" name="displayName" />
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -29,13 +25,8 @@
         <nz-form-item>
           <nz-form-label nzFor="employeeNumber">{{ 'mxk.users.employeeNumber' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input
-              nz-input
-              [(ngModel)]="query.params.employeeNumber"
-              [ngModelOptions]="{ standalone: true }"
-              id="employeeNumber"
-              name="employeeNumber"
-            />
+            <input nz-input [(ngModel)]="query.params.employeeNumber" [ngModelOptions]="{ standalone: true }"
+              id="employeeNumber" name="employeeNumber" />
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -43,14 +34,9 @@
         <nz-form-item>
           <nz-form-label nzFor="startDatePicker">{{ 'mxk.text.startDate' | i18n }}</nz-form-label>
           <nz-form-control>
-            <nz-date-picker
-              nzShowTime
-              nzFormat="yyyy-MM-dd HH:mm:ss"
-              [(ngModel)]="query.params.startDatePicker"
-              [ngModelOptions]="{ standalone: true }"
-              name="startDatePicker"
-              nzPlaceHolder="startDatePicker"
-            ></nz-date-picker>
+            <nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="query.params.startDatePicker"
+              [ngModelOptions]="{ standalone: true }" name="startDatePicker" nzPlaceHolder="startDatePicker">
+            </nz-date-picker>
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -58,45 +44,29 @@
         <nz-form-item>
           <nz-form-label nzFor="endDatePicker">{{ 'mxk.text.endDate' | i18n }}</nz-form-label>
           <nz-form-control>
-            <nz-date-picker
-              nzShowTime
-              nzFormat="yyyy-MM-dd HH:mm:ss"
-              [(ngModel)]="query.params.endDatePicker"
-              [ngModelOptions]="{ standalone: true }"
-              name="endDatePicker"
-              nzPlaceHolder="endDatePicker"
-            ></nz-date-picker>
+            <nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="query.params.endDatePicker"
+              [ngModelOptions]="{ standalone: true }" name="endDatePicker" nzPlaceHolder="endDatePicker">
+            </nz-date-picker>
           </nz-form-control>
         </nz-form-item>
       </div>
 
       <div nz-col [nzSpan]="query.expandForm ? 24 : 8" [class.text-right]="query.expandForm">
-        <button nz-button type="submit" [nzType]="'primary'" [nzLoading]="query.submitLoading">{{ 'mxk.text.query' | i18n }}</button>
+        <button nz-button type="submit" [nzType]="'primary'" [nzLoading]="query.submitLoading">{{ 'mxk.text.query' |
+          i18n }}</button>
         <button nz-button type="reset" (click)="onReset()" class="mx-sm">{{ 'mxk.text.reset' | i18n }}</button>
         <button nz-button (click)="query.expandForm = !query.expandForm" class="mx-sm">
-          {{ query.expandForm ? ('mxk.text.collapse' | i18n) : ('mxk.text.expand' | i18n) }}</button
-        >
+          {{ query.expandForm ? ('mxk.text.collapse' | i18n) : ('mxk.text.expand' | i18n) }}</button>
       </div>
     </div>
   </form>
 </nz-card>
 <nz-card>
-  <nz-table
-    #dynamicTable
-    nzTableLayout="auto"
-    nzSize="small"
-    nzShowSizeChanger
-    [nzBordered]="true"
-    [nzData]="query.results.rows"
-    [nzFrontPagination]="false"
-    [nzTotal]="query.results.records"
-    [nzPageSizeOptions]="query.params.pageSizeOptions"
-    [nzPageSize]="query.params.pageSize"
-    [nzPageIndex]="query.params.pageNumber"
-    [nzLoading]="this.query.tableLoading"
-    (nzQueryParams)="onQueryParamsChange($event)"
-    [nzScroll]="{ x: '100%', y: '100%' }"
-  >
+  <nz-table #dynamicTable nzTableLayout="auto" nzSize="small" nzShowSizeChanger [nzBordered]="true"
+    [nzData]="query.results.rows" [nzFrontPagination]="false" [nzTotal]="query.results.records"
+    [nzPageSizeOptions]="query.params.pageSizeOptions" [nzPageSize]="query.params.pageSize"
+    [nzPageIndex]="query.params.pageNumber" [nzLoading]="this.query.tableLoading"
+    (nzQueryParams)="onQueryParamsChange($event)">
     <thead>
       <tr>
         <th nzAlign="center">{{ 'mxk.history.login.sessionId' | i18n }}</th>
@@ -128,4 +98,4 @@
       </tr>
     </tbody>
   </nz-table>
-</nz-card>
+</nz-card>

+ 23 - 53
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-synchronizer/audit-synchronizer.component.html

@@ -5,23 +5,19 @@
     <div nz-row [nzGutter]="{ xs: 8, sm: 8, md: 8, lg: 24, xl: 48, xxl: 48 }">
       <div nz-col nzMd="8" nzSm="24">
         <nz-form-item>
-          <nz-form-label nzFor="username">{{ 'mxk.users.username' | i18n }}</nz-form-label>
+          <nz-form-label nzFor="syncName">{{ 'mxk.history.synchronizer.syncName' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input nz-input [(ngModel)]="query.params.username" [ngModelOptions]="{ standalone: true }" name="username" id="username" />
+            <input nz-input [(ngModel)]="query.params.syncName" [ngModelOptions]="{ standalone: true }" name="syncName"
+              id="syncName" />
           </nz-form-control>
         </nz-form-item>
       </div>
       <div nz-col nzMd="8" nzSm="24">
         <nz-form-item>
-          <nz-form-label nzFor="displayName">{{ 'mxk.users.displayName' | i18n }}</nz-form-label>
+          <nz-form-label nzFor="objectName">{{ 'mxk.history.synchronizer.objectName' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input
-              nz-input
-              [(ngModel)]="query.params.displayName"
-              [ngModelOptions]="{ standalone: true }"
-              id="displayName"
-              name="displayName"
-            />
+            <input nz-input [(ngModel)]="query.params.objectName" [ngModelOptions]="{ standalone: true }"
+              id="objectName" name="objectName" />
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -29,13 +25,8 @@
         <nz-form-item>
           <nz-form-label nzFor="employeeNumber">{{ 'mxk.users.employeeNumber' | i18n }}</nz-form-label>
           <nz-form-control>
-            <input
-              nz-input
-              [(ngModel)]="query.params.employeeNumber"
-              [ngModelOptions]="{ standalone: true }"
-              id="employeeNumber"
-              name="employeeNumber"
-            />
+            <input nz-input [(ngModel)]="query.params.employeeNumber" [ngModelOptions]="{ standalone: true }"
+              id="employeeNumber" name="employeeNumber" />
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -43,14 +34,9 @@
         <nz-form-item>
           <nz-form-label nzFor="startDatePicker">{{ 'mxk.text.startDate' | i18n }}</nz-form-label>
           <nz-form-control>
-            <nz-date-picker
-              nzShowTime
-              nzFormat="yyyy-MM-dd HH:mm:ss"
-              [(ngModel)]="query.params.startDatePicker"
-              [ngModelOptions]="{ standalone: true }"
-              name="startDatePicker"
-              nzPlaceHolder="startDatePicker"
-            ></nz-date-picker>
+            <nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="query.params.startDatePicker"
+              [ngModelOptions]="{ standalone: true }" name="startDatePicker" nzPlaceHolder="startDatePicker">
+            </nz-date-picker>
           </nz-form-control>
         </nz-form-item>
       </div>
@@ -58,45 +44,29 @@
         <nz-form-item>
           <nz-form-label nzFor="endDatePicker">{{ 'mxk.text.endDate' | i18n }}</nz-form-label>
           <nz-form-control>
-            <nz-date-picker
-              nzShowTime
-              nzFormat="yyyy-MM-dd HH:mm:ss"
-              [(ngModel)]="query.params.endDatePicker"
-              [ngModelOptions]="{ standalone: true }"
-              name="endDatePicker"
-              nzPlaceHolder="endDatePicker"
-            ></nz-date-picker>
+            <nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="query.params.endDatePicker"
+              [ngModelOptions]="{ standalone: true }" name="endDatePicker" nzPlaceHolder="endDatePicker">
+            </nz-date-picker>
           </nz-form-control>
         </nz-form-item>
       </div>
 
       <div nz-col [nzSpan]="query.expandForm ? 24 : 8" [class.text-right]="query.expandForm">
-        <button nz-button type="submit" [nzType]="'primary'" [nzLoading]="query.submitLoading">{{ 'mxk.text.query' | i18n }}</button>
+        <button nz-button type="submit" [nzType]="'primary'" [nzLoading]="query.submitLoading">{{ 'mxk.text.query' |
+          i18n }}</button>
         <button nz-button type="reset" (click)="onReset()" class="mx-sm">{{ 'mxk.text.reset' | i18n }}</button>
         <button nz-button (click)="query.expandForm = !query.expandForm" class="mx-sm">
-          {{ query.expandForm ? ('mxk.text.collapse' | i18n) : ('mxk.text.expand' | i18n) }}</button
-        >
+          {{ query.expandForm ? ('mxk.text.collapse' | i18n) : ('mxk.text.expand' | i18n) }}</button>
       </div>
     </div>
   </form>
 </nz-card>
 <nz-card>
-  <nz-table
-    #dynamicTable
-    nzTableLayout="auto"
-    nzSize="small"
-    nzShowSizeChanger
-    [nzBordered]="true"
-    [nzData]="query.results.rows"
-    [nzFrontPagination]="false"
-    [nzTotal]="query.results.records"
-    [nzPageSizeOptions]="query.params.pageSizeOptions"
-    [nzPageSize]="query.params.pageSize"
-    [nzPageIndex]="query.params.pageNumber"
-    [nzLoading]="this.query.tableLoading"
-    (nzQueryParams)="onQueryParamsChange($event)"
-    [nzScroll]="{ x: '100%', y: '100%' }"
-  >
+  <nz-table #dynamicTable nzTableLayout="auto" nzSize="small" nzShowSizeChanger [nzBordered]="true"
+    [nzData]="query.results.rows" [nzFrontPagination]="false" [nzTotal]="query.results.records"
+    [nzPageSizeOptions]="query.params.pageSizeOptions" [nzPageSize]="query.params.pageSize"
+    [nzPageIndex]="query.params.pageNumber" [nzLoading]="this.query.tableLoading"
+    (nzQueryParams)="onQueryParamsChange($event)">
     <thead>
       <tr>
         <th nzAlign="center">{{ 'mxk.history.synchronizer.syncId' | i18n }}</th>
@@ -122,4 +92,4 @@
       </tr>
     </tbody>
   </nz-table>
-</nz-card>
+</nz-card>

+ 7 - 8
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/audit/audit-synchronizer/audit-synchronizer.component.ts

@@ -1,19 +1,18 @@
 /*
  * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * 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.
  */
- 
 
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@@ -33,8 +32,8 @@ import { HistoryService } from '../../../service/history.service';
 export class AuditSynchronizerComponent implements OnInit {
   query: {
     params: {
-      username: String;
-      displayName: String;
+      syncName: String;
+      objectName: String;
       employeeNumber: String;
       startDate: String;
       endDate: String;
@@ -53,8 +52,8 @@ export class AuditSynchronizerComponent implements OnInit {
     tableLoading: boolean;
   } = {
       params: {
-        username: '',
-        displayName: '',
+        syncName: '',
+        objectName: '',
         employeeNumber: '',
         startDate: '',
         endDate: '',

+ 12 - 5
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/config.module.ts

@@ -1,19 +1,18 @@
 /*
  * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * 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.
  */
- 
 
 import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
@@ -34,6 +33,8 @@ import { SelectAccountsStrategyComponent } from './accounts-strategy/select-acco
 import { AdapterEditerComponent } from './adapters/adapter-editer/adapter-editer.component';
 import { AdaptersComponent } from './adapters/adapters.component';
 import { SelectAdaptersComponent } from './adapters/select-adapters/select-adapters.component';
+import { ConnectorEditerComponent } from './connectors/connector-editer/connector-editer.component';
+import { ConnectorsComponent } from './connectors/connectors.component';
 import { EmailSendersComponent } from './email-senders/email-senders.component';
 import { InstitutionsComponent } from './institutions/institutions.component';
 import { LdapContextComponent } from './ldap-context/ldap-context.component';
@@ -75,6 +76,10 @@ const routes: Routes = [
     component: SynchronizersComponent
   },
   {
+    path: 'connectors',
+    component: ConnectorsComponent
+  },
+  {
     path: 'accountsstrategys',
     component: AccountsStrategyComponent
   },
@@ -108,7 +113,9 @@ const COMPONENTS = [PasswordPolicyComponent, EmailSendersComponent, LdapContextC
     InstitutionsComponent,
     NoticesComponent,
     SelectAccountsStrategyComponent,
-    SelectAdaptersComponent
+    SelectAdaptersComponent,
+    ConnectorsComponent,
+    ConnectorEditerComponent
   ],
   imports: [SharedModule, CommonModule, RouterModule.forChild(routes)],
   exports: [RouterModule]

+ 81 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.html

@@ -0,0 +1,81 @@
+<div *nzModalTitle> {{ isEdit ? ('mxk.text.edit' | i18n) : ('mxk.text.add' | i18n) }} </div>
+<div>
+    <form nz-form [formGroup]="formGroup" (ngSubmit)="onSubmit($event)" se-container="1">
+        <nz-form-item>
+            <nz-form-label [nzMd]="6" nzFor="id">{{ 'mxk.text.id' | i18n }}</nz-form-label>
+            <nz-form-control [nzMd]="18" nzErrorTip="The input is not valid id!">
+                <input [(ngModel)]="form.model.id" disabled="{{ isEdit }}" [ngModelOptions]="{ standalone: true }"
+                    nz-input name="id" id="id" />
+            </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+            <nz-form-label [nzMd]="6" nzFor="id">{{ 'mxk.connectors.connName' | i18n }}</nz-form-label>
+            <nz-form-control [nzMd]="18" nzErrorTip="The input is not valid connName!">
+                <input [(ngModel)]="form.model.connName" [ngModelOptions]="{ standalone: true }" nz-input
+                    name="connName" id="connName" />
+            </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+            <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="justInTime">{{ 'mxk.connectors.justInTime' | i18n }}
+            </nz-form-label>
+            <nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid justInTime!">
+                <nz-switch [(ngModel)]="form.model.switch_justInTime" [ngModelOptions]="{ standalone: true }"
+                    name="justInTime" [nzCheckedChildren]="checkedTemplateJustInTime"
+                    [nzUnCheckedChildren]="unCheckedTemplateJustInTime">
+                </nz-switch>
+                <ng-template #checkedTemplateJustInTime><i nz-icon nzType="check"></i></ng-template>
+                <ng-template #unCheckedTemplateJustInTime><i nz-icon nzType="close"></i></ng-template>
+            </nz-form-control>
+        </nz-form-item>
+        <nz-form-item *ngIf="!form.model.switch_justInTime">
+            <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="scheduler">{{ 'mxk.connectors.scheduler' | i18n }}
+            </nz-form-label>
+            <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not valid icon!">
+                <input [(ngModel)]="form.model.scheduler" [ngModelOptions]="{ standalone: true }" nz-input
+                    name="scheduler" placeholder="0 0 12 * * ?" id="scheduler" />
+            </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+            <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="providerUrl">{{ 'mxk.connectors.providerUrl' | i18n
+                }} </nz-form-label>
+            <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48"
+                nzErrorTip="The input is not valid providerUrl!">
+                <input [(ngModel)]="form.model.providerUrl" [ngModelOptions]="{ standalone: true }" nz-input
+                    name="providerUrl" id="providerUrl" />
+            </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+            <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="principal">{{ 'mxk.connectors.principal' | i18n }}
+            </nz-form-label>
+            <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48"
+                nzErrorTip="The input is not valid principal!">
+                <input [(ngModel)]="form.model.principal" [ngModelOptions]="{ standalone: true }" nz-input
+                    name="principal" id="principal" />
+            </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+            <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="credentials">{{ 'mxk.connectors.credentials' | i18n
+                }} </nz-form-label>
+            <nz-form-control [nzSm]="18" [nzMd]="18" [nzXs]="36" [nzXl]="48"
+                nzErrorTip="The input is not valid credentials!">
+                <input type="password" [(ngModel)]="form.model.credentials" [ngModelOptions]="{ standalone: true }"
+                    nz-input name="credentials" id="credentials" />
+            </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+            <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="status">{{ 'mxk.text.status' | i18n }}
+            </nz-form-label>
+            <nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid status!">
+                <nz-switch [(ngModel)]="form.model.switch_status" [ngModelOptions]="{ standalone: true }" name="status"
+                    [nzCheckedChildren]="checkedTemplate" [nzUnCheckedChildren]="unCheckedTemplate"></nz-switch>
+                <ng-template #checkedTemplate><i nz-icon nzType="check"></i></ng-template>
+                <ng-template #unCheckedTemplate><i nz-icon nzType="close"></i></ng-template>
+            </nz-form-control>
+        </nz-form-item>
+    </form>
+</div>
+
+<div *nzModalFooter>
+    <button nz-button nzType="default" (click)="onClose($event)">{{ 'mxk.text.close' | i18n }}</button>
+    <button nz-button nzType="primary" (click)="onSubmit($event)">{{ 'mxk.text.submit' | i18n }}</button>
+</div>

+ 0 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.less


+ 25 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.spec.ts

@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ConnectorEditerComponent } from './connector-editer.component';
+
+describe('ConnectorEditerComponent', () => {
+  let component: ConnectorEditerComponent;
+  let fixture: ComponentFixture<ConnectorEditerComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ConnectorEditerComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ConnectorEditerComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 91 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connector-editer/connector-editer.component.ts

@@ -0,0 +1,91 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.
+ */
+
+import { Component, ChangeDetectorRef, Input, OnInit, Inject } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { I18NService } from '@core';
+import { _HttpClient, ALAIN_I18N_TOKEN, SettingsService } from '@delon/theme';
+import format from 'date-fns/format';
+import { NzMessageService } from 'ng-zorro-antd/message';
+import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
+
+import { Connectors } from '../../../../entity/Connectors';
+import { ConnectorsService } from '../../../../service/connectors.service';
+
+@Component({
+  selector: 'app-connector-editer',
+  templateUrl: './connector-editer.component.html',
+  styleUrls: ['./connector-editer.component.less'],
+  styles: [
+    `
+      nz-form-item {
+        width: 100%;
+      }
+    `
+  ]
+})
+export class ConnectorEditerComponent implements OnInit {
+  @Input() id?: String;
+  @Input() isEdit?: boolean;
+  form: {
+    submitting: boolean;
+    model: Connectors;
+  } = {
+      submitting: false,
+      model: new Connectors()
+    };
+
+  formGroup: FormGroup = new FormGroup({});
+
+  constructor(
+    private modalRef: NzModalRef,
+    private connectorsService: ConnectorsService,
+    private fb: FormBuilder,
+    private msg: NzMessageService,
+    @Inject(ALAIN_I18N_TOKEN) private i18n: I18NService,
+    private cdr: ChangeDetectorRef
+  ) { }
+
+  ngOnInit(): void {
+    if (this.isEdit) {
+      this.connectorsService.get(`${this.id}`).subscribe(res => {
+        this.form.model.init(res.data);
+        this.cdr.detectChanges();
+      });
+    }
+  }
+
+  onClose(e: MouseEvent): void {
+    e.preventDefault();
+    this.modalRef.destroy({ refresh: false });
+  }
+
+  onSubmit(e: MouseEvent): void {
+    e.preventDefault();
+    this.form.submitting = true;
+    this.form.model.trans();
+    (this.isEdit ? this.connectorsService.update(this.form.model) : this.connectorsService.add(this.form.model)).subscribe(res => {
+      if (res.code == 0) {
+        this.msg.success(this.i18n.fanyi(this.isEdit ? 'mxk.alert.update.success' : 'mxk.alert.add.success'));
+      } else {
+        this.msg.error(this.i18n.fanyi(this.isEdit ? 'mxk.alert.update.error' : 'mxk.alert.add.error'));
+      }
+      this.form.submitting = false;
+      this.modalRef.destroy({ refresh: true });
+      this.cdr.detectChanges();
+    });
+  }
+}

+ 75 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.html

@@ -0,0 +1,75 @@
+<page-header> </page-header>
+
+<nz-card [nzBordered]="false">
+    <form nz-form [nzLayout]="'inline'" (ngSubmit)="onSearch()" class="search__form">
+        <div nz-row [nzGutter]="{ xs: 8, sm: 8, md: 8, lg: 24, xl: 48, xxl: 48 }">
+            <div nz-col nzMd="16" nzSm="24">
+                <nz-form-item>
+                    <nz-form-label nzFor="connName">{{ 'mxk.connectors.connName' | i18n }}</nz-form-label>
+                    <nz-form-control>
+                        <input nz-input [(ngModel)]="query.params.connName" [ngModelOptions]="{ standalone: true }"
+                            name="connName" placeholder="" id="connName" />
+                    </nz-form-control>
+                </nz-form-item>
+            </div>
+
+            <div nz-col [nzSpan]="query.expandForm ? 24 : 8" [class.text-right]="query.expandForm">
+                <button nz-button type="submit" [nzType]="'primary'" [nzLoading]="query.submitLoading">{{
+                    'mxk.text.query' | i18n }}</button>
+                <button nz-button type="reset" (click)="onReset()" class="mx-sm" style="display: none">{{
+                    'mxk.text.reset' | i18n }}</button>
+                <button nz-button (click)="query.expandForm = !query.expandForm" class="mx-sm" style="display: none">
+                    {{ query.expandForm ? ('mxk.text.collapse' | i18n) : ('mxk.text.expand' | i18n) }}</button>
+            </div>
+        </div>
+    </form>
+</nz-card>
+<nz-card>
+    <div nz-col [nzSpan]="24" class="table-list-toolbar">
+        <button nz-button type="button" [nzType]="'primary'" (click)="onAdd($event)">{{ 'mxk.text.add' | i18n
+            }}</button>
+        <button nz-button type="button" (click)="onBatchDelete($event)" [nzType]="'primary'" nzDanger class="mx-sm">{{
+            'mxk.text.delete' | i18n
+            }}</button>
+    </div>
+    <nz-table #dynamicTable nzTableLayout="auto" nzSize="small" nzBordered nzShowSizeChanger
+        [nzData]="query.results.rows" [nzFrontPagination]="false" [nzTotal]="query.results.records"
+        [nzPageSizeOptions]="query.params.pageSizeOptions" [nzPageSize]="query.params.pageSize"
+        [nzPageIndex]="query.params.pageNumber" [nzLoading]="this.query.tableLoading"
+        (nzQueryParams)="onQueryParamsChange($event)" nzWidth="100%">
+        <thead>
+            <tr>
+                <th [nzChecked]="query.checked" [nzIndeterminate]="query.indeterminate"
+                    (nzCheckedChange)="onTableAllChecked($event)"></th>
+                <th nzAlign="center" style="display: none">Id</th>
+                <th nzAlign="center">{{ 'mxk.connectors.connName' | i18n }}</th>
+                <th nzAlign="center">{{ 'mxk.connectors.justInTime' | i18n }}</th>
+                <th nzAlign="center">{{ 'mxk.text.status' | i18n }}</th>
+                <th nzAlign="center"><a>{{ 'mxk.text.action' | i18n }}</a></th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr *ngFor="let data of query.results.rows">
+                <td [nzChecked]="query.tableCheckedId.has(data.id)" [nzDisabled]="data.disabled"
+                    (nzCheckedChange)="onTableItemChecked(data.id, $event)"></td>
+                <td nzAlign="left" style="display: none">
+                    <span>{{ data.id }}</span>
+                </td>
+
+                <td nzAlign="left"> {{ data.connName }}</td>
+                <td nzAlign="center" *ngIf="data.justInTime == 1">{{ 'mxk.text.yes' | i18n }}</td>
+                <td nzAlign="center" *ngIf="data.justInTime == 0">{{ 'mxk.text.no' | i18n }}</td>
+                <td nzAlign="center"> <i *ngIf="data.status == 1" nz-icon nzType="check-circle" nzTheme="fill"
+                        style="color: green"></i></td>
+                <td nzAlign="left" nzBreakWord="false">
+                    <div nz-col>
+                        <button nz-button type="button" (click)="onEdit($event, data.id)" style="float: left">{{
+                            'mxk.text.edit' | i18n }}</button>
+                        <button nz-button type="button" (click)="onDelete($event, data.id)" nzDanger>{{
+                            'mxk.text.delete' | i18n }}</button>
+                    </div>
+                </td>
+            </tr>
+        </tbody>
+    </nz-table>
+</nz-card>

+ 0 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.less


+ 25 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.spec.ts

@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ConnectorsComponent } from './connectors.component';
+
+describe('ConnectorsComponent', () => {
+  let component: ConnectorsComponent;
+  let fixture: ComponentFixture<ConnectorsComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ConnectorsComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ConnectorsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 203 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/routes/config/connectors/connectors.component.ts

@@ -0,0 +1,203 @@
+import { ChangeDetectionStrategy, ViewContainerRef, ChangeDetectorRef, Component, OnInit, Inject } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { I18NService } from '@core';
+import { _HttpClient, ALAIN_I18N_TOKEN, SettingsService } from '@delon/theme';
+import { format, addDays } from 'date-fns';
+import { NzSafeAny } from 'ng-zorro-antd/core/types';
+import { NzMessageService } from 'ng-zorro-antd/message';
+import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
+import { NzTableQueryParams } from 'ng-zorro-antd/table';
+
+import { ConnectorsService } from '../../../service/connectors.service';
+import { set2String } from '../../../shared/index';
+import { ConnectorEditerComponent } from './connector-editer/connector-editer.component';
+
+@Component({
+  selector: 'app-connectors',
+  templateUrl: './connectors.component.html',
+  styleUrls: ['./connectors.component.less']
+})
+export class ConnectorsComponent implements OnInit {
+  query: {
+    params: {
+      connName: String;
+      displayName: String;
+      protocol: String;
+      startDate: String;
+      endDate: String;
+      startDatePicker: Date;
+      endDatePicker: Date;
+      pageSize: number;
+      pageNumber: number;
+      pageSizeOptions: number[];
+    };
+    results: {
+      records: number;
+      rows: NzSafeAny[];
+    };
+    expandForm: Boolean;
+    submitLoading: boolean;
+    tableLoading: boolean;
+    tableCheckedId: Set<String>;
+    indeterminate: boolean;
+    checked: boolean;
+  } = {
+      params: {
+        connName: '',
+        displayName: '',
+        protocol: '',
+        startDate: '',
+        endDate: '',
+        startDatePicker: addDays(new Date(), -30),
+        endDatePicker: new Date(),
+        pageSize: 10,
+        pageNumber: 1,
+        pageSizeOptions: [10, 20, 50]
+      },
+      results: {
+        records: 0,
+        rows: []
+      },
+      expandForm: false,
+      submitLoading: false,
+      tableLoading: false,
+      tableCheckedId: new Set<String>(),
+      indeterminate: false,
+      checked: false
+    };
+
+  constructor(
+    private modalService: NzModalService,
+    private connectorsService: ConnectorsService,
+    private viewContainerRef: ViewContainerRef,
+    private fb: FormBuilder,
+    private msg: NzMessageService,
+    @Inject(ALAIN_I18N_TOKEN) private i18n: I18NService,
+    private cdr: ChangeDetectorRef
+  ) { }
+
+  ngOnInit(): void {
+    this.fetch();
+  }
+
+  onQueryParamsChange(tableQueryParams: NzTableQueryParams): void {
+    this.query.params.pageNumber = tableQueryParams.pageIndex;
+    this.query.params.pageSize = tableQueryParams.pageSize;
+    this.fetch();
+  }
+
+  onSearch(): void {
+    this.fetch();
+  }
+
+  onReset(): void { }
+
+  onBatchDelete(e: MouseEvent): void {
+    e.preventDefault();
+    this.connectorsService.delete(set2String(this.query.tableCheckedId)).subscribe(res => {
+      if (res.code == 0) {
+        this.msg.success(this.i18n.fanyi('mxk.alert.delete.success'));
+        this.fetch();
+      } else {
+        this.msg.error(this.i18n.fanyi('mxk.alert.delete.error'));
+      }
+      this.cdr.detectChanges();
+    });
+  }
+
+  onAdd(e: MouseEvent): void {
+    e.preventDefault();
+    const modal = this.modalService.create({
+      nzContent: ConnectorEditerComponent,
+      nzViewContainerRef: this.viewContainerRef,
+      nzComponentParams: {
+        isEdit: false,
+        id: ''
+      },
+      nzOnOk: () => new Promise(resolve => setTimeout(resolve, 1000))
+    });
+    // Return a result when closed
+    modal.afterClose.subscribe(result => {
+      if (result.refresh) {
+        this.fetch();
+      }
+    });
+  }
+
+  onEdit(e: MouseEvent, editId: String): void {
+    e.preventDefault();
+    const modal = this.modalService.create({
+      nzContent: ConnectorEditerComponent,
+      nzViewContainerRef: this.viewContainerRef,
+      nzComponentParams: {
+        isEdit: true,
+        id: editId
+      },
+      nzOnOk: () => new Promise(resolve => setTimeout(resolve, 1000))
+    });
+    // Return a result when closed
+    modal.afterClose.subscribe(result => {
+      if (result.refresh) {
+        this.fetch();
+      }
+    });
+  }
+
+  onDelete(e: MouseEvent, deleteId: String): void {
+    e.preventDefault();
+    this.connectorsService.delete(deleteId).subscribe(res => {
+      if (res.code == 0) {
+        this.msg.success(this.i18n.fanyi('mxk.alert.delete.success'));
+        this.fetch();
+      } else {
+        this.msg.error(this.i18n.fanyi('mxk.alert.delete.error'));
+      }
+      this.cdr.detectChanges();
+    });
+  }
+
+  fetch(): void {
+    this.query.submitLoading = true;
+    this.query.tableLoading = true;
+    this.query.indeterminate = false;
+    this.query.checked = false;
+    this.query.tableCheckedId.clear();
+    if (this.query.expandForm) {
+      this.query.params.endDate = format(this.query.params.endDatePicker, 'yyyy-MM-dd HH:mm:ss');
+      this.query.params.startDate = format(this.query.params.startDatePicker, 'yyyy-MM-dd HH:mm:ss');
+    } else {
+      this.query.params.endDate = '';
+      this.query.params.startDate = '';
+    }
+    this.connectorsService.fetch(this.query.params).subscribe(res => {
+      this.query.results = res.data;
+      this.query.submitLoading = false;
+      this.query.tableLoading = false;
+      this.cdr.detectChanges();
+    });
+  }
+
+  updateTableCheckedSet(id: String, checked: boolean): void {
+    if (checked) {
+      this.query.tableCheckedId.add(id);
+    } else {
+      this.query.tableCheckedId.delete(id);
+    }
+  }
+
+  refreshTableCheckedStatus(): void {
+    const listOfEnabledData = this.query.results.rows.filter(({ disabled }) => !disabled);
+    this.query.checked = listOfEnabledData.every(({ id }) => this.query.tableCheckedId.has(id));
+    this.query.indeterminate = listOfEnabledData.some(({ id }) => this.query.tableCheckedId.has(id)) && !this.query.checked;
+  }
+
+  onTableItemChecked(id: String, checked: boolean): void {
+    this.updateTableCheckedSet(id, checked);
+    this.refreshTableCheckedStatus();
+  }
+
+  onTableAllChecked(checked: boolean): void {
+    this.query.results.rows.filter(({ disabled }) => !disabled).forEach(({ id }) => this.updateTableCheckedSet(id, checked));
+    this.refreshTableCheckedStatus();
+  }
+}

+ 33 - 0
maxkey-web-frontend/maxkey-web-mgt-app/src/app/service/connectors.service.ts

@@ -0,0 +1,33 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.
+ */
+
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { NzSafeAny } from 'ng-zorro-antd/core/types';
+import { Observable } from 'rxjs';
+
+import { Connectors } from '../entity/Connectors';
+import { Message } from '../entity/Message';
+import { BaseService } from './base.service';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ConnectorsService extends BaseService<Connectors> {
+  constructor(private _httpClient: HttpClient) {
+    super(_httpClient, '/config/connectors');
+  }
+}

+ 3 - 4
maxkey-web-frontend/maxkey-web-mgt-app/src/app/service/synchronizers.service.ts

@@ -1,19 +1,18 @@
 /*
  * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * 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.
  */
- 
 
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';

+ 21 - 7
maxkey-web-frontend/maxkey-web-mgt-app/src/assets/app-data.json

@@ -118,13 +118,6 @@
               "children": []
             },
             {
-              "text": "社交服务",
-              "i18n": "mxk.menu.config.socialsproviders",
-              "link": "/config/socialsproviders",
-              "icon": "anticon-comment",
-              "children": []
-            },
-            {
               "text": "同步器管理",
               "i18n": "mxk.menu.config.synchronizers",
               "link": "/config/synchronizers",
@@ -132,6 +125,20 @@
               "children": []
             },
             {
+              "text": "连接器管理",
+              "i18n": "mxk.menu.config.connectors",
+              "link": "/config/connectors",
+              "icon": "anticon-api",
+              "children": []
+            },
+            {
+              "text": "社交服务",
+              "i18n": "mxk.menu.config.socialsproviders",
+              "link": "/config/socialsproviders",
+              "icon": "anticon-comment",
+              "children": []
+            },
+            {
               "text": "LDAP配置",
               "i18n": "mxk.menu.config.ldapcontext",
               "link": "/config/ldapcontext",
@@ -189,6 +196,13 @@
               "children": []
             },
             {
+              "text": "连接器日志",
+              "i18n": "mxk.menu.audit.connector",
+              "link": "/audit/audit-connector",
+              "icon": "anticon-audit",
+              "children": []
+            },
+            {
               "text": "管理日志",
               "i18n": "mxk.menu.audit.operate",
               "link": "/audit/audit-system-logs",

+ 12 - 1
maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/en-US.json

@@ -45,6 +45,7 @@
 				"accountsstrategys": "Strategys",
 				"socialsproviders": "Socials",
 				"synchronizers": "Synchronizers",
+				"connectors":"Connectors",
 				"adapters": "Adapters",
 				"notices": "Notices",
 				"ldapcontext": "LDAP",
@@ -537,6 +538,15 @@
 			"resumeTime": "Join Time",
 			"suspendTime": "Suspend Time"
 		},
+		"connectors": {
+			"connName": "Name",
+			"scheduler": "Scheduler",
+			"justInTime":"JustInTime",
+			"providerUrl": "ProviderUrl",
+			"principal": "Principal",
+			"credentials": "Credentials",
+			"filters": "Filters"
+		},
 		"password": {
 			"id": "Id",
 			"displayName": "DisplayName",
@@ -610,7 +620,8 @@
 
 			"connector.id": "Id",
 			"connector.conName": "Connector",
-			"connector.conType": "Type",
+			"connector.topic": "Topic",
+			"connector.actionType": "ActionType",
 			"connector.sourceId": "SourceId",
 			"connector.sourceName": "SourceName",
 			"connector.objectId": "ObjectId",

+ 12 - 1
maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-CN.json

@@ -45,6 +45,7 @@
 				"accountsstrategys": "账号策略",
 				"socialsproviders": "社交服务",
 				"synchronizers": "同步器管理",
+				"connectors":"连接器管理",
 				"adapters": "适配器注册",
 				"notices": "通知公告",
 				"ldapcontext": "LDAP配置",
@@ -535,6 +536,15 @@
 			"resumeTime": "执行时间",
 			"suspendTime": "挂起时间"
 		},
+		"connectors": {
+			"connName": "名称",
+			"scheduler": "定时任务",
+			"justInTime":"实时同步",
+			"providerUrl": "地址",
+			"principal": "账号",
+			"credentials": "凭证",
+			"filters": "过滤条件"
+		},
 		"password": {
 			"id": "用户编码",
 			"displayName": "姓名",
@@ -608,7 +618,8 @@
 
 			"connector.id": "编号",
 			"connector.conName": "连接器",
-			"connector.conType": "类型",
+			"connector.topic": "主题",
+			"connector.actionType": "操作",
 			"connector.sourceId": "源编码",
 			"connector.sourceName": "源名称",
 			"connector.objectId": "对象编号",

+ 12 - 1
maxkey-web-frontend/maxkey-web-mgt-app/src/assets/i18n/zh-TW.json

@@ -46,6 +46,7 @@
 				"accountsstrategys": "賬號策略",
 				"socialsproviders": "社交服務",
 				"synchronizers": "同步器管理",
+				"connectors":"連接器管理",
 				"adapters": "適配器註冊",
 				"notices": "通知公告",
 				"ldapcontext": "LDAP配置",
@@ -536,6 +537,15 @@
 			"resumeTime": "執行時間",
 			"suspendTime": "掛起時間"
 		},
+		"connectors": {
+			"connName": "名稱",
+			"scheduler": "定時任務",
+			"justInTime":"實时同步",
+			"providerUrl": "地址",
+			"principal": "賬號",
+			"credentials": "憑證",
+			"filters": "過濾條件"
+		},
 		"password": {
 			"id": "用戶編碼",
 			"displayName": "姓名",
@@ -609,7 +619,8 @@
 
 			"connector.id": "編號",
 			"connector.conName": "連接器",
-			"connector.conType": "類型",
+			"connector.topic": "主題",
+			"connector.actionType": "操作",
 			"connector.sourceId": "源編碼",
 			"connector.sourceName": "源名稱",
 			"connector.objectId": "對象編號",

+ 3 - 3
maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java

@@ -106,7 +106,7 @@ public class MaxKeyMvcConfig implements WebMvcConfigurer {
         //addPathPatterns 用于添加拦截规则 , 先把所有路径都加入拦截, 再一个个排除
         //excludePathPatterns 表示改路径不用拦截
         
-        _logger.debug("add HttpKerberosEntryPoint");
+        _logger.debug("add Http Kerberos Entry Point");
         registry.addInterceptor(new HttpKerberosEntryPoint(
     			authenticationProvider,kerberosService,applicationConfig,true))
     		.addPathPatterns("/login");
@@ -115,13 +115,13 @@ public class MaxKeyMvcConfig implements WebMvcConfigurer {
         if(httpHeaderEnable) {
             registry.addInterceptor(new HttpHeaderEntryPoint(httpHeaderName,httpHeaderEnable))
                     .addPathPatterns("/*");
-            _logger.debug("add HttpHeaderEntryPoint");
+            _logger.debug("add Http Header Entry Point");
         }
         
         if(basicEnable) {
             registry.addInterceptor(new BasicEntryPoint(basicEnable))
                     .addPathPatterns("/*");
-            _logger.debug("add BasicEntryPoint");
+            _logger.debug("add Basic Entry Point");
         }
         
         //for frontend

+ 2 - 2
maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties

@@ -44,8 +44,8 @@ maxkey.server.authz.uri                         =${maxkey.server.name}:${server.
 maxkey.server.frontend.uri                      =/maxkey
 #InMemory 0 , Redis 2               
 maxkey.server.persistence                       =${SERVER_PERSISTENCE:0}
-#identity none, provision
-maxkey.server.message.queue                     =${SERVER_MESSAGE_QUEUE:none}
+#identity true,false
+maxkey.server.provision                         =${SERVER_PROVISION:false}
 #issuer name                
 maxkey.app.issuer                               =CN=ConSec,CN=COM,CN=SH
 #must > jwt expire * 2    

+ 22 - 0
maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtListenerConfig.java

@@ -18,11 +18,15 @@
 package org.maxkey;
 
 import org.maxkey.authn.session.SessionManager;
+import org.maxkey.configuration.ApplicationConfig;
 import org.maxkey.listener.DynamicRolesListenerAdapter;
 import org.maxkey.listener.ListenerAdapter;
 import org.maxkey.listener.ListenerParameter;
 import org.maxkey.listener.SessionListenerAdapter;
+import org.maxkey.persistence.service.ConnectorsService;
 import org.maxkey.persistence.service.RolesService;
+import org.maxkey.provision.thread.ProvisioningRunner;
+import org.maxkey.provision.thread.ProvisioningRunnerThread;
 import org.quartz.Scheduler;
 import org.quartz.SchedulerException;
 import org.slf4j.Logger;
@@ -31,6 +35,7 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.core.JdbcTemplate;
 
 @Configuration
 public class MaxKeyMgtListenerConfig  implements InitializingBean {
@@ -69,6 +74,23 @@ public class MaxKeyMgtListenerConfig  implements InitializingBean {
         return "dynamicRolesListenerAdapter";
     }
     
+    @Bean
+    public String  provisioningRunnerThread(
+    		ConnectorsService connectorsService,
+    		JdbcTemplate jdbcTemplate,
+    		ApplicationConfig applicationConfig
+            ) throws SchedulerException {
+        if(applicationConfig.isProvisionSupport()) {
+	    	ProvisioningRunner runner = new ProvisioningRunner(connectorsService,jdbcTemplate);
+	    	ProvisioningRunnerThread runnerThread = new ProvisioningRunnerThread(runner);
+	    	runnerThread.start();
+	        _logger.debug("provisioning Runner Thread .");
+        }else {
+        	_logger.debug("not need init provisioning Runner Thread .");
+        }
+        return "provisioningRunnerThread";
+    }
+    
     @Override
     public void afterPropertiesSet() throws Exception {
         

+ 4 - 4
maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtMvcConfig.java

@@ -84,7 +84,7 @@ public class MaxKeyMgtMvcConfig implements WebMvcConfigurer {
     public void addInterceptors(InterceptorRegistry registry) {
         //addPathPatterns 用于添加拦截规则 , 先把所有路径都加入拦截, 再一个个排除
         //excludePathPatterns 表示改路径不用拦截
-        _logger.debug("add HttpJwtEntryPoint");
+        _logger.debug("add Interceptors");
 
         permissionInterceptor.setMgmt(true);
         
@@ -118,7 +118,7 @@ public class MaxKeyMgtMvcConfig implements WebMvcConfigurer {
                 .addPathPatterns("/logout/**")
                 ;
         
-        _logger.debug("add PermissionAdapter");
+        _logger.debug("add Permission Adapter");
         
         registry.addInterceptor(historyLogsAdapter)
                 .addPathPatterns("/userinfo/**")
@@ -131,7 +131,7 @@ public class MaxKeyMgtMvcConfig implements WebMvcConfigurer {
                 .addPathPatterns("/apps/**")
                 .addPathPatterns("/approles/**")
                 ;
-        _logger.debug("add HistoryLogsAdapter");
+        _logger.debug("add History Logs Adapter");
         
         /*
          * api
@@ -144,7 +144,7 @@ public class MaxKeyMgtMvcConfig implements WebMvcConfigurer {
                 .addPathPatterns("/api/idm/scim/**")
                 ;
 		
-        _logger.debug("add RestApiPermissionAdapter");
+        _logger.debug("add Rest Api Permission Adapter");
         
     }
     

+ 105 - 0
maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/config/contorller/ConnectorsController.java

@@ -0,0 +1,105 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.config.contorller;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.mybatis.jpa.persistence.JpaPageResults;
+import org.maxkey.authn.annotation.CurrentUser;
+import org.maxkey.crypto.password.PasswordReciprocal;
+import org.maxkey.entity.Connectors;
+import org.maxkey.entity.Message;
+import org.maxkey.entity.UserInfo;
+import org.maxkey.persistence.service.ConnectorsService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+@RequestMapping(value={"/config/connectors"})
+public class ConnectorsController {
+	final static Logger _logger = LoggerFactory.getLogger(ConnectorsController.class);
+	
+	@Autowired
+	ConnectorsService connectorsService;
+	
+	@RequestMapping(value = { "/fetch" }, produces = {MediaType.APPLICATION_JSON_VALUE})
+	@ResponseBody
+	public ResponseEntity<?> fetch(Connectors connector,@CurrentUser UserInfo currentUser) {
+		_logger.debug(""+connector);
+		connector.setInstId(currentUser.getInstId());
+		return new Message<JpaPageResults<Connectors>>(
+				connectorsService.queryPageResults(connector)).buildResponse();
+	}
+	
+	@RequestMapping(value = { "/get/{id}" }, produces = {MediaType.APPLICATION_JSON_VALUE})
+	public ResponseEntity<?> get(@PathVariable("id") String id) {
+		Connectors connector = connectorsService.get(id);
+		if(StringUtils.isNotBlank(connector.getCredentials())) {
+			connector.setCredentials(PasswordReciprocal.getInstance().decoder(connector.getCredentials()));
+		}
+		return new Message<Connectors>(connector).buildResponse();
+	}
+	
+	@ResponseBody
+	@RequestMapping(value={"/add"}, produces = {MediaType.APPLICATION_JSON_VALUE})
+	public ResponseEntity<?> insert(@RequestBody  Connectors connector,@CurrentUser UserInfo currentUser) {
+		_logger.debug("-Add  :" + connector);
+		connector.setInstId(currentUser.getInstId());
+		if(StringUtils.isNotBlank(connector.getCredentials())) {
+			connector.setCredentials(PasswordReciprocal.getInstance().encode(connector.getCredentials()));
+		}
+		if (connectorsService.insert(connector)) {
+			return new Message<Connectors>(Message.SUCCESS).buildResponse();
+		} else {
+			return new Message<Connectors>(Message.FAIL).buildResponse();
+		}
+	}
+	
+	@ResponseBody
+	@RequestMapping(value={"/update"}, produces = {MediaType.APPLICATION_JSON_VALUE})
+	public ResponseEntity<?> update(@RequestBody  Connectors connector,@CurrentUser UserInfo currentUser) {
+		_logger.debug("-update  :" + connector);
+		connector.setInstId(currentUser.getInstId());
+		connector.setCredentials(PasswordReciprocal.getInstance().encode(connector.getCredentials()));
+		if (connectorsService.update(connector)) {
+		    return new Message<Connectors>(Message.SUCCESS).buildResponse();
+		} else {
+			return new Message<Connectors>(Message.FAIL).buildResponse();
+		}
+	}
+	
+	@ResponseBody
+	@RequestMapping(value={"/delete"}, produces = {MediaType.APPLICATION_JSON_VALUE})
+	public ResponseEntity<?> delete(@RequestParam("ids") String ids,@CurrentUser UserInfo currentUser) {
+		_logger.debug("-delete  ids : {} " , ids);
+		if (connectorsService.deleteBatch(ids)) {
+			 return new Message<Connectors>(Message.SUCCESS).buildResponse();
+		} else {
+			return new Message<Connectors>(Message.FAIL).buildResponse();
+		}
+	}
+
+}

+ 2 - 2
maxkey-webs/maxkey-web-mgt/src/main/resources/application-http.properties

@@ -33,8 +33,8 @@ maxkey.server.mgt.uri                           =${maxkey.server.uri}
 maxkey.server.authz.uri                         =https://${maxkey.server.domain}/maxkey
 #InMemory 0 , Redis 2 
 maxkey.server.persistence                       =0
-#identity none, provision
-maxkey.server.message.queue                     =${SERVER_MESSAGE_QUEUE:none}
+#identity true,false
+maxkey.server.provision                         =${SERVER_PROVISION:false}
 
 maxkey.session.timeout                          =${SERVER_SESSION_TIMEOUT:1800}