Explorar el Código

同步域架构

徐少年 hace 2 años
padre
commit
2682a393df

+ 7 - 0
Admin.NET/Admin.NET.Core/Entity/SysLdap.cs

@@ -77,6 +77,13 @@ public class SysLdap : EntityTenant
     public virtual string BindAttrEmployeeId { get; set; } = "EmployeeId";
 
     /// <summary>
+    /// 绑定Code属性值
+    /// </summary>
+    [SugarColumn(ColumnDescription = "绑定对象Code属性值", Length = 64)]
+    [Required]
+    public virtual string BindAttrCode { get; set; } = "objectGUID";
+
+    /// <summary>
     /// 状态
     /// </summary>
     [SugarColumn(ColumnDescription = "状态")]

+ 6 - 0
Admin.NET/Admin.NET.Core/Entity/SysUserLdap.cs

@@ -36,4 +36,10 @@ public class SysUserLdap : EntityTenant
     /// </summary>
     [SugarColumn(ColumnDescription = "对应EmployeeId", Length = 32)]
     public string EmployeeId { get; set; }
+
+    /// <summary>
+    /// 组织代码
+    /// </summary>
+    [SugarColumn(ColumnDescription = "DeptCode", Length = 64)]
+    public string DeptCode { get; set; }
 }

+ 1 - 0
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -89,6 +89,7 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=1310000000195, Pid=1310000000191, Title="增加", Permission="sysLdap:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
             new SysMenu{ Id=1310000000196, Pid=1310000000191, Title="删除", Permission="sysLdap:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
             new SysMenu{ Id=1310000000197, Pid=1310000000191, Title="同步域账户", Permission="sysLdap:syncUser", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150 },
+            new SysMenu{ Id=1310000000198, Pid=1310000000191, Title="同步域组织", Permission="sysLdap:syncOrg", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
 
             new SysMenu{ Id=1310000000301, Pid=0, Title="平台管理", Path="/platform", Name="platform", Component="Layout", Icon="ele-Menu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=11000 },
 

+ 159 - 16
Admin.NET/Admin.NET.Core/Service/Auth/SysLdapService.cs

@@ -186,15 +186,13 @@ public class SysLdapService : IDynamicApiController, ITransient
                 }
 
                 var attrs = ldapEntry.GetAttributeSet();
+                string deptCode = GetDepartmentCode(attrs, sysLdap.BindAttrCode);
                 if (attrs.Count == 0 || attrs.ContainsKey("OU"))
-                    SearchDnLdapUser(ldapConn, sysLdap, userLdapList, ldapEntry.Dn);
+                    SearchDnLdapUser(ldapConn, sysLdap, userLdapList, ldapEntry.Dn, deptCode);
                 else
                 {
-                    var sysUserLdap = new SysUserLdap
-                    {
-                        Account = !attrs.ContainsKey(sysLdap.BindAttrAccount) ? null : attrs.GetAttribute(sysLdap.BindAttrAccount)?.StringValue,
-                        EmployeeId = !attrs.ContainsKey(sysLdap.BindAttrEmployeeId) ? null : attrs.GetAttribute(sysLdap.BindAttrEmployeeId)?.StringValue
-                    };
+                    var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode);
+
                     if (string.IsNullOrEmpty(sysUserLdap.EmployeeId)) continue;
                     userLdapList.Add(sysUserLdap);
                 }
@@ -220,15 +218,47 @@ public class SysLdapService : IDynamicApiController, ITransient
     }
 
     /// <summary>
+    /// 获取部门代码
+    /// </summary>
+    /// <param name="attrs"></param>
+    /// <param name="bindAttrCode"></param>
+    /// <returns></returns>
+    private static string GetDepartmentCode(LdapAttributeSet attrs, string bindAttrCode)
+    {
+        return bindAttrCode == "objectGUID"
+            ? new Guid(attrs.GetAttribute(bindAttrCode)?.ByteValue).ToString()
+            : attrs.GetAttribute(bindAttrCode)?.StringValue ?? "0";
+    }
+
+    /// <summary>
+    /// 创建同步对象
+    /// </summary>
+    /// <param name="attrs"></param>
+    /// <param name="bindAttrAccount"></param>
+    /// <param name="bindAttrEmployeeId"></param>
+    /// <param name="deptCode"></param>
+    /// <returns></returns>
+    private static SysUserLdap CreateSysUserLdap(LdapAttributeSet attrs, string bindAttrAccount, string bindAttrEmployeeId, string deptCode)
+    {
+        return new SysUserLdap
+        {
+            Account = !attrs.ContainsKey(bindAttrAccount) ? null : attrs.GetAttribute(bindAttrAccount)?.StringValue,
+            EmployeeId = !attrs.ContainsKey(bindAttrEmployeeId) ? null : attrs.GetAttribute(bindAttrEmployeeId)?.StringValue,
+            DeptCode = deptCode
+        };
+    }
+
+    /// <summary>
     /// 遍历查询域用户
     /// </summary>
-    /// <param name="conn"></param>
-    /// <param name="ldap"></param>
+    /// <param name="ldapConn"></param>
+    /// <param name="sysLdap"></param>
     /// <param name="userLdapList"></param>
     /// <param name="baseDn"></param>
-    private static void SearchDnLdapUser(LdapConnection conn, SysLdap ldap, List<SysUserLdap> userLdapList, string baseDn)
+    /// <param name="deptCode"></param>
+    private static void SearchDnLdapUser(LdapConnection ldapConn, SysLdap sysLdap, List<SysUserLdap> userLdapList, string baseDn, string deptCode)
     {
-        var ldapSearchResults = conn.Search(baseDn, LdapConnection.ScopeOne, "(objectClass=*)", null, false);
+        var ldapSearchResults = ldapConn.Search(baseDn, LdapConnection.ScopeOne, "(objectClass=*)", null, false);
         while (ldapSearchResults.HasMore())
         {
             LdapEntry ldapEntry;
@@ -243,18 +273,131 @@ public class SysLdapService : IDynamicApiController, ITransient
             }
 
             var attrs = ldapEntry.GetAttributeSet();
+            deptCode = GetDepartmentCode(attrs, sysLdap.BindAttrCode);
+
             if (attrs.Count == 0 || attrs.ContainsKey("OU"))
-                SearchDnLdapUser(conn, ldap, userLdapList, ldapEntry.Dn);
+                SearchDnLdapUser(ldapConn, sysLdap, userLdapList, ldapEntry.Dn, deptCode);
             else
             {
-                var sysUserLdap = new SysUserLdap
-                {
-                    Account = !attrs.ContainsKey(ldap.BindAttrAccount) ? null : attrs.GetAttribute(ldap.BindAttrAccount)?.StringValue,
-                    EmployeeId = !attrs.ContainsKey(ldap.BindAttrEmployeeId) ? null : attrs.GetAttribute(ldap.BindAttrEmployeeId)?.StringValue
-                };
+                var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode);
+
                 if (string.IsNullOrEmpty(sysUserLdap.EmployeeId)) continue;
                 userLdapList.Add(sysUserLdap);
             }
         }
     }
+
+    /// <summary>
+    /// 同步域组织 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [DisplayName("同步域组织")]
+    public async Task SyncDept(SyncSysLdapInput input)
+    {
+        var sysLdap = await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        var ldapConn = new LdapConnection();
+        try
+        {
+            ldapConn.Connect(sysLdap.Host, sysLdap.Port);
+            ldapConn.Bind(sysLdap.Version, sysLdap.BindDn, sysLdap.BindPass);
+            var ldapSearchResults = ldapConn.Search(sysLdap.BaseDn, LdapConnection.ScopeOne, "(objectClass=*)", null, false);
+            var listOrgs = new List<SysOrg>();
+            while (ldapSearchResults.HasMore())
+            {
+                LdapEntry ldapEntry;
+                try
+                {
+                    ldapEntry = ldapSearchResults.Next();
+                    if (ldapEntry == null) continue;
+                }
+                catch (LdapException)
+                {
+                    continue;
+                }
+
+                var attrs = ldapEntry.GetAttributeSet();
+                if (attrs.Count == 0 || attrs.ContainsKey("OU"))
+                {
+                    var sysOrg = CreateSysOrg(attrs, sysLdap, listOrgs, new SysOrg { Id = 0, Level = 0 });
+                    listOrgs.Add(sysOrg);
+
+                    SearchDnLdapDept(ldapConn, sysLdap, listOrgs, ldapEntry.Dn, sysOrg);
+                }
+            }
+
+            if (listOrgs.Count == 0)
+                return;
+
+            await App.GetRequiredService<SysOrgService>().BatchAddOrgs(listOrgs);
+        }
+        catch (LdapException e)
+        {
+            throw e.ResultCode switch
+            {
+                LdapException.NoSuchObject or LdapException.NoSuchAttribute => Oops.Oh(ErrorCodeEnum.D0009),
+                _ => Oops.Oh(e.Message),
+            };
+        }
+        finally
+        {
+            ldapConn.Disconnect();
+        }
+    }
+
+    /// <summary>
+    /// 遍历查询域用户
+    /// </summary>
+    /// <param name="ldapConn"></param>
+    /// <param name="sysLdap"></param>
+    /// <param name="listOrgs"></param>
+    /// <param name="baseDn"></param>
+    /// <param name="org"></param>
+    private static void SearchDnLdapDept(LdapConnection ldapConn, SysLdap sysLdap, List<SysOrg> listOrgs, string baseDn, SysOrg org)
+    {
+        var ldapSearchResults = ldapConn.Search(baseDn, LdapConnection.ScopeOne, "(objectClass=*)", null, false);
+        while (ldapSearchResults.HasMore())
+        {
+            LdapEntry ldapEntry;
+            try
+            {
+                ldapEntry = ldapSearchResults.Next();
+                if (ldapEntry == null) continue;
+            }
+            catch (LdapException)
+            {
+                continue;
+            }
+
+            var attrs = ldapEntry.GetAttributeSet();
+            if (attrs.Count == 0 || attrs.ContainsKey("OU"))
+            {
+                var sysOrg = CreateSysOrg(attrs, sysLdap, listOrgs, org);
+                listOrgs.Add(sysOrg);
+
+                SearchDnLdapDept(ldapConn, sysLdap, listOrgs, ldapEntry.Dn, sysOrg);
+            }
+        }
+    }
+
+    /// <summary>
+    /// 创建架构对象
+    /// </summary>
+    /// <param name="attrs"></param>
+    /// <param name="sysLdap"></param>
+    /// <param name="listOrgs"></param>
+    /// <param name="org"></param>
+    /// <returns></returns>
+    private static SysOrg CreateSysOrg(LdapAttributeSet attrs, SysLdap sysLdap, List<SysOrg> listOrgs, SysOrg org)
+    {
+        return new SysOrg
+        {
+            Pid = org.Id,
+            Id = YitIdHelper.NextId(),
+            Code = !attrs.ContainsKey(sysLdap.BindAttrCode) ? null : new Guid(attrs.GetAttribute(sysLdap.BindAttrCode)?.ByteValue).ToString(),
+            Level = org.Level + 1,
+            Name = !attrs.ContainsKey(sysLdap.BindAttrAccount) ? null : attrs.GetAttribute(sysLdap.BindAttrAccount)?.StringValue,
+            OrderNo = listOrgs.Count + 1,
+        };
+    }
 }

+ 13 - 0
Admin.NET/Admin.NET.Core/Service/Org/SysOrgService.cs

@@ -130,6 +130,19 @@ public class SysOrgService : IDynamicApiController, ITransient
     }
 
     /// <summary>
+    /// 批量增加机构
+    /// </summary>
+    /// <param name="list"></param>
+    /// <returns></returns>
+    [NonAction]
+    public async Task BatchAddOrgs(List<SysOrg> list)
+    {
+        DeleteAllUserOrgCache(0, 0);
+        await _sysOrgRep.AsDeleteable().ExecuteCommandAsync();
+        await _sysOrgRep.AsInsertable(list).ExecuteCommandAsync();
+    }
+
+    /// <summary>
     /// 更新机构 🔖
     /// </summary>
     /// <param name="input"></param>

+ 83 - 0
Web/src/api-services/apis/sys-ldap-api.ts

@@ -321,6 +321,54 @@ export const SysLdapApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
+         * @summary 同步域组织 🔖
+         * @param {SyncSysLdapInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        apiSysLdapSyncOrgPost: async (body?: SyncSysLdapInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/api/sysLdap/syncOrg`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+            // http bearer authentication required
+            if (configuration && configuration.accessToken) {
+                const accessToken = typeof configuration.accessToken === 'function'
+                    ? await configuration.accessToken()
+                    : await configuration.accessToken;
+                localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
          * @summary 更新系统域登录配置 🔖
          * @param {UpdateSysLdapInput} [body] 
          * @param {*} [options] Override http request option.
@@ -461,6 +509,20 @@ export const SysLdapApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
+         * @summary 同步域组织 🔖
+         * @param {SyncSysLdapInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysLdapSyncOrgPost(body?: SyncSysLdapInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysLdapApiAxiosParamCreator(configuration).apiSysLdapSyncOrgPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
          * @summary 更新系统域登录配置 🔖
          * @param {UpdateSysLdapInput} [body] 
          * @param {*} [options] Override http request option.
@@ -543,6 +605,16 @@ export const SysLdapApiFactory = function (configuration?: Configuration, basePa
         },
         /**
          * 
+         * @summary 同步域组织 🔖
+         * @param {SyncSysLdapInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async apiSysLdapSyncOrgPost(body?: SyncSysLdapInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysLdapApiFp(configuration).apiSysLdapSyncOrgPost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
          * @summary 更新系统域登录配置 🔖
          * @param {UpdateSysLdapInput} [body] 
          * @param {*} [options] Override http request option.
@@ -628,6 +700,17 @@ export class SysLdapApi extends BaseAPI {
     }
     /**
      * 
+     * @summary 同步域组织 🔖
+     * @param {SyncSysLdapInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysLdapApi
+     */
+    public async apiSysLdapSyncOrgPost(body?: SyncSysLdapInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysLdapApiFp(this.configuration).apiSysLdapSyncOrgPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
      * @summary 更新系统域登录配置 🔖
      * @param {UpdateSysLdapInput} [body] 
      * @param {*} [options] Override http request option.

+ 8 - 0
Web/src/api-services/models/add-sys-ldap-input.ts

@@ -166,6 +166,14 @@ export interface AddSysLdapInput {
     bindAttrEmployeeId: string;
 
     /**
+     * 绑定Code属性值
+     *
+     * @type {string}
+     * @memberof AddSysLdapInput
+     */
+    bindAttrCode: string;
+
+    /**
      * @type {StatusEnum}
      * @memberof AddSysLdapInput
      */

+ 8 - 0
Web/src/api-services/models/sys-ldap.ts

@@ -166,6 +166,14 @@ export interface SysLdap {
     bindAttrEmployeeId: string;
 
     /**
+     * 绑定Code属性值
+     *
+     * @type {string}
+     * @memberof AddSysLdapInput
+     */
+    bindAttrCode: string;
+
+    /**
      * @type {StatusEnum}
      * @memberof SysLdap
      */

+ 8 - 0
Web/src/api-services/models/update-sys-ldap-input.ts

@@ -166,6 +166,14 @@ export interface UpdateSysLdapInput {
     bindAttrEmployeeId: string;
 
     /**
+     * 绑定Code属性值
+     *
+     * @type {string}
+     * @memberof AddSysLdapInput
+     */
+    bindAttrCode: string;
+
+    /**
      * @type {StatusEnum}
      * @memberof UpdateSysLdapInput
      */

+ 6 - 0
Web/src/views/system/ldap/component/editLdap.vue

@@ -50,6 +50,11 @@
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="绑定Code属性" prop="bindAttrCode">
+							<el-input v-model="state.ruleForm.bindAttrCode" placeholder="请输入绑定Code属性!" maxlength="64" show-word-limit clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						<el-form-item label="Ldap版本" prop="version">
 							<el-input v-model="state.ruleForm.version" type="number" placeholder="请输入Ldap版本" maxlength="4" clearable />
 						</el-form-item>
@@ -130,6 +135,7 @@ const rules = ref<FormRules>({
 	version: [{ required: true, message: '请输入Ldap版本!', trigger: 'blur' }],
 	bindAttrAccount: [{ required: true, message: '请输入账号绑定字段!', trigger: 'blur' }],
 	bindAttrEmployeeId: [{ required: true, message: '绑定用户EmployeeId属性!', trigger: 'blur' }],
+	bindAttrCode: [{ required: true, message: '绑定Code属性!', trigger: 'blur' }],
 });
 
 // 导出对象

+ 17 - 1
Web/src/views/system/ldap/index.vue

@@ -41,11 +41,12 @@
 						<ModifyRecord :data="scope.row" />
 					</template>
 				</el-table-column>
-				<el-table-column label="操作" width="240" align="center" fixed="right" show-overflow-tooltip="" v-if="auth('sysLdap:update') || auth('sysLdap:delete') || auth('sysLdap:syncUser')">
+				<el-table-column label="操作" width="240" align="center" fixed="right" show-overflow-tooltip="" v-if="auth('sysLdap:update') || auth('sysLdap:delete') || auth('sysLdap:syncUser') || auth('sysLdap:syncOrg')">
 					<template #default="scope">
 						<el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditSysLdap(scope.row)" v-auth="'sysLdap:update'"> 编辑 </el-button>
 						<el-button icon="ele-Delete" size="small" text type="danger" @click="delSysLdap(scope.row)" v-auth="'sysLdap:delete'"> 删除 </el-button>
 						<el-button icon="ele-Refresh" size="small" text type="primary" @click="syncDomainUser(scope.row)" v-auth="'sysLdap:syncUser'"> 同步域账户 </el-button>
+						<el-button icon="ele-Refresh" size="small" text type="primary" @click="syncDomainOrg(scope.row)" v-auth="'sysLdap:syncOrg'"> 同步域组织 </el-button>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -166,4 +167,19 @@ const syncDomainUser = (row: any) => {
 		})
 		.catch(() => {});
 };
+
+// 同步域组织
+const syncDomainOrg = (row: any) => {
+	ElMessageBox.confirm(`确定要同步域组织架构吗?`, '提示', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	})
+		.then(async () => {
+			await getAPI(SysLdapApi).apiSysLdapSyncOrgPost({ id: row.id });
+			handleQuery();
+			ElMessage.success('删除成功');
+		})
+		.catch(() => {});
+};
 </script>