This commit is contained in:
eryajf 2022-06-14 11:22:40 +08:00
commit 70116b7c19
67 changed files with 1694 additions and 1344 deletions

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 📜 官方文档 | GO Ldap Admin Doc
url: http://ldapdoc.eryajf.net
about: 关于项目的功能用法以及设计考量,都会在官网进行呈现,提交问题之前,请先阅读官方文档,如果还不能满足,则再提问题。 | Regarding the functional usage and design considerations of the project, they will be presented on the official website. Before submitting the question, please read the official document first. If it is not satisfied, you will ask the question.
- name: 👀 Github论坛 | GitHub Discussions
url: https://github.com/eryajf/go-ldap-admin/discussions
about: 如果您的问题不是功能或者错误,请转到讨论面板并在提交之前检索您的问题是否已经存在。 | If your question is not a feature or a bug, please go to the discussion panel and retrieve if your question already exists before submitting.

View File

@ -0,0 +1,18 @@
---
name: 🐛 错误报告 | Bug Report
about: 请详细描述您使用过程中遇到的问题。| Please describe in detail the problems you encountered in the process of using.
title: "🐛 一些问题。。。 | [Bug] Some problem..."
labels: ["bug"]
---
<!-- 请在您提交 bug 之前,回答以下这些问题。 | Please answer these questions before you submit a bug. -->
#### 您使用的版本? | Your usage version?
#### 您使用的场景? | Your usage scenarios?
#### 您做了什么操作? | What did you do?
#### 您遇到了什么问题? | What are your problems?
#### 您期望的结果是怎样的? | What is your expected outcome?

View File

@ -0,0 +1,12 @@
---
name: 🚀 功能请求 | Feature Request
about: 请详细描述您期望的功能。 | Please describe in detail the features you expect.
title: "🚀 一些功能。。。 | [Feature]Some feature..."
labels: ["enhancement"]
---
<!-- 请在您提交期望的功能之前,回答以下这些问题。 | Please answer these questions before you submit the desired feature. -->
#### 您使用的场景? | 1. Your usage scenarios?
#### 您期望的结果是怎样的? | 2. What is your expected outcome?

View File

@ -0,0 +1,22 @@
---
name: 🙋 问题交流 | Question Report
about: 在文档或讨论中没有回答的使用问题 | Usage question that isn't answered in docs or discussion
title: "🙋 问题交流。。。 | [Question] Some question..."
labels: ["question"]
---
## Question Report
- 搜索打开和关闭的 [GitHub 问题](https://github.com/eryajf/go-ldap-admin/issues)
请在提交问题之前回答这些问题,谢谢。 | Please answer these questions before submitting them. Thank you.
### 你使用了哪个版本? | Which version did you use?
### 预期行为 | Expected behavior
### 实际行为 | Actual behavior
### 原因分析(如果可以) | Cause analysis (if possible)
### 问题重现步骤 | Steps to reproduce the problem

View File

@ -1,4 +1,41 @@
# Configuration for Release Drafter: https://github.com/toolmantim/release-drafter
name-template: 'v$NEXT_PATCH_VERSION 🌈'
tag-template: 'v$NEXT_PATCH_VERSION'
version-template: $MAJOR.$MINOR.$PATCH
# Emoji reference: https://gitmoji.carloscuesta.me/
categories:
- title: '🚀 Features'
labels:
- 'feature'
- 'enhancement'
- 'kind/feature'
- title: '🐛 Bug Fixes'
labels:
- 'fix'
- 'bugfix'
- 'bug'
- 'regression'
- 'kind/bug'
- title: 📝 Documentation updates
labels:
- documentation
- 'kind/doc'
- title: 👻 Maintenance
labels:
- chore
- dependencies
- 'kind/chore'
- 'kind/dep'
- title: 🚦 Tests
labels:
- test
- tests
exclude-labels:
- reverted
- no-changelog
- skip-changelog
- invalid
change-template: '* $TITLE (#$NUMBER) @$AUTHOR'
template: |
## Whats Changed
$CHANGES

View File

@ -23,30 +23,30 @@ jobs:
buildx:
runs-on: ubuntu-latest
steps:
-
name: Checkout
- name: Checkout
uses: actions/checkout@v2
- name: Get current date
id: date
run: echo "::set-output name=today::$(date +'%Y-%m-%d_%H-%M')"
-
name: Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
-
name: Available platforms
- name: Available platforms
run: echo ${{ steps.buildx.outputs.platforms }}
-
name: Login to DockerHub
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .

17
.github/workflows/golangci-lint.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: golangci-lint
on: [push, pull_request]
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.17
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.46.2

21
.github/workflows/issue.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Issue Reply
on:
issues:
types: [labeled]
jobs:
reply-helper:
runs-on: ubuntu-latest
steps:
- name: help wanted
if: github.event.label.name == 'help wanted' || github.event.label.name == 'enhancement' || github.event.label.name == 'bug' || github.event.label.name == 'question'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment'
token: ${{ secrets.ACCESS_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
您好 @${{ github.event.issue.user.login }}👋,我已收到您的反馈,我将安排时间考虑您提交的信息并进行回复。-- 这条信息是由自动回复的机器人发出的。
Hello @${{ github.event.issue.user.login }}. I have received your feedback, and I will arrange time to consider the information you submitted and reply. -- This message is sent by an automatic reply robot.

3
.gitignore vendored
View File

@ -6,10 +6,13 @@
*.dylib
.idea
.vscode
.token
build.sh
logs
go-web-mini
go-ldap-admin
# Test binary, built with `go test -c`
*.test

View File

@ -10,4 +10,7 @@ build-linux:
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o go-ldap-admin main.go
build-linux-arm:
CGO_ENABLED=0 GOARCH=arm64 GOOS=linux go build -o go-ldap-admin main.go
CGO_ENABLED=0 GOARCH=arm64 GOOS=linux go build -o go-ldap-admin main.go
lint:
env GOGC=25 golangci-lint run --fix -j 8 -v ./...

238
README.md
View File

@ -1,38 +1,57 @@
<h1 align="center">Go-Ldap-Admin</h1>
<p align="center">
<a href="" rel="noopener">
<img src="https://cdn.staticaly.com/gh/eryajf/tu/main/img/image_20220607_174313.png" alt="Project logo"></a>
</p>
<h1 align="center">Go Ldap Admin</h1>
<div align="center">
基于Go+Vue实现的openLDAP后台管理项目。
<p align="center">
<img src="https://img.shields.io/github/go-mod/go-version/eryajf-world/go-ldap-admin" alt="Go version"/>
<img src="https://img.shields.io/badge/Gin-1.6.3-brightgreen" alt="Gin version"/>
<img src="https://img.shields.io/badge/Gorm-1.20.12-brightgreen" alt="Gorm version"/>
<img src="https://img.shields.io/github/license/eryajf-world/go-ldap-admin" alt="License"/>
</p>
[![Go Version](https://img.shields.io/github/go-mod/go-version/eryajf-world/go-ldap-admin)](https://github.com/eryajf/go-ldap-admin)
[![Gin Version](https://img.shields.io/badge/Gin-1.6.3-brightgreen)](https://github.com/eryajf/go-ldap-admin)
[![Gorm Version](https://img.shields.io/badge/Gorm-1.20.12-brightgreen)](https://github.com/eryajf/go-ldap-admin)
[![Status](https://img.shields.io/badge/status-active-success.svg)](https://github.com/eryajf/go-ldap-admin)
[![GitHub Issues](https://img.shields.io/github/issues/eryajf/go-ldap-admin.svg)](https://github.com/eryajf/go-ldap-admin/issues)
[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/eryajf/go-ldap-admin)](https://github.com/eryajf/go-ldap-admin/pulls)
[![GitHub Pull Requests](https://img.shields.io/github/stars/eryajf/go-ldap-admin)](https://github.com/eryajf/go-ldap-admin/stargazers)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](/LICENSE)
</div>
---
<p align="center"> 🌉 基于Go+Vue实现的openLDAP后台管理项目。
<br>
</p>
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**目录**
- [在线体验](#%E5%9C%A8%E7%BA%BF%E4%BD%93%E9%AA%8C)
- [项目地址](#%E9%A1%B9%E7%9B%AE%E5%9C%B0%E5%9D%80)
- [核心功能](#%E6%A0%B8%E5%BF%83%E5%8A%9F%E8%83%BD)
- [快速开始](#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)
- [本地开发](#%E6%9C%AC%E5%9C%B0%E5%BC%80%E5%8F%91)
- [生产部署](#%E7%94%9F%E4%BA%A7%E9%83%A8%E7%BD%B2)
- [文档快链](#%E6%96%87%E6%A1%A3%E5%BF%AB%E9%93%BE)
- [感谢](#%E6%84%9F%E8%B0%A2)
- [另外](#%E5%8F%A6%E5%A4%96)
- [贡献者](#%E8%B4%A1%E7%8C%AE%E8%80%85)
- [加群](#%E5%8A%A0%E7%BE%A4)
- [为什么有这个项目](#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%9C%89%E8%BF%99%E4%B8%AA%E9%A1%B9%E7%9B%AE)
- [捐赠](#%E6%8D%90%E8%B5%A0)
- [贡献者](#%E8%B4%A1%E7%8C%AE%E8%80%85)
- [使用登记](#%E4%BD%BF%E7%94%A8%E7%99%BB%E8%AE%B0)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## 在线体验
> admin / 123456
提供在线体验地址如下:
演示地址:[http://demo-go-ldap-admin.eryajf.net](http://demo-go-ldap-admin.eryajf.net)
| 分类 | 地址 | 用户名 | 密码 |
| :-----------: | :----------------------------------------------------------: | :-----------------------: | ------ |
| go-ldap-admin | [http://demo-go-ldap-admin.eryajf.net](http://demo-go-ldap-admin.eryajf.net) | admin | 123456 |
| phpLdapAdmin | [http://demo-go-ldap-admin.eryajf.net:8091/](http://demo-go-ldap-admin.eryajf.net:8091/) | cn=admin,dc=eryajf,dc=net | 123456 |
在线环境可能不稳,如果遇到访问异常,或者数据错乱,请联系我进行修复。
## 项目地址
@ -41,151 +60,16 @@
| 后端 | https://github.com/eryajf/go-ldap-admin.git | https://gitee.com/eryajf-world/go-ldap-admin.git |
| 前端 | https://github.com/eryajf/go-ldap-admin-ui.git | https://gitee.com/eryajf-world/go-ldap-admin-ui.git |
## 核心功能
## 文档快链
- 基于 GIN WEB API 框架基于Casbin的 RBAC 访问控制模型JWT 认证Validator 参数校验
- 基于 GORM 的数据库存储
- 基于 go-ldap 库的主逻辑交互
- 用户管理
- 用户的增删改查
- 分组管理
- 分组的增删改查
- 分组内成员的管理
项目相关介绍,使用,最佳实践等相关内容,都会在官方文档呈现,如有疑问,请先阅读官方文档,以下列举以下常用快链。
## 快速开始
你可以通过docker-compose在本地快速拉起进行体验。
快速拉起的容器包括MySQL-5.7openLDAP-1.4.0phpldapadmin-0.9.0go-ldap-admin-servergo-ldap-admin-ui。
服务端口映射如下:
| Service | Port |
| :-----------: | :-------------------: |
| MySQL | `3307:3306` |
| openLDAP | `389:389` |
| phpldapadmin | `8091:80` |
| go-ldap-admin | `8090:80`,`8888:8888` |
拉起之前确认是否有与本地端口冲突的情况。
```
$ git clone https://github.com/eryajf/go-ldap-admin.git
$ cd docs/docker-compose
$ docker-compose up -d
```
当看到容器都正常运行之后可以在本地进行访问http://localhost:8090用户名/密码admin/123456
如果想要访问PhpLdapAdmin则可访问http://localhost:8091用户名/密码cn=admin,dc=eryajf,dc=net/123456
`登录页:`
![](http://t.eryajf.net/imgs/2022/05/17dbe07a137c9b4c.png)
`首页:`
![](http://t.eryajf.net/imgs/2022/05/b18c5fbf5ba0e6af.png)
`用户管理:`
![](http://t.eryajf.net/imgs/2022/05/f3ae695b703c00c8.png)
`分组管理:`
![](http://t.eryajf.net/imgs/2022/05/e49632b76a4327ec.png)
`分组内成员管理:`
![](http://t.eryajf.net/imgs/2022/05/f1732540ce0632de.png)
## 本地开发
### 前言准备
前提是已准备好MySQL与openLDAP本地开发建议直接通过docker拉起即可可参考文档[https://wiki.eryajf.net/pages/3a0d5f](https://wiki.eryajf.net/pages/3a0d5f)。
### 拉取代码
```
# 后端代码
$ git clone https://github.com/eryajf/go-ldap-admin.git
# 前端代码
$ git clone https://github.com/eryajf/go-ldap-admin-ui.git
```
后端目录结构:
```
├─config # viper读取配置
├─controller # controller层响应路由请求的方法
├─docs # 一些物料信息
├─logic # 主要的处理逻辑
├─middleware # 中间件
├─model # 结构体模型
├─public # 一些公共的,工具类的放在这里
├─routes # 所有路由
├─service # 整合与底层存储交互的方法
├─svc # 定义入参出参的结构体
└─test # 跑测试用的
```
### 更改配置
```
# 修改后端配置
$ cd go-ldap-admin
# 文件路径 config.yml
$ vim config.yml
# 根据自己本地的情况调整数据库以及openLDAP等配置信息。
```
### 启动服务
```
# 启动后端
$ cd go-ldap-admin
$ go mod tidy
$ go run main.go
$ make run
# 启动前端
$ cd go-ldap-admin-ui
$ git config --global url."https://".insteadOf git://
$ npm install --registry=http://registry.npmmirror.com
$ yarn dev
```
本地访问http://localhost:8090用户名/密码admin/密码是配置文件中openLDAP中admin的密码。
## 生产部署
生产环境单独部署通过Nginx代理服务配置如下
```nginx
server {
listen 80;
server_name demo-go-ldap-admin.eryajf.net;
root /data/www/web/dist;
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control 'no-store';
}
location /api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8888;
}
}
```
- [官网地址](http://ldapdoc.eryajf.net)
- [项目背景](http://ldapdoc.eryajf.net/pages/101948/)
- [快速开始](http://ldapdoc.eryajf.net/pages/706e78/)
- [功能概览](http://ldapdoc.eryajf.net/pages/7a40de/)
- [本地开发](http://ldapdoc.eryajf.net/pages/cb7497/)
- [生产部署](http://ldapdoc.eryajf.net/pages/5769c4/)
## 感谢
@ -209,6 +93,15 @@ server {
- 如果你还有其他想法或者需求欢迎在issue中交流
- 程序还有很多bug欢迎各位朋友一起协同共建
## 加群
如果想要加群交流,可通过搜索 eryajf 添加我的微信,备注 ldap 拉你进群。
## 捐赠
如果你觉得这个项目对你有帮助,你可以请作者喝杯咖啡 ☕️ [点我](http://ldapdoc.eryajf.net/pages/2b6725/)
## 贡献者
<!-- readme: collaborators,contributors -start -->
@ -231,29 +124,6 @@ server {
</table>
<!-- readme: collaborators,contributors -end -->
## 使用登记
## 加群
可通过搜索 eryajf 添加我的微信,备注 ldap 拉你进群。
## 为什么有这个项目
我曾经经历的公司强依赖openLDAP来作为企业内部员工管理的平台并通过openLDAP进行各平台的认证打通工作。
但成也萧何败也萧何给运维省力的同时ldap又是维护不够友好的。
在[godap](https://github.com/bradleypeabody/godap)项目中作者这样描述对ldap的感受
> The short version of the story goes like this: I hate LDAP. I used to love it. But I loved it for all the wrong reasons. LDAP is supported as an authentication solution by many different pieces of software. Aside from its de jure standard status, its wide deployment cements it as a de facto standard as well.
>
> However, just because it is a standard doesn't mean it is a great idea.
>
> I'll admit that given its age LDAP has had a good run. I'm sure its authors carefully considered how to construct the protocol and chose ASN.1 and its encoding with all of wellest of well meaning intentions.
>
> The trouble is that with today's Internet, LDAP is just a pain in the ass. You can't call it from your browser. It's not human readable or easy to debug. Tooling is often arcane and confusing. It's way more complicated than what is needed for most simple authentication-only uses. (Yes, I know there are many other uses than authentication - but it's often too complicated for those too.)
>
> Likely owing to the complexity of the protocol, there seems to be virtually no easy to use library to implement the server side of the LDAP protocol that isn't tied in with some complete directory server system; and certainly not in a language as easy to "make it work" as Go.
他说他对ldap又爱又恨因为ldap出现的最早许多的三方软件都兼容支持它它成了这方面的一个标准。但问题在于它对于维护者而言又是复杂麻烦的。就算是有Phpldapadmin这样的平台能够在浏览器维护但看到那样上古的界面以及复杂的交互逻辑仍旧能够把不少人劝退。
鉴于此我开发了这个现代化的openLDAP管理后台。
如果你所在公司使用了该项目,烦请在这里留下脚印,感谢支持🥳 [点我](https://github.com/eryajf/go-ldap-admin/issues/18)

View File

@ -85,24 +85,34 @@ email:
ldap:
# ldap服务器地址
ldap-url: ldap://localhost:389
# ldap服务器基础DN
ldap-base-dn: "dc=eryajf,dc=net"
# ldap管理员DN
ldap-admin-dn: "cn=admin,dc=eryajf,dc=net"
# ldap管理员密码
ldap-admin-pass: "123456"
# ldap用户OU
ldap-user-dn: "ou=people,dc=eryajf,dc=net"
ldap-group-dn: "ou=group,dc=eryajf,dc=net"
# ldap用户初始默认密码
ldap-user-init-password: "123456"
# 是否允许更改分组DN
ldap-group-name-modify: false
# 是否允许更改用户DN
ldap-user-name-modify: false
dingtalk:
#为了方便数据库存储防止第三方id重复故而增加一个前缀用于用户表和分组表中第三方id存储,加上此处配置的source字段进行区分来源判断唯一。长度不超过10.
#因为分组表不可能成为性能瓶颈,故而不再拆分到新的关系表去维护第三方信息,用户表设计同理
ding-talk-flag: "dingtalk"
# 使用之前是需要在钉钉开发者后台(https://open-dev.dingtalk.com/#/index) 创建一个小程序或应用.获取appkey和appsecretagentId
# 目前agent-id尚未使用先存着后续功能可能会用到
# 由于获取钉钉第一个部门的id默认为1故而这边需要配置一下钉钉的第一个部门的名称不去钉钉获取
ding-talk-app-key: "xxxxxx"
ding-talk-app-secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxx-vhIGL"
ding-talk-app-key: "xxxxxxxxxxxxxxx"
ding-talk-app-secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ding-talk-agent-id: "12121212"
ding-talk-root-ou-name: "钉钉首个部门的名称"
#为了方便数据库存储防止第三方id重复故而增加一个前缀用于用户表和分组表中第三方id存储,加上此处配置的source字段进行区分来源判断唯一。长度不超过10.
#因为分组表不可能成为性能瓶颈,故而不再拆分到新的关系表去维护第三方信息,用户表设计同理
ding-talk-id-source: "dingtalk"
ding-talk-user-init-password: "dingding@123"
# 是否开启定时同步钉钉的任务
ding-talk-enable-sync: false
wecom:
flag: "wecom"
feishu:
flag: "feishu"

View File

@ -16,15 +16,17 @@ import (
var Conf = new(config)
type config struct {
System *SystemConfig `mapstructure:"system" json:"system"`
Logs *LogsConfig `mapstructure:"logs" json:"logs"`
Mysql *MysqlConfig `mapstructure:"mysql" json:"mysql"`
Casbin *CasbinConfig `mapstructure:"casbin" json:"casbin"`
Jwt *JwtConfig `mapstructure:"jwt" json:"jwt"`
RateLimit *RateLimitConfig `mapstructure:"rate-limit" json:"rateLimit"`
Ldap *LdapConfig `mapstructure:"ldap" json:"ldap"`
Email *EmailConfig `mapstructure:"email" json:"email"`
DingTalk *DingTalkConfig `mapstructure:"dingtalk" json:"dingTalk"`
System *SystemConfig `mapstructure:"system" json:"system"`
Logs *LogsConfig `mapstructure:"logs" json:"logs"`
Mysql *MysqlConfig `mapstructure:"mysql" json:"mysql"`
Casbin *CasbinConfig `mapstructure:"casbin" json:"casbin"`
Jwt *JwtConfig `mapstructure:"jwt" json:"jwt"`
RateLimit *RateLimitConfig `mapstructure:"rate-limit" json:"rateLimit"`
Ldap *LdapConfig `mapstructure:"ldap" json:"ldap"`
Email *EmailConfig `mapstructure:"email" json:"email"`
DingTalk *DingTalkConfig `mapstructure:"dingtalk" json:"dingTalk"`
WeComConfig *WeComConfig `mapstructure:"wecom" json:"weCom"`
FeiShuConfig *FeiShuConfig `mapstructure:"feishu" json:"feiShu"`
}
// 设置读取配置信息
@ -75,7 +77,10 @@ func RSAReadKeyFromFile(filename string) []byte {
defer f.Close()
fileInfo, _ := f.Stat()
b = make([]byte, fileInfo.Size())
f.Read(b)
_, err = f.Read(b)
if err != nil {
return b
}
return b
}
@ -129,14 +134,14 @@ type RateLimitConfig struct {
}
type LdapConfig struct {
LdapUrl string `mapstructure:"ldap-url" json:"ldapUrl"`
LdapBaseDN string `mapstructure:"ldap-base-dn" json:"ldapBaseDN"`
LdapAdminDN string `mapstructure:"ldap-admin-dn" json:"ldapAdminDN"`
LdapAdminPass string `mapstructure:"ldap-admin-pass" json:"ldapAdminPass"`
LdapUserDN string `mapstructure:"ldap-user-dn" json:"ldapUserDN"`
LdapGroupDN string `mapstructure:"ldap-group-dn" json:"ldapGroupDN"`
LdapGroupNameModify bool `mapstructure:"ldap-group-name-modify" json:"ldapGroupNameModify"`
LdapUserNameModify bool `mapstructure:"ldap-user-name-modify" json:"ldapUserNameModify"`
LdapUrl string `mapstructure:"ldap-url" json:"ldapUrl"`
LdapBaseDN string `mapstructure:"ldap-base-dn" json:"ldapBaseDN"`
LdapAdminDN string `mapstructure:"ldap-admin-dn" json:"ldapAdminDN"`
LdapAdminPass string `mapstructure:"ldap-admin-pass" json:"ldapAdminPass"`
LdapUserDN string `mapstructure:"ldap-user-dn" json:"ldapUserDN"`
LdapUserInitPassword string `mapstructure:"ldap-user-init-password" json:"ldapUserInitPassword"`
LdapGroupNameModify bool `mapstructure:"ldap-group-name-modify" json:"ldapGroupNameModify"`
LdapUserNameModify bool `mapstructure:"ldap-user-name-modify" json:"ldapUserNameModify"`
}
type EmailConfig struct {
Host string `mapstructure:"host" json:"host"`
@ -147,11 +152,18 @@ type EmailConfig struct {
}
type DingTalkConfig struct {
DingTalkAppKey string `mapstructure:"ding-talk-app-key" json:"dingTalkAppKey"`
DingTalkAppSecret string `mapstructure:"ding-talk-app-secret" json:"dingTalkAppSecret"`
DingTalkAgentId string `mapstructure:"ding-talk-agent-id" json:"dingTalkAgentId"`
DingTalkRootOuName string `mapstructure:"ding-talk-root-ou-name" json:"dingTalkRootOuName"`
DingTalkIdSource string `mapstructure:"ding-talk-id-source" json:"dingTalkIdSource"`
DingTalkUserInitPassword string `mapstructure:"ding-talk-user-init-password" json:"dingTalkUserInitPassword"`
DingTalkEnableSync bool `mapstructure:"ding-talk-enable-sync" json:"dingTalkEnableSync"`
DingTalkAppKey string `mapstructure:"ding-talk-app-key" json:"dingTalkAppKey"`
DingTalkAppSecret string `mapstructure:"ding-talk-app-secret" json:"dingTalkAppSecret"`
DingTalkAgentId string `mapstructure:"ding-talk-agent-id" json:"dingTalkAgentId"`
DingTalkRootOuName string `mapstructure:"ding-talk-root-ou-name" json:"dingTalkRootOuName"`
DingTalkFlag string `mapstructure:"ding-talk-flag" json:"dingTalkFlag"`
DingTalkEnableSync bool `mapstructure:"ding-talk-enable-sync" json:"dingTalkEnableSync"`
}
type WeComConfig struct {
Flag string `mapstructure:"flag" json:"flag"`
}
type FeiShuConfig struct {
Flag string `mapstructure:"flag" json:"flag"`
}

View File

@ -2,7 +2,7 @@ package controller
import (
"github.com/eryajf/go-ldap-admin/logic"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/gin-gonic/gin"
)

View File

@ -2,7 +2,7 @@ package controller
import (
"github.com/eryajf/go-ldap-admin/logic"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/gin-gonic/gin"
)

View File

@ -2,7 +2,7 @@ package controller
import (
"github.com/eryajf/go-ldap-admin/logic"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/gin-gonic/gin"
)
@ -85,6 +85,6 @@ func (m *GroupController) RemoveUser(c *gin.Context) {
func (m *GroupController) SyncDingTalkDepts(c *gin.Context) {
req := new(request.SyncDingTalkDeptsReq)
Run(c, req, func() (interface{}, interface{}) {
return logic.DingTalk.DsyncDingTalkDepts(c, req)
return logic.DingTalk.SyncDingTalkDepts(c, req)
})
}

View File

@ -2,7 +2,7 @@ package controller
import (
"github.com/eryajf/go-ldap-admin/logic"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/gin-gonic/gin"
)

View File

@ -2,7 +2,7 @@ package controller
import (
"github.com/eryajf/go-ldap-admin/logic"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/gin-gonic/gin"
)

View File

@ -2,7 +2,7 @@ package controller
import (
"github.com/eryajf/go-ldap-admin/logic"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/gin-gonic/gin"
)

View File

@ -2,7 +2,7 @@ package controller
import (
"github.com/eryajf/go-ldap-admin/logic"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/gin-gonic/gin"
)

40
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/appleboy/gin-jwt/v2 v2.6.4
github.com/casbin/casbin/v2 v2.22.0
github.com/casbin/gorm-adapter/v3 v3.1.0
github.com/fsnotify/fsnotify v1.4.9
github.com/fsnotify/fsnotify v1.5.4
github.com/gin-gonic/gin v1.6.3
github.com/go-ldap/ldap/v3 v3.4.2
github.com/go-playground/locales v0.14.0
@ -15,7 +15,7 @@ require (
github.com/juju/ratelimit v1.0.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/robfig/cron/v3 v3.0.0
github.com/spf13/viper v1.7.1
github.com/spf13/viper v1.11.0
github.com/thoas/go-funk v0.7.0
github.com/zhaoyunxing92/dingtalk/v2 v2.0.7-0.20220601083444-173c10c3f835
go.uber.org/zap v1.19.1
@ -26,8 +26,14 @@ require (
)
require (
github.com/BurntSushi/toml v1.1.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/lib/pq v1.10.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.0 // indirect
github.com/rogpeppe/go-internal v1.8.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
require (
@ -39,7 +45,7 @@ require (
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.8.0 // indirect
@ -51,29 +57,29 @@ require (
github.com/jackc/pgx/v4 v4.10.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.1 // indirect
github.com/json-iterator/go v1.1.10 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.4 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mozillazg/go-pinyin v0.19.0
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/spf13/afero v1.5.1 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/ugorji/go/codec v1.2.3 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.25.0 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/postgres v1.0.7 // indirect
gorm.io/driver/sqlserver v1.0.6 // indirect

627
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,10 @@ import (
"fmt"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/model/response"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/gin-gonic/gin"
"github.com/thoas/go-funk"

View File

@ -4,11 +4,11 @@ import (
"fmt"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/model/response"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/ildap"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/gin-gonic/gin"
)

167
logic/common_login.go Normal file
View File

@ -0,0 +1,167 @@
package logic
import (
"fmt"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/ildap"
"github.com/eryajf/go-ldap-admin/service/isql"
)
func CommonAddGroup(group *model.Group) error {
// 先在ldap中创建组
err := ildap.Group.Add(group)
if err != nil {
return err
}
// 然后在数据库中创建组
err = isql.Group.Add(group)
if err != nil {
return err
}
// 默认创建分组之后需要将admin添加到分组中
adminInfo := new(model.User)
err = isql.User.Find(tools.H{"id": 1}, adminInfo)
if err != nil {
return err
}
err = isql.Group.AddUserToGroup(group, []model.User{*adminInfo})
if err != nil {
return err
}
return nil
}
func CommonUpdateGroup(oldGroup, newGroup *model.Group) error {
//若配置了不允许修改分组名称,则不更新分组名称
if !config.Conf.Ldap.LdapGroupNameModify {
newGroup.GroupName = oldGroup.GroupName
}
err := ildap.Group.Update(oldGroup, newGroup)
if err != nil {
return err
}
err = isql.Group.Update(newGroup)
if err != nil {
return err
}
return nil
}
func CommonAddUser(user *model.User, groupId []uint) error {
if user.Departments == "" {
user.Departments = "默认:研发中心"
}
if user.GivenName == "" {
user.GivenName = user.Nickname
}
if user.PostalAddress == "" {
user.PostalAddress = "默认:地球"
}
if user.Position == "" {
user.Position = "默认:技术"
}
if user.Introduction == "" {
user.Introduction = user.Nickname
}
// 先将用户添加到MySQL
err := isql.User.Add(user)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("向MySQL创建用户失败" + err.Error()))
}
// 再将用户添加到ldap
err = ildap.User.Add(user)
if err != nil {
return tools.NewLdapError(fmt.Errorf("AddUser向LDAP创建用户失败" + err.Error()))
}
// 获取用户将要添加的分组
groups, err := isql.Group.GetGroupByIds(groupId)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("根据部门ID获取部门信息失败" + err.Error()))
}
for _, group := range groups {
if group.GroupDN[:3] == "ou=" {
continue
}
// 先将用户和部门信息维护到MySQL
err := isql.Group.AddUserToGroup(group, []model.User{*user})
if err != nil {
return tools.NewMySqlError(fmt.Errorf("向MySQL添加用户到分组关系失败" + err.Error()))
}
//根据选择的部门,添加到部门内
err = ildap.Group.AddUserToGroup(group.GroupDN, user.UserDN)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("向Ldap添加用户到分组关系失败" + err.Error()))
}
}
return nil
}
func CommonUpdateUser(oldUser, newUser *model.User, groupId []uint) error {
// 更新用户
if !config.Conf.Ldap.LdapUserNameModify {
newUser.Username = oldUser.Username
}
err := ildap.User.Update(oldUser.Username, newUser)
if err != nil {
return tools.NewLdapError(fmt.Errorf("在LDAP更新用户失败" + err.Error()))
}
err = isql.User.Update(newUser)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("在MySQL更新用户失败" + err.Error()))
}
//判断部门信息是否有变化有变化则更新相应的数据库
oldDeptIds := tools.StringToSlice(oldUser.DepartmentId, ",")
addDeptIds, removeDeptIds := tools.ArrUintCmp(oldDeptIds, groupId)
// 先处理添加的部门
addgroups, err := isql.Group.GetGroupByIds(addDeptIds)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("根据部门ID获取部门信息失败" + err.Error()))
}
for _, group := range addgroups {
if group.GroupDN[:3] == "ou=" {
continue
}
// 先将用户和部门信息维护到MySQL
err := isql.Group.AddUserToGroup(group, []model.User{*newUser})
if err != nil {
return tools.NewMySqlError(fmt.Errorf("向MySQL添加用户到分组关系失败" + err.Error()))
}
//根据选择的部门,添加到部门内
err = ildap.Group.AddUserToGroup(group.GroupDN, newUser.UserDN)
if err != nil {
return tools.NewLdapError(fmt.Errorf("向Ldap添加用户到分组关系失败" + err.Error()))
}
}
// 再处理删除的部门
removegroups, err := isql.Group.GetGroupByIds(removeDeptIds)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("根据部门ID获取部门信息失败" + err.Error()))
}
for _, group := range removegroups {
if group.GroupDN[:3] == "ou=" {
continue
}
err := isql.Group.RemoveUserFromGroup(group, []model.User{*newUser})
if err != nil {
return tools.NewMySqlError(fmt.Errorf("在MySQL将用户从分组移除失败" + err.Error()))
}
err = ildap.Group.RemoveUserFromGroup(group.GroupDN, newUser.UserDN)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("在ldap将用户从分组移除失败" + err.Error()))
}
}
return nil
}

View File

@ -1,574 +0,0 @@
package logic
import (
"errors"
"fmt"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/ildap"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/gin-gonic/gin"
"github.com/mozillazg/go-pinyin"
"github.com/zhaoyunxing92/dingtalk/v2"
dingreq "github.com/zhaoyunxing92/dingtalk/v2/request"
"gorm.io/gorm"
"regexp"
"strconv"
"strings"
)
type DingTalkLogic struct {
}
//通过钉钉获取部门信息
func (d *DingTalkLogic) DsyncDingTalkDepts(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
client, err := dingtalk.NewClient(config.Conf.DingTalk.DingTalkAppKey, config.Conf.DingTalk.DingTalkAppSecret)
// 先存根部门信息到数据库和ldap钉钉根部门id为1ldap根部门名称为config.Conf.DingTalk.DingTalkRootOu
r := request.DingGroupAddReq{}
r.GroupName = config.Conf.DingTalk.DingTalkRootOuName
r.GroupType = "ou"
r.ParentId = 0
r.Remark = "钉钉根部门"
r.Source = config.Conf.DingTalk.DingTalkIdSource
r.SourceDeptId = fmt.Sprintf("%s_%s", config.Conf.DingTalk.DingTalkIdSource, "1")
r.SourceDeptParentId = fmt.Sprintf("%s_%s", config.Conf.DingTalk.DingTalkIdSource, "0")
group, err := d.AddDept(&r)
if err != nil {
return nil, fmt.Sprintf("新增部门失败:部门名称为:%s,钉钉部门id为%d,错误信息:%s", r.GroupName, r.SourceDeptId, err.Error())
}
// 获取根部门下的部门信息,进行处理
reqDept := &dingreq.DeptList{}
reqDept.DeptId = 1
reqDept.Language = "zh_CN"
err = d.GetSubDepts(client, reqDept, group.ID, r.Source)
if err != nil {
return nil, fmt.Sprintf("DsyncDingTalkDepts同步部门出错%s", err.Error())
}
return nil, nil
}
// 通过钉钉获取部门信息,并存入数据库
func (d *DingTalkLogic) GetSubDepts(client *dingtalk.DingTalk, req *dingreq.DeptList, pgId uint, source string) error {
// 获取子部门列表
depts, err := client.GetDeptList(req)
if err != nil {
return errors.New(fmt.Sprintf("GetSubDepts获取部门列表失败%s", err.Error()))
}
fmt.Println("GetSubDepts获取到的钉钉部门列表", depts)
// 遍历并处理当前部门信息
for _, dept := range depts.Depts {
//先判断分组类型,默认为cn方便应对钉钉动态调整原本没有成员的部门加入成员后导致我们无法增加
localDept := request.DingGroupAddReq{
GroupType: "cn",
ParentId: pgId,
GroupName: dept.Name,
Remark: dept.Name,
Source: config.Conf.DingTalk.DingTalkIdSource,
SourceDeptParentId: fmt.Sprintf("%s_%d", source, dept.ParentId),
SourceDeptId: fmt.Sprintf("%s_%d", source, dept.Id),
SourceUserNum: 0,
}
//获取钉钉方若部门存在人员信息则设置为cn类型
//reqTemp := &dingreq.DeptUserId{}
//reqTemp.DeptId = dept.Id
//repTemp, err := client.GetDeptUserIds(reqTemp)
//if err != nil {
// return errors.New(fmt.Sprintf("GetSubDepts获取部门用户Id列表失败%s", err.Error()))
//}
//fmt.Println("钉钉部门人员列表:", repTemp)
//if len(repTemp.UserIds) > 0 {
// localDept.GroupType = "cn"
// localDept.SourceUserNum = len(repTemp.UserIds)
//}
// 处理部门入库
deptTemp, err := d.AddDept(&localDept)
if err != nil {
return errors.New(fmt.Sprintf("GetSubDepts添加部门入库失败%s", err.Error()))
}
// 递归调用
sub := &dingreq.DeptList{}
sub.DeptId = dept.Id
sub.Language = "zh_CN"
d.GetSubDepts(client, sub, deptTemp.ID, deptTemp.Source)
}
return nil
}
//根据现有数据库同步到的部门信息,开启用户同步
func (d DingTalkLogic) SyncDingTalkUsers(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
client, err := dingtalk.NewClient(config.Conf.DingTalk.DingTalkAppKey, config.Conf.DingTalk.DingTalkAppSecret)
//获取数据库里面的钉钉同步过来的部门信息
r := request.GroupListAllReq{}
r.GroupType = "cn"
r.Source = config.Conf.DingTalk.DingTalkIdSource
depts, err := isql.Group.ListAll(&r)
if err != nil {
return nil, fmt.Sprintf("SyncDingTalkUsers查询本地部门列表失败", err.Error())
}
//遍历处理部门,获取钉钉对应的用户信息
for index, dept := range depts {
fmt.Println(fmt.Sprintf("当前进行的步调为:%d,部门名称为:%s", index, dept.GroupName))
err = d.AddDeptUser(client, dept, 0)
if err != nil {
return nil, fmt.Sprintf("SyncDingTalkUsers添加部门下用户失败", err.Error())
}
}
return nil, nil
}
//获取并处理钉钉部门下的用户信息入库
func (d DingTalkLogic) AddDeptUser(client *dingtalk.DingTalk, dept *model.Group, cursor int) error {
// 处理部门下的人员信息
deptId := strings.Split(dept.SourceDeptId, "_")
tempId, err := strconv.Atoi(deptId[1])
if err != nil {
return err
}
//方式一获取部门下用户信息一次100个遍历后插入数据库经过验证第三方依赖包有问题
r := dingreq.DeptDetailUserInfo{}
r.DeptId = tempId
r.Language = "zh_CN"
r.Cursor = cursor
r.Size = 100
//获取钉钉部门人员信息
rep, err := client.GetDeptDetailUserInfo(&r)
fmt.Println(fmt.Sprintf("当前获取的部门名称为:%s,总用户量为:%d", dept.GroupName, len(rep.DeptDetailUsers)))
if err != nil {
return errors.New(fmt.Sprintf("AddDeptUser获取钉钉部门人员信息失败%s", err.Error()))
}
//方式二临时处理方案获取部门用户id列表遍历挨个从钉钉获取用户信息
//dingr := dingreq.DeptUserId{}
//dingr.DeptId = tempId
//deptUserIds, err := client.GetDeptUserIds(&dingr)
//if err != nil {
// return errors.New(fmt.Sprintf("AddDeptUser通过用户部门id从钉钉获取用户id列表失败:%s", err.Error()))
//}
// 遍历并处理当前部门下的人员信息
for _, detail := range rep.DeptDetailUsers {
//for index, userId := range deptUserIds.UserIds {
// fmt.Println(fmt.Sprintf("获取到的部门用户数为:%d,正在处理的用户序号为:%d,总Ids为", len(deptUserIds.UserIds), index))
// fmt.Println(deptUserIds.UserIds)
// userReq := dingreq.UserDetail{}
// userReq.UserId = userId
// userReq.Language = "zh_CN"
// detail, err := client.GetUserDetail(&userReq)
// if err != nil {
// return errors.New(fmt.Sprintf("AddDeptUser通过用户id从钉钉获取用户详情失败:%s", err.Error()))
// }
// 获取人员信息
fmt.Println("钉钉人员详情:", detail)
userName := ""
if detail.OrgEmail != "" {
emailstr := strings.Split(detail.OrgEmail, "@")
userName = emailstr[0]
}
if userName == "" && detail.Name != "" {
name := pinyin.LazyConvert(detail.Name, nil)
userName = strings.Join(name, "")
}
if userName == "" && detail.Mobile != "" {
userName = detail.Mobile
}
if detail.JobNumber == "" {
detail.JobNumber = userName
}
//钉钉部门ids,转换为内部部门id
sourceDeptIds := []string{}
for _, deptId := range detail.DeptIds {
sourceDeptIds = append(sourceDeptIds, fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkIdSource, deptId))
}
groupIds, err := isql.Group.DingTalkDeptIdsToGroupIds(sourceDeptIds)
if err != nil {
return errors.New(fmt.Sprintf("AddDeptUser转换钉钉部门id到本地分组id出错%s", err.Error()))
}
user := request.DingUserAddReq{
Username: userName,
Password: config.Conf.DingTalk.DingTalkUserInitPassword,
Nickname: detail.Name,
GivenName: detail.Name,
Mail: detail.OrgEmail,
JobNumber: detail.JobNumber,
Mobile: detail.Mobile,
Avatar: detail.Avatar,
PostalAddress: detail.WorkPlace,
Departments: dept.GroupName,
Position: detail.Title,
Introduction: detail.Remark,
Status: 1,
DepartmentId: groupIds,
Source: config.Conf.DingTalk.DingTalkIdSource,
SourceUserId: fmt.Sprintf("%s_%s", config.Conf.DingTalk.DingTalkIdSource, detail.UserId),
SourceUnionId: fmt.Sprintf("%s_%s", config.Conf.DingTalk.DingTalkIdSource, detail.UnionId),
}
// 入库
repUser, err := d.AddUser(&user)
if err != nil {
return errors.New(fmt.Sprintf("AddDeptUser添加用户失败%s", err.Error()))
}
fmt.Println("入库成功,用户信息为:")
fmt.Println(repUser)
}
if rep.HasMore {
err = d.AddDeptUser(client, dept, rep.NextCursor)
if err != nil {
return errors.New(fmt.Sprintf("AddDeptUser添加用户失败%s", err.Error()))
}
}
return nil
}
// AddGroup 添加部门数据
func (d DingTalkLogic) AddDept(r *request.DingGroupAddReq) (data *model.Group, rspError error) {
// 判断部门名称是否存在
filter := tools.H{"source_dept_id": r.SourceDeptId}
dept := new(model.Group)
err := isql.Group.Find(filter, dept)
flag := errors.Is(err, gorm.ErrRecordNotFound)
fmt.Println("部门是否存在:", filter, flag)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New(fmt.Sprintf("AddDept添加部门失败%s", err.Error()))
}
//分组不存在直接创建此处通过部门名称和第三方id来共同判定唯一理论上不会出现重复
if errors.Is(err, gorm.ErrRecordNotFound) {
group := model.Group{
GroupType: r.GroupType,
ParentId: r.ParentId,
GroupName: r.GroupName,
Remark: r.Remark,
Creator: "system",
Source: r.Source,
SourceDeptParentId: r.SourceDeptParentId,
SourceDeptId: r.SourceDeptId,
SourceUserNum: r.SourceUserNum,
}
pdn := ""
if group.ParentId > 0 {
pdn, err = isql.Group.GetGroupDn(r.ParentId, "")
if err != nil {
return nil, errors.New(fmt.Sprintf("AddDept获取父级部门dn失败%s", err.Error()))
}
}
err = ildap.Group.Add(&group, pdn)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("AddDept向LDAP创建分组失败" + err.Error()))
}
// 创建
err = isql.Group.Add(&group)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("AddDept向MySQL创建分组失败:" + err.Error()))
}
// 默认创建分组之后需要将admin添加到分组中
adminInfo := new(model.User)
err = isql.User.Find(tools.H{"id": 1}, adminInfo)
if err != nil {
return nil, errors.New(fmt.Sprintf("AddDept获取admin用户失败%s", tools.NewMySqlError(err).Error()))
}
err = isql.Group.AddUserToGroup(&group, []model.User{*adminInfo})
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("AddDept添加用户到分组失败: %s", err.Error()))
}
return &group, nil
} else { //分组存在
//判断是否名字/备注/钉钉部门ID有修改
if r.GroupName != dept.GroupName || r.Remark != dept.Remark || r.SourceDeptParentId != dept.SourceDeptParentId || r.SourceUserNum != dept.SourceUserNum {
err = d.UpdateDept(r)
if err != nil {
return nil, errors.New(fmt.Sprintf("AddDept更新部门失败%s", err.Error()))
}
}
//处理父级部门变化
if r.SourceDeptParentId != dept.SourceDeptParentId {
// TODO 待处理父级部门变化情况
}
return dept, nil
}
}
// UpdateDept 更新部门数据
func (d DingTalkLogic) UpdateDept(r *request.DingGroupAddReq) error {
oldData := new(model.Group)
filter := tools.H{"source_dept_id": r.SourceDeptId}
err := isql.Group.Find(filter, oldData)
if err != nil {
return errors.New(fmt.Sprintf("UpdateDept获取旧的部门信息失败:%s", tools.NewMySqlError(err).Error()))
}
dept := model.Group{
Model: oldData.Model,
GroupName: r.GroupName,
Remark: r.Remark,
Creator: "system",
GroupType: oldData.GroupType,
SourceDeptId: r.SourceDeptId,
SourceDeptParentId: r.SourceDeptParentId,
SourceUserNum: r.SourceUserNum,
}
oldGroupName := oldData.GroupName
oldRemark := oldData.Remark
dn, err := isql.Group.GetGroupDn(oldData.ID, "")
if err != nil {
return errors.New(fmt.Sprintf("UpdateDept不去部门dn失败:%s", tools.NewMySqlError(err).Error()))
}
err = ildap.Group.Update(&dept, dn, oldGroupName, oldRemark)
if err != nil {
return tools.NewLdapError(fmt.Errorf("UpdateDept向LDAP更新分组失败" + err.Error()))
}
//若配置了不允许修改分组名称,则不更新分组名称
if !config.Conf.Ldap.LdapGroupNameModify {
dept.GroupName = oldGroupName
}
err = isql.Group.Update(&dept)
if err != nil {
return tools.NewLdapError(fmt.Errorf("UpdateDept向MySQL更新分组失败:" + err.Error()))
}
return nil
}
// AddUser 添加用户数据
func (d DingTalkLogic) AddUser(r *request.DingUserAddReq) (data *model.User, rspError error) {
// 兼容处理钉钉异常人员信息若usernamemailmobile都没有的直接跳过
if r.Username == "" && r.Mail == "" && r.Mobile == "" {
emptyData := new(model.User)
emptyData.Introduction = fmt.Sprintf("此用户:%susernamemailmobile皆为空跳过入库请手动置后台添加", r.Nickname)
emptyData.Nickname = r.Nickname
emptyData.SourceUserId = r.SourceUserId
emptyData.Source = r.Source
emptyData.GivenName = r.GivenName
return emptyData, nil
}
isExist := false
oldData := new(model.User)
if isql.User.Exist(tools.H{"source_user_id": r.SourceUserId}) {
err := isql.User.Find(tools.H{"source_user_id": r.SourceUserId}, oldData)
if err != nil {
return nil, errors.New(fmt.Sprintf("AddUser根据钉钉用户id获取用户失败%s", err.Error()))
}
isExist = true
}
if !isExist {
if isql.User.Exist(tools.H{"source_union_id": r.SourceUnionId}) {
err := isql.User.Find(tools.H{"source_union_id": r.SourceUnionId}, oldData)
if err != nil {
return nil, errors.New(fmt.Sprintf("AddUser根据钉钉用户unionid获取用户失败%s", err.Error()))
}
isExist = true
}
}
//if !isExist {
// if r.Mail != "" && isql.User.Exist(tools.H{"mail": r.Mail}) {
// err := isql.User.Find(tools.H{"mail": r.Mail}, oldData)
// if err != nil {
// return nil, errors.New(fmt.Sprintf("AddUser根据钉钉用户mail获取用户失败%s", err.Error()))
// }
// isExist = true
// }
//}
if !isExist {
if isql.User.Exist(tools.H{"job_number": r.JobNumber}) {
err := isql.User.Find(tools.H{"job_number": r.JobNumber}, oldData)
if err != nil {
return nil, errors.New(fmt.Sprintf("AddUser根据钉钉用户job_number获取用户失败%s", err.Error()))
}
isExist = true
}
}
if !isExist {
if isql.User.Exist(tools.H{"mobile": r.Mobile}) {
err := isql.User.Find(tools.H{"mobile": r.Mobile}, oldData)
if err != nil {
return nil, errors.New(fmt.Sprintf("AddUser根据钉钉用户mobile获取用户失败%s", err.Error()))
}
isExist = true
}
}
if !isExist {
//组装用户名
//先根据钉钉唯一id获取查看数据库中是否存在
//不存在,则根据用户名 like 用户名获取尾号最大的账号
//重新设定用户名
userData := new(model.User)
err := isql.User.FindTheSameUserName(r.Username, userData)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
} else {
// 找到重名用户,
re := regexp.MustCompile("[0-9]+")
num := re.FindString(userData.Username)
n := 1
if num != "" {
m, err := strconv.Atoi(num)
if err != nil {
return
}
n = m + 1
}
r.Username = fmt.Sprintf("%s%d", r.Username, n)
}
}
if isExist {
r.Username = oldData.Username
user, err := d.UpdateUser(r, oldData)
if err != nil {
return nil, errors.New(fmt.Sprintf("AddUser用户已存在更新用户失败%s", err.Error()))
}
return user, nil
}
// 根据角色id获取角色
r.RoleIds = []uint{2} // 默认添加为普通用户角色
roles, err := isql.Role.GetRolesByIds(r.RoleIds)
if err != nil {
return nil, tools.NewValidatorError(fmt.Errorf("AddUser根据角色ID获取角色信息失败:%s", err.Error()))
}
var reqRoleSorts []int
for _, role := range roles {
reqRoleSorts = append(reqRoleSorts, int(role.Sort))
}
deptIds := tools.SliceToString(r.DepartmentId, ",")
user := model.User{
Username: r.Username,
Password: r.Password,
Nickname: r.Nickname,
GivenName: r.GivenName,
Mail: r.Mail,
JobNumber: r.JobNumber,
Mobile: r.Mobile,
Avatar: r.Avatar,
PostalAddress: r.PostalAddress,
Departments: r.Departments,
Position: r.Position,
Introduction: r.Introduction,
Status: r.Status,
Creator: "system",
DepartmentId: deptIds,
Roles: roles,
Source: r.Source,
SourceUserId: r.SourceUserId,
SourceUnionId: r.SourceUnionId,
}
if user.Introduction == "" {
user.Introduction = r.Nickname
}
if user.JobNumber == "" {
user.JobNumber = r.Mobile
}
//先识别用户选择的部门是否是OU开头
gdns := make(map[uint]string)
for _, deptId := range r.DepartmentId {
dn, err := isql.Group.GetGroupDn(deptId, "")
if err != nil {
return nil, errors.New(fmt.Sprintf("AddUser根据用户dn信息失败:%s", err.Error()))
}
gdn := fmt.Sprintf("%s,%s", dn, config.Conf.Ldap.LdapBaseDN)
if gdn[:3] == "ou=" {
return nil, errors.New(fmt.Sprintf("AddUser不能添加用户到OU组织单元:%s", gdn))
}
gdns[deptId] = gdn
}
//先创建用户到默认分组
err = ildap.User.Add(&user)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("AddUser向LDAP创建用户失败" + err.Error()))
}
isExistUser := false
for deptId, gdn := range gdns {
//根据选择的部门,添加到部门内
err = ildap.Group.AddUserToGroup(gdn, fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN))
if err != nil {
return nil, errors.New(fmt.Sprintf("AddUser向部门添加用户失败%s", err.Error()))
}
if !isExistUser {
err = isql.User.Add(&user)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("向MySQL创建用户失败" + err.Error()))
}
isExistUser = true
}
//根据部门分配,将用户和部门信息维护到部门关系表里面
users := []model.User{}
users = append(users, user)
depart := new(model.Group)
filter := tools.H{"id": deptId}
err = isql.Group.Find(filter, depart)
if err != nil {
return nil, tools.NewMySqlError(err)
}
err = isql.Group.AddUserToGroup(depart, users)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("AddUser向MySQL添加用户到分组关系失败" + err.Error()))
}
}
return &user, nil
}
// Update 更新数据
func (d DingTalkLogic) UpdateUser(r *request.DingUserAddReq, oldData *model.User) (data *model.User, rspError error) {
deptIds := tools.SliceToString(r.DepartmentId, ",")
user := model.User{
Model: oldData.Model,
Username: r.Username,
Nickname: r.Nickname,
GivenName: r.GivenName,
Mail: r.Mail,
JobNumber: r.JobNumber,
Mobile: r.Mobile,
Avatar: r.Avatar,
PostalAddress: r.PostalAddress,
Departments: r.Departments,
Position: r.Position,
Introduction: r.Introduction,
Creator: "system",
DepartmentId: deptIds,
Source: oldData.Source,
Roles: oldData.Roles,
SourceUserId: r.SourceUserId,
SourceUnionId: r.SourceUnionId,
}
if user.Introduction == "" {
user.Introduction = r.Nickname
}
if user.PostalAddress == "" {
user.PostalAddress = "没有填写地址"
}
if user.Position == "" {
user.Position = "技术"
}
if user.JobNumber == "" {
user.JobNumber = r.Mobile
}
err := ildap.User.Update(oldData.Username, &user)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("UpdateUser在LDAP更新用户失败" + err.Error()))
}
// 更新用户
if !config.Conf.Ldap.LdapUserNameModify {
user.Username = oldData.Username
}
err = isql.User.Update(&user)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("UpdateUser在MySQL更新用户失败" + err.Error()))
}
//判断部门信息是否有变化有变化则更新相应的数据库
oldDeptIds := tools.StringToSlice(oldData.DepartmentId, ",")
addDeptIds, removeDeptIds := tools.ArrUintCmp(oldDeptIds, r.DepartmentId)
for _, deptId := range removeDeptIds {
//从旧组中删除
err = User.RemoveUserToGroup(deptId, []uint{oldData.ID})
if err != nil {
return nil, errors.New(fmt.Sprintf("UpdateUser将用户从分组移除失败%s", err.Error()))
}
}
for _, deptId := range addDeptIds {
//添加到新分组中
err = User.AddUserToGroup(deptId, []uint{oldData.ID})
if err != nil {
return nil, errors.New(fmt.Sprintf("UpdateUser将用户添加至分组失败%s", err.Error()))
}
}
return &user, nil
}

268
logic/dingtalk_logic.go Normal file
View File

@ -0,0 +1,268 @@
package logic
import (
"fmt"
"strings"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/public/client/dingtalk"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/ildap"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/gin-gonic/gin"
"github.com/mozillazg/go-pinyin"
)
type DingTalkLogic struct {
}
// TODO: 目前同步没毛病,只有更新还需要再琢磨琢磨
//通过钉钉获取部门信息
func (d *DingTalkLogic) SyncDingTalkDepts(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
// 1.获取所有部门
depts, err := dingtalk.GetDingTalkAllDepts(1)
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("获取钉钉部门列表失败:%s", err.Error()))
}
// 2.将部门这个数组进行拆分一组是父ID为1的一组是父ID不为1的
var firstDepts []*dingtalk.DingTalkDept // 父ID为1的部门
var otherDepts []*dingtalk.DingTalkDept // 父ID不为1的部门
for _, dept := range depts {
if dept.ParentId == 1 {
firstDepts = append(firstDepts, dept)
} else {
otherDepts = append(otherDepts, dept)
}
}
// 3.先写父ID为1的再写父ID不为1的
for _, dept := range firstDepts {
err := d.AddDepts(&request.DingGroupAddReq{
GroupType: "cn",
GroupName: dept.Name,
Remark: dept.Remark,
SourceDeptId: fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkFlag, dept.Id),
Source: config.Conf.DingTalk.DingTalkFlag,
SourceDeptParentId: fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkFlag, 1),
})
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("DsyncDingTalkDepts添加根部门失败%s", err.Error()))
}
}
for _, dept := range otherDepts {
err := d.AddDepts(&request.DingGroupAddReq{
GroupType: "cn",
GroupName: dept.Name,
Remark: dept.Remark,
SourceDeptId: fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkFlag, dept.Id),
Source: config.Conf.DingTalk.DingTalkFlag,
SourceDeptParentId: fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkFlag, dept.ParentId),
})
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("DsyncDingTalkDepts添加根部门失败%s", err.Error()))
}
}
return nil, nil
}
// AddGroup 添加部门数据
func (d DingTalkLogic) AddDepts(r *request.DingGroupAddReq) error {
// 判断部门名称是否存在
parentGroup := new(model.Group)
err := isql.Group.Find(tools.H{"source_dept_id": r.SourceDeptParentId}, parentGroup)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("查询父级部门失败:%s", err.Error()))
}
if !isql.Group.Exist(tools.H{"source_dept_id": r.SourceDeptId}) {
groupTmp := model.Group{
GroupName: r.GroupName,
Remark: r.Remark,
Creator: "system",
GroupType: "cn",
ParentId: parentGroup.ID,
SourceDeptId: r.SourceDeptId,
Source: r.Source,
SourceDeptParentId: r.SourceDeptParentId,
GroupDN: fmt.Sprintf("cn=%s,%s", r.GroupName, parentGroup.GroupDN),
}
err = CommonAddGroup(&groupTmp)
if err != nil {
return tools.NewOperationError(fmt.Errorf("添加部门失败:%s", err.Error()))
}
}
// todo: 分组存在,但是信息有变更的情况,需要考量,但是这种组织架构的调整,通常是比较复杂的情况,这里并不好与之一一对应同步,暂时不做支持
return nil
}
//根据现有数据库同步到的部门信息,开启用户同步
func (d DingTalkLogic) SyncDingTalkUsers(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
// 1.获取钉钉用户列表
users, err := dingtalk.GetDingTalkAllUsers()
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("SyncDingTalkUsers获取钉钉用户列表失败%s", err.Error()))
}
// 2.遍历用户,开始写入
for _, detail := range users {
// 用户名的几种情况
var userName string
if detail.OrgEmail != "" {
userName = strings.Split(detail.OrgEmail, "@")[0]
}
if userName == "" && detail.Name != "" {
userName = strings.Join(pinyin.LazyConvert(detail.Name, nil), "")
}
if userName == "" && detail.Mobile != "" {
userName = detail.Mobile
}
if userName == "" && detail.Email != "" {
userName = strings.Split(detail.Email, "@")[0]
}
if detail.OrgEmail == "" {
detail.OrgEmail = detail.Email
}
// 如果企业内没有工号,则工号用名字占位
if detail.JobNumber == "" {
detail.JobNumber = detail.Mobile
}
//钉钉部门ids,转换为内部部门id
var sourceDeptIds []string
for _, deptId := range detail.DeptIds {
sourceDeptIds = append(sourceDeptIds, fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkFlag, deptId))
}
groupIds, err := isql.Group.DingTalkDeptIdsToGroupIds(sourceDeptIds)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("SyncDingTalkUsers获取钉钉部门ids转换为内部部门id失败%s", err.Error()))
}
// 写入用户
user := request.DingUserAddReq{
Username: userName,
Password: config.Conf.Ldap.LdapUserInitPassword,
Nickname: detail.Name,
GivenName: detail.Name,
Mail: detail.OrgEmail,
JobNumber: detail.JobNumber,
Mobile: detail.Mobile,
Avatar: detail.Avatar,
PostalAddress: detail.WorkPlace,
// Departments: dept.GroupName,
Position: detail.Title,
Introduction: detail.Remark,
Status: 1,
DepartmentId: groupIds,
Source: config.Conf.DingTalk.DingTalkFlag,
SourceUserId: fmt.Sprintf("%s_%s", config.Conf.DingTalk.DingTalkFlag, detail.UserId),
SourceUnionId: fmt.Sprintf("%s_%s", config.Conf.DingTalk.DingTalkFlag, detail.UnionId),
}
// 入库
err = d.AddUsers(&user)
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("SyncDingTalkUsers写入用户失败%s", err.Error()))
}
}
// 3.获取钉钉已离职用户id列表
userIds, err := dingtalk.GetDingTalkLeaveUserIds()
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("SyncDingTalkUsers获取钉钉离职用户列表失败%s", err.Error()))
}
// 4.遍历id开始处理
for _, uid := range userIds {
user := new(model.User)
err = isql.User.Find(tools.H{"source_user_id": fmt.Sprintf("%s_%s", config.Conf.DingTalk.DingTalkFlag, uid)}, user)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("在MySQL查询用户失败: " + err.Error()))
}
// 先从ldap删除用户
err = ildap.User.Delete(user.UserDN)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("在LDAP删除用户失败" + err.Error()))
}
// 然后更新MySQL中用户状态
err = isql.User.ChangeStatus(int(user.ID), 2)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("在MySQL更新用户状态失败: " + err.Error()))
}
}
return nil, nil
}
// AddUser 添加用户数据
func (d DingTalkLogic) AddUsers(r *request.DingUserAddReq) error {
// 根据 unionid 查询用户,不存在则创建
if !isql.User.Exist(tools.H{"source_union_id": r.SourceUnionId}) {
// 根据角色id获取角色
r.RoleIds = []uint{2} // 默认添加为普通用户角色
roles, err := isql.Role.GetRolesByIds(r.RoleIds)
if err != nil {
return tools.NewValidatorError(fmt.Errorf("根据角色ID获取角色信息失败:%s", err.Error()))
}
deptIds := tools.SliceToString(r.DepartmentId, ",")
user := model.User{
Username: r.Username,
Password: r.Password,
Nickname: r.Nickname,
GivenName: r.GivenName,
Mail: r.Mail,
JobNumber: r.JobNumber,
Mobile: r.Mobile,
Avatar: r.Avatar,
PostalAddress: r.PostalAddress,
Departments: r.Departments,
Position: r.Position,
Introduction: r.Introduction,
Status: r.Status,
Creator: "system",
DepartmentId: deptIds,
Roles: roles,
Source: r.Source,
SourceUserId: r.SourceUserId,
SourceUnionId: r.SourceUnionId,
UserDN: fmt.Sprintf("uid=%s,%s", r.Username, config.Conf.Ldap.LdapUserDN),
}
err = CommonAddUser(&user, r.DepartmentId)
if err != nil {
return err
}
}
// todo: 用户如果存在则暂时跳过目前用户名取自邮箱等内容因为这个不确定性可能会造成一些逻辑上的问题因为默认情况下用户名是无法在ldap中更改的所以暂时跳过如果用户有这里的需求可以根据自己的情况固定用户名的字段也就可以打开如下的注释了
// else {
// oldData := new(model.User)
// if err := isql.User.Find(tools.H{"source_union_id": r.SourceUnionId}, oldData); err != nil {
// return err
// }
// if r.Username != oldData.Username || r.Mail != oldData.Mail || r.Mobile != oldData.Mobile {
// user := model.User{
// Model: oldData.Model,
// Username: r.Username,
// Nickname: r.Nickname,
// GivenName: r.GivenName,
// Mail: r.Mail,
// JobNumber: r.JobNumber,
// Mobile: r.Mobile,
// Avatar: r.Avatar,
// PostalAddress: r.PostalAddress,
// Departments: r.Departments,
// Position: r.Position,
// Introduction: r.Introduction,
// Creator: oldData.Creator,
// DepartmentId: tools.SliceToString(r.DepartmentId, ","),
// Source: oldData.Source,
// Roles: oldData.Roles,
// UserDN: oldData.UserDN,
// }
// if err := CommonUpdateUser(oldData, &user, r.DepartmentId); err != nil {
// return err
// }
// }
// }
return nil
}

View File

@ -7,11 +7,11 @@ import (
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/model/response"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/ildap"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/gin-gonic/gin"
)
@ -37,28 +37,35 @@ func (l GroupLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspE
}
group := model.Group{
GroupType: r.GroupType,
ParentId: r.ParentId,
GroupName: r.GroupName,
Remark: r.Remark,
Creator: ctxUser.Username,
Source: "platform", //默认是平台添加
SourceDeptId: "platform_0",
SourceDeptParentId: "platform_0",
GroupType: r.GroupType,
ParentId: r.ParentId,
GroupName: r.GroupName,
Remark: r.Remark,
Creator: ctxUser.Username,
Source: "platform", //默认是平台添加
}
pdn := ""
if group.ParentId > 0 {
pdn, err = isql.Group.GetGroupDn(r.ParentId, "")
if r.ParentId == 0 {
group.SourceDeptId = "platform_0"
group.SourceDeptParentId = "platform_0"
group.GroupDN = fmt.Sprintf("%s=%s,%s", r.GroupType, r.GroupName, config.Conf.Ldap.LdapBaseDN)
} else {
parentGroup := new(model.Group)
err := isql.Group.Find(tools.H{"id": r.ParentId}, parentGroup)
if err != nil {
return nil, err.Error()
return nil, tools.NewMySqlError(fmt.Errorf("获取父级组信息失败"))
}
group.SourceDeptId = "platform_0"
group.SourceDeptParentId = fmt.Sprintf("%s_%d", parentGroup.Source, r.ParentId)
group.GroupDN = fmt.Sprintf("%s=%s,%s", r.GroupType, r.GroupName, parentGroup.GroupDN)
}
err = ildap.Group.Add(&group, pdn)
// 先在ldap中创建组
err = ildap.Group.Add(&group)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP创建分组失败" + err.Error()))
}
// 创建
// 然后在数据库中创建
err = isql.Group.Add(&group)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向MySQL创建分组失败"))
@ -146,35 +153,30 @@ func (l GroupLogic) Update(c *gin.Context, req interface{}) (data interface{}, r
return nil, tools.NewMySqlError(fmt.Errorf("获取当前登陆用户失败"))
}
oldData := new(model.Group)
err = isql.Group.Find(filter, oldData)
oldGroup := new(model.Group)
err = isql.Group.Find(filter, oldGroup)
if err != nil {
return nil, tools.NewMySqlError(err)
}
group := model.Group{
Model: oldData.Model,
newGroup := model.Group{
Model: oldGroup.Model,
GroupName: r.GroupName,
Remark: r.Remark,
Creator: ctxUser.Username,
GroupType: oldData.GroupType,
GroupType: oldGroup.GroupType,
}
oldGroupName := oldData.GroupName
oldRemark := oldData.Remark
dn, err := isql.Group.GetGroupDn(r.ID, "")
if err != nil {
return nil, err.Error()
//若配置了不允许修改分组名称,则不更新分组名称
if !config.Conf.Ldap.LdapGroupNameModify {
newGroup.GroupName = oldGroup.GroupName
}
err = ildap.Group.Update(&group, dn, oldGroupName, oldRemark)
err = ildap.Group.Update(oldGroup, &newGroup)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP更新分组失败" + err.Error()))
}
//若配置了不允许修改分组名称,则不更新分组名称
if !config.Conf.Ldap.LdapGroupNameModify {
group.GroupName = oldGroupName
}
err = isql.Group.Update(&group)
err = isql.Group.Update(&newGroup)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向MySQL更新分组失败"))
}
@ -192,7 +194,7 @@ func (l GroupLogic) Delete(c *gin.Context, req interface{}) (data interface{}, r
for _, id := range r.GroupIds {
filter := tools.H{"id": int(id)}
if !isql.Group.Exist(filter) {
return nil, tools.NewMySqlError(fmt.Errorf("分组不存在"))
return nil, tools.NewMySqlError(fmt.Errorf("分组不存在"))
}
}
@ -200,28 +202,26 @@ func (l GroupLogic) Delete(c *gin.Context, req interface{}) (data interface{}, r
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取分组列表失败: %s", err.Error()))
}
for _, group := range groups {
// 判断存在子分组,不允许删除
filter := tools.H{"parent_id": int(group.ID)}
if isql.Group.Exist(filter) {
return nil, tools.NewMySqlError(fmt.Errorf("存在子分组,不允许删除"))
return nil, tools.NewMySqlError(fmt.Errorf("存在子分组,请先删除子分组,再执行该分组的删除操作"))
}
dn, err := isql.Group.GetGroupDn(group.ID, "")
if err != nil {
return nil, err.Error()
}
gdn := fmt.Sprintf("%s,%s", dn, config.Conf.Ldap.LdapBaseDN)
err = ildap.Group.Delete(gdn)
// 删除的时候先从ldap进行删除
err = ildap.Group.Delete(group.GroupDN)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP删除分组失败" + err.Error()))
}
}
// 删除接口
err = isql.Group.Delete(r.GroupIds)
// 从MySQL中删除
err = isql.Group.Delete(groups)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("删除接口失败: %s", err.Error()))
}
// TODO: 删除用户组关系
return nil, nil
}
@ -251,28 +251,24 @@ func (l GroupLogic) AddUser(c *gin.Context, req interface{}) (data interface{},
return nil, tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
for _, user := range users {
gdn, err := isql.Group.GetGroupDn(group.ID, "")
if err != nil {
return nil, err.Error()
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
udn := config.Conf.Ldap.LdapAdminDN
if user.Username != "admin" {
udn = fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN)
}
err = ildap.Group.AddUserToGroup(gdn, udn)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP添加用户到分组失败" + err.Error()))
}
if group.GroupDN[:3] == "ou=" {
return nil, tools.NewMySqlError(fmt.Errorf("ou类型的分组不能添加用户"))
}
// 先添加到MySQL
err = isql.Group.AddUserToGroup(group, users)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("添加用户到分组失败: %s", err.Error()))
}
// 再往ldap添加
for _, user := range users {
err = ildap.Group.AddUserToGroup(group.GroupDN, user.UserDN)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP添加用户到分组失败" + err.Error()))
}
}
return nil, nil
}
@ -300,17 +296,20 @@ func (l GroupLogic) RemoveUser(c *gin.Context, req interface{}) (data interface{
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
gdn, err := isql.Group.GetGroupDn(r.GroupID, "")
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
if group.GroupDN[:3] == "ou=" {
return nil, tools.NewMySqlError(fmt.Errorf("ou类型的分组内没有用户"))
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
// 先操作ldap
for _, user := range users {
err := ildap.Group.RemoveUserFromGroup(gdn, user.Username)
err := ildap.Group.RemoveUserFromGroup(group.GroupDN, user.UserDN)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("将用户从ldap移除失败" + err.Error()))
}
}
// 再操作MySQL
err = isql.Group.RemoveUserFromGroup(group, users)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("将用户从MySQL移除失败: %s", err.Error()))

View File

@ -4,9 +4,9 @@ import (
"fmt"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/gin-gonic/gin"
)

View File

@ -4,10 +4,10 @@ import (
"fmt"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/model/response"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/gin-gonic/gin"
)

View File

@ -4,11 +4,11 @@ import (
"fmt"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/model/response"
"github.com/eryajf/go-ldap-admin/public/common"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/gin-gonic/gin"
"github.com/thoas/go-funk"

View File

@ -1,16 +1,15 @@
package logic
import (
"errors"
"fmt"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/model/response"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/ildap"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/gin-gonic/gin"
"github.com/thoas/go-funk"
@ -51,7 +50,7 @@ func (l UserLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspEr
return nil, tools.NewValidatorError(fmt.Errorf("密码长度至少为6位"))
}
} else {
r.Password = "123456"
r.Password = config.Conf.Ldap.LdapUserInitPassword
}
// 当前登陆用户角色排序最小值(最高等级角色)以及当前登陆的用户
@ -81,7 +80,7 @@ func (l UserLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspEr
if currentRoleSortMin >= reqRoleSortMin {
return nil, tools.NewValidatorError(fmt.Errorf("用户不能创建比自己等级高的或者相同等级的用户"))
}
deptIds := tools.SliceToString(r.DepartmentId, ",")
user := model.User{
Username: r.Username,
Password: r.Password,
@ -97,58 +96,19 @@ func (l UserLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspEr
Introduction: r.Introduction,
Status: r.Status,
Creator: ctxUser.Username,
DepartmentId: deptIds,
DepartmentId: tools.SliceToString(r.DepartmentId, ","),
Source: r.Source,
Roles: roles,
UserDN: fmt.Sprintf("uid=%s,%s", r.Username, config.Conf.Ldap.LdapUserDN),
}
if user.Source == "" {
user.Source = "platform"
}
//先识别用户选择的部门是否是OU开头
gdns := make(map[uint]string)
for _, deptId := range r.DepartmentId {
dn, err := isql.Group.GetGroupDn(deptId, "")
if err != nil {
return nil, err.Error()
}
gdn := fmt.Sprintf("%s,%s", dn, config.Conf.Ldap.LdapBaseDN)
if gdn[:3] == "ou=" {
return nil, errors.New("不能添加用户到OU组织单元")
}
gdns[deptId] = gdn
}
//先创建用户到默认分组
err = ildap.User.Add(&user)
err = CommonAddUser(&user, r.DepartmentId)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP创建用户失败" + err.Error()))
}
isExistUser := false
for deptId, gdn := range gdns {
//根据选择的部门,添加到部门内
err = ildap.Group.AddUserToGroup(gdn, fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN))
if err != nil {
return nil, err.Error()
}
if !isExistUser {
err = isql.User.Add(&user)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("向MySQL创建用户失败" + err.Error()))
}
isExistUser = true
}
//根据部门分配,将用户和部门信息维护到部门关系表里面
users := []model.User{}
users = append(users, user)
depart := new(model.Group)
filter := tools.H{"id": deptId}
err = isql.Group.Find(filter, depart)
if err != nil {
return "", tools.NewMySqlError(err)
}
err = isql.Group.AddUserToGroup(depart, users)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("向MySQL添加用户到分组关系失败" + err.Error()))
}
return nil, tools.NewOperationError(fmt.Errorf("添加用户失败" + err.Error()))
}
return nil, nil
}
@ -233,7 +193,7 @@ func (l UserLogic) Update(c *gin.Context, req interface{}) (data interface{}, rs
if err != nil {
return nil, tools.NewMySqlError(err)
}
deptIds := tools.SliceToString(r.DepartmentId, ",")
user := model.User{
Model: oldData.Model,
Username: r.Username,
@ -248,9 +208,10 @@ func (l UserLogic) Update(c *gin.Context, req interface{}) (data interface{}, rs
Position: r.Position,
Introduction: r.Introduction,
Creator: ctxUser.Username,
DepartmentId: deptIds,
DepartmentId: tools.SliceToString(r.DepartmentId, ","),
Source: oldData.Source,
Roles: roles,
UserDN: oldData.UserDN,
}
// 判断是更新自己还是更新别人,如果操作的ID与登陆用户的ID一致则说明操作的是自己
@ -275,117 +236,13 @@ func (l UserLogic) Update(c *gin.Context, req interface{}) (data interface{}, rs
return nil, tools.NewValidatorError(fmt.Errorf("用户不能把别的用户角色等级更新得比自己高或相等"))
}
}
err = ildap.User.Update(oldData.Username, &user)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("在LDAP更新用户失败" + err.Error()))
if err = CommonUpdateUser(oldData, &user, r.DepartmentId); err != nil {
return nil, tools.NewOperationError(fmt.Errorf("更新用户失败" + err.Error()))
}
// 更新用户
if !config.Conf.Ldap.LdapUserNameModify {
user.Username = oldData.Username
}
err = isql.User.Update(&user)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("在MySQL更新用户失败" + err.Error()))
}
//判断部门信息是否有变化有变化则更新相应的数据库
oldDeptIds := tools.StringToSlice(oldData.DepartmentId, ",")
addDeptIds, removeDeptIds := tools.ArrUintCmp(oldDeptIds, r.DepartmentId)
for _, deptId := range removeDeptIds {
//从旧组中删除
err = l.RemoveUserToGroup(deptId, []uint{r.ID})
if err != nil {
return nil, err
}
}
for _, deptId := range addDeptIds {
//添加到新分组中
err = l.AddUserToGroup(deptId, []uint{r.ID})
if err != nil {
return nil, err
}
}
return nil, nil
}
// RemoveUser 移除用户
func (l UserLogic) RemoveUserToGroup(groupId uint, userIds []uint) error {
filter := tools.H{"id": groupId}
if !isql.Group.Exist(filter) {
return tools.NewMySqlError(fmt.Errorf("分组不存在"))
}
users, err := isql.User.GetUserByIds(userIds)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取用户列表失败: %s", err.Error()))
}
group := new(model.Group)
err = isql.Group.Find(filter, group)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
gdn, err := isql.Group.GetGroupDn(groupId, "")
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
for _, user := range users {
err := ildap.Group.RemoveUserFromGroup(gdn, user.Username)
if err != nil {
return tools.NewLdapError(fmt.Errorf("将用户从ldap移除失败" + err.Error()))
}
}
err = isql.Group.RemoveUserFromGroup(group, users)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("将用户从MySQL移除失败: %s", err.Error()))
}
return nil
}
//将用户添加到分组中
func (l UserLogic) AddUserToGroup(groupId uint, userIds []uint) error {
filter := tools.H{"id": groupId}
if !isql.Group.Exist(filter) {
return tools.NewMySqlError(fmt.Errorf("分组不存在"))
}
users, err := isql.User.GetUserByIds(userIds)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取用户列表失败: %s", err.Error()))
}
group := new(model.Group)
err = isql.Group.Find(filter, group)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
for _, user := range users {
gdn, err := isql.Group.GetGroupDn(group.ID, "")
if err != nil {
return err
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
udn := config.Conf.Ldap.LdapAdminDN
if user.Username != "admin" {
udn = fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN)
}
err = ildap.Group.AddUserToGroup(gdn, udn)
if err != nil {
return tools.NewLdapError(fmt.Errorf("向LDAP添加用户到分组失败" + err.Error()))
}
}
err = isql.Group.AddUserToGroup(group, users)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("添加用户到分组失败: %s", err.Error()))
}
return nil
}
// Delete 删除数据
func (l UserLogic) Delete(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
r, ok := req.(*request.UserDeleteReq)
@ -394,6 +251,13 @@ func (l UserLogic) Delete(c *gin.Context, req interface{}) (data interface{}, rs
}
_ = c
for _, id := range r.UserIds {
filter := tools.H{"id": int(id)}
if !isql.User.Exist(filter) {
return nil, tools.NewMySqlError(fmt.Errorf("有用户不存在"))
}
}
// 根据用户ID获取用户角色排序最小值
roleMinSortList, err := isql.User.GetUserMinRoleSortsByIds(r.UserIds) // TODO: 这里应该复用下边的 GetUserByIds 方法
if err != nil || len(roleMinSortList) == 0 {
@ -423,13 +287,16 @@ func (l UserLogic) Delete(c *gin.Context, req interface{}) (data interface{}, rs
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取用户信息失败: " + err.Error()))
}
// 先将用户从ldap中删除
for _, user := range users {
err := ildap.User.Delete(user.Username)
err := ildap.User.Delete(user.UserDN)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("在LDAP删除用户失败" + err.Error()))
}
}
// 再将用户从MySQL中删除
err = isql.User.Delete(r.UserIds)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("在MySQL删除用户失败: " + err.Error()))
@ -473,7 +340,7 @@ func (l UserLogic) ChangePwd(c *gin.Context, req interface{}) (data interface{},
return nil, tools.NewValidatorError(fmt.Errorf("原密码错误"))
}
// ldap更新密码时可以直接指定用户DN和新密码即可更改成功
err = ildap.User.ChangePwd(user.Username, "", r.NewPassword)
err = ildap.User.ChangePwd(user.UserDN, "", r.NewPassword)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("在LDAP更新密码失败" + err.Error()))
}
@ -524,7 +391,7 @@ func (l UserLogic) ChangeUserStatus(c *gin.Context, req interface{}) (data inter
}
if r.Status == 2 {
err = ildap.User.Delete(user.Username)
err = ildap.User.Delete(user.UserDN)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("在LDAP删除用户失败" + err.Error()))
}

24
main.go
View File

@ -3,14 +3,14 @@ package main
import (
"context"
"fmt"
"github.com/eryajf/go-ldap-admin/logic"
"github.com/robfig/cron/v3"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/eryajf/go-ldap-admin/logic"
"github.com/robfig/cron/v3"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/middleware"
"github.com/eryajf/go-ldap-admin/public/common"
@ -68,16 +68,21 @@ func main() {
if config.Conf.DingTalk.DingTalkEnableSync {
//启动定时任务
c := cron.New(cron.WithSeconds())
c.AddFunc("0 1 0 * * *", func() {
_, err := c.AddFunc("0 1 0 * * *", func() {
common.Log.Info("每天0点1分0秒执行一次同步钉钉部门和用户信息到ldap")
logic.DingTalk.DsyncDingTalkDepts(nil, nil)
logic.DingTalk.SyncDingTalkDepts(nil, nil)
})
if err != nil {
common.Log.Errorf("启动同步部门的定时任务失败: %v", err)
}
//每天凌晨1点执行一次
c.AddFunc("0 15 0 * * *", func() {
_, err = c.AddFunc("0 15 0 * * *", func() {
common.Log.Info("每天凌晨00点15分执行一次同步钉钉部门和用户信息到ldap")
logic.DingTalk.SyncDingTalkUsers(nil, nil)
})
if err != nil {
common.Log.Errorf("启动同步用户的定时任务失败: %v", err)
}
c.Start()
}
@ -85,11 +90,12 @@ func main() {
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
quit := make(chan os.Signal)
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
signal.Notify(quit, os.Interrupt)
<-quit
common.Log.Info("Shutting down server...")

View File

@ -11,8 +11,8 @@ import (
"time"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/model/response"
jwt "github.com/appleboy/gin-jwt/v2"
"github.com/gin-gonic/gin"

View File

@ -14,7 +14,7 @@ func CORSMiddleware() gin.HandlerFunc {
method := c.Request.Method //请求方法
origin := c.Request.Header.Get("Origin") //请求头部
var headerKeys []string // 声明请求头keys
for k, _ := range c.Request.Header {
for k := range c.Request.Header {
headerKeys = append(headerKeys, k)
}
headerStr := strings.Join(headerKeys, ", ")
@ -41,5 +41,6 @@ func CORSMiddleware() gin.HandlerFunc {
}
// 处理请求
c.Next() // 处理请求
_ = headerStr
}
}

View File

@ -31,15 +31,13 @@ func OperationLogMiddleware() gin.HandlerFunc {
// 获取当前登录用户
var username string
ctxUser, exists := c.Get("user")
if !exists {
username = "未登录"
}
ctxUser, _ := c.Get("user")
user, ok := ctxUser.(model.User)
if !ok {
username = "未登录"
} else {
username = user.Username
}
username = user.Username
// 获取访问路径
path := strings.TrimPrefix(c.FullPath(), "/"+config.Conf.System.UrlPathPrefix)

View File

@ -3,7 +3,7 @@ package middleware
import (
"time"
"github.com/eryajf/go-ldap-admin/svc/response"
"github.com/eryajf/go-ldap-admin/model/response"
"github.com/gin-gonic/gin"
"github.com/juju/ratelimit"

View File

@ -15,4 +15,5 @@ type Group struct {
SourceDeptParentId string `gorm:"type:varchar(100);comment:'父部门编号'" json:"sourceDeptParentId"`
SourceUserNum int `gorm:"default:0;comment:'部门下的用户数量,从第三方获取的数据'" json:"source_user_num"`
Children []*Group `gorm:"-" json:"children"`
GroupDN string `gorm:"type:varchar(255);not null;comment:'分组dn'" json:"groupDn"` // 分组在ldap的dn
}

View File

@ -4,23 +4,24 @@ import "gorm.io/gorm"
type User struct {
gorm.Model
Username string `gorm:"type:varchar(50);not null;unique;comment:'用户名'" json:"username"` // 用户名
Password string `gorm:"size:255;not null;comment:'用户密码'" json:"password"` // 用户密码
Nickname string `gorm:"type:varchar(50);comment:'中文名'" json:"nickname"` // 昵称
GivenName string `gorm:"type:varchar(50);comment:'花名'" json:"givenName"` // 花名,如果有的话,没有的话用昵称占位
Mail string `gorm:"type:varchar(100);comment:'邮箱'" json:"mail"` // 邮箱
JobNumber string `gorm:"type:varchar(20);comment:'工号'" json:"jobNumber"` // 工号
Mobile string `gorm:"type:varchar(11);not null;unique;comment:'手机号'" json:"mobile"` // 手机号
Avatar string `gorm:"type:varchar(255);comment:'头像'" json:"avatar"` // 头像
PostalAddress string `gorm:"type:varchar(255);comment:'地址'" json:"postalAddress"` // 地址
Departments string `gorm:"type:varchar(128);comment:'部门'" json:"departments"` // 部门
Position string `gorm:"type:varchar(128);comment:'职位'" json:"position"` // 职位
Introduction string `gorm:"type:varchar(255);comment:'个人简介'" json:"introduction"` // 个人简介
Status uint `gorm:"type:tinyint(1);default:1;comment:'状态:1在职, 2离职'" json:"status"` // 状态
Creator string `gorm:"type:varchar(20);;comment:'创建者'" json:"creator"` // 创建者
Source string `gorm:"type:varchar(50);comment:'用户来源dingTalk、weCom、ldap、platform'" json:"source"` // 来源
DepartmentId string `gorm:"type:varchar(100);not null;comment:'部门id'" json:"departmentId"` // 部门id
Roles []*Role `gorm:"many2many:user_roles" json:"roles"` // 角色
SourceUserId string `gorm:"type:varchar(100);not null;comment:'第三方用户id'" json:"sourceUserId"` // 第三方用户id
SourceUnionId string `gorm:"type:varchar(100);not null;comment:'第三方唯一unionId'" json:"sourceUnionId"` // 第三方唯一unionId
Username string `gorm:"type:varchar(50);not null;unique;comment:'用户名'" json:"username"` // 用户名
Password string `gorm:"size:255;not null;comment:'用户密码'" json:"password"` // 用户密码
Nickname string `gorm:"type:varchar(50);comment:'中文名'" json:"nickname"` // 昵称
GivenName string `gorm:"type:varchar(50);comment:'花名'" json:"givenName"` // 花名,如果有的话,没有的话用昵称占位
Mail string `gorm:"type:varchar(100);comment:'邮箱'" json:"mail"` // 邮箱
JobNumber string `gorm:"type:varchar(20);comment:'工号'" json:"jobNumber"` // 工号
Mobile string `gorm:"type:varchar(11);not null;unique;comment:'手机号'" json:"mobile"` // 手机号
Avatar string `gorm:"type:varchar(255);comment:'头像'" json:"avatar"` // 头像
PostalAddress string `gorm:"type:varchar(255);comment:'地址'" json:"postalAddress"` // 地址
Departments string `gorm:"type:varchar(128);comment:'部门'" json:"departments"` // 部门
Position string `gorm:"type:varchar(128);comment:'职位'" json:"position"` // 职位
Introduction string `gorm:"type:varchar(255);comment:'个人简介'" json:"introduction"` // 个人简介
Status uint `gorm:"type:tinyint(1);default:1;comment:'状态:1在职, 2离职'" json:"status"` // 状态
Creator string `gorm:"type:varchar(20);;comment:'创建者'" json:"creator"` // 创建者
Source string `gorm:"type:varchar(50);comment:'用户来源dingTalk、wecom、feishu、ldap、platform'" json:"source"` // 来源
DepartmentId string `gorm:"type:varchar(100);not null;comment:'部门id'" json:"departmentId"` // 部门id
Roles []*Role `gorm:"many2many:user_roles" json:"roles"` // 角色
SourceUserId string `gorm:"type:varchar(100);not null;comment:'第三方用户id'" json:"sourceUserId"` // 第三方用户id
SourceUnionId string `gorm:"type:varchar(100);not null;comment:'第三方唯一unionId'" json:"sourceUnionId"` // 第三方唯一unionId
UserDN string `gorm:"type:varchar(255);not null;comment:'用户dn'" json:"userDn"` // 用户在ldap的dn
}

View File

@ -0,0 +1,53 @@
package dingtalk
import (
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/public/common"
"github.com/zhaoyunxing92/dingtalk/v2"
)
func InitDingTalkClient() *dingtalk.DingTalk {
dingTalk, err := dingtalk.NewClient(config.Conf.DingTalk.DingTalkAppKey, config.Conf.DingTalk.DingTalkAppSecret)
if err != nil {
common.Log.Error("init dingding client failed, err:%v\n", err)
}
return dingTalk
}
// 部门结构体
type DingTalkDept struct {
Id int `json:"dept_id"` // 部门ID
Name string `json:"name"` // 部门名称拼音
Remark string `json:"remark"` // 部门中文名
ParentId int `json:"parent_id"` // 父部门ID
}
// DingTalkUser 部门用户详情
type DingTalkUser struct {
UserId string `json:"userid"`
UnionId string `json:"unionid"` // 员工在当前开发者企业账号范围内的唯一标识
Name string `json:"name"` // 员工名称
Avatar string `json:"avatar"` // 头像
StateCode string `json:"state_code"` // 国际电话区号
ManagerUserId string `json:"manager_userid"` // 员工的直属主管
Mobile string `json:"mobile"` // 手机号码
HideMobile bool `json:"hide_mobile"` // 是否号码隐藏
Telephone string `json:"telephone"` // 分机号
JobNumber string `json:"job_number"` // 员工工号
Title string `json:"title"` // 职位
WorkPlace string `json:"work_place"` // 办公地点
Remark string `json:"remark"` // 备注
LoginId string `json:"loginId"` // 专属帐号登录名
DeptIds []int `json:"dept_id_list"` // 所属部门ID列表
DeptOrder int `json:"dept_order"` // 员工在部门中的排序
Extension string `json:"extension"` // 员工在对应的部门中的排序
HiredDate int `json:"hired_date"` // 入职时间
Active bool `json:"active"` // 是否激活了钉钉
Admin bool `json:"admin"` //是否为企业的管理员:
Boss bool `json:"boss"` // 是否为企业的老板
ExclusiveAccount bool `json:"exclusive_account"` // 是否专属帐号
Leader bool `json:"leader"` // 是否是部门的主管
ExclusiveAccountType string `json:"exclusive_account_type"` //专属帐号类型sso企业自建专属帐号 dingtalk钉钉自建专属帐号
OrgEmail string `json:"org_email"` //员工的企业邮箱,如果员工的企业邮箱没有开通,返回信息中不包含该数据
Email string `json:"email"` //员工邮箱,企业内部应用如果没有返回该字段,需要检查当前应用通讯录权限中邮箱等个人信息权限是否开启,员工信息面板中有邮箱字段值才返回该字段,第三方企业应用不返回该参数
}

View File

@ -0,0 +1,107 @@
package dingtalk
import (
"strings"
"github.com/mozillazg/go-pinyin"
"github.com/zhaoyunxing92/dingtalk/v2/request"
)
func GetDingTalkAllDepts(deptId int) (result []*DingTalkDept, err error) {
depts, err := InitDingTalkClient().FetchDeptList(deptId, true, "zh_CN")
if err != nil {
return result, err
}
for _, dept := range depts.Dept {
result = append(result, &DingTalkDept{
Id: dept.Id,
Name: strings.Join(pinyin.LazyConvert(dept.Name, nil), ""),
Remark: dept.Name,
ParentId: dept.ParentId,
})
}
return
}
func GetDingTalkAllUsers() (result []*DingTalkUser, err error) {
depts, err := GetDingTalkAllDepts(1)
if err != nil {
return nil, err
}
for _, dept := range depts {
r := request.DeptDetailUserInfo{
DeptId: dept.Id,
Cursor: 0,
Size: 99,
Language: "zh_CN",
}
for {
//获取钉钉部门人员信息
rsp, err := InitDingTalkClient().GetDeptDetailUserInfo(&r)
if err != nil {
return nil, err
}
for _, user := range rsp.DeptDetailUsers {
result = append(result, &DingTalkUser{
UserId: user.UserId,
UnionId: user.UnionId,
Name: user.Name,
Avatar: user.Avatar,
StateCode: user.StateCode,
ManagerUserId: user.ManagerUserId,
Mobile: user.Mobile,
HideMobile: user.HideMobile,
Telephone: user.Telephone,
JobNumber: user.JobNumber,
Title: user.Title,
WorkPlace: user.WorkPlace,
Remark: user.Remark,
LoginId: user.LoginId,
DeptIds: user.DeptIds,
DeptOrder: user.DeptOrder,
Extension: user.Extension,
HiredDate: user.HiredDate,
Active: user.Active,
Admin: user.Admin,
Boss: user.Boss,
ExclusiveAccount: user.ExclusiveAccount,
Leader: user.Leader,
ExclusiveAccountType: user.ExclusiveAccountType,
OrgEmail: user.OrgEmail,
Email: user.Email,
})
}
if !rsp.HasMore {
break
}
r.Cursor = rsp.NextCursor
}
}
return
}
func GetDingTalkLeaveUserIds() ([]string, error) {
var ids []string
ReqParm := struct {
Cursor int `json:"cursor"`
Size int `json:"size"`
}{
Cursor: 0,
Size: 50,
}
for {
rsp, err := InitDingTalkClient().GetHrmResignEmployeeIds(ReqParm.Cursor, ReqParm.Size)
if err != nil {
return nil, err
}
ids = append(ids, rsp.UserIds...)
if rsp.NextCursor == 0 {
break
}
ReqParm.Cursor = rsp.NextCursor
}
return ids, nil
}

View File

@ -63,7 +63,7 @@ func InitMysql() {
// 自动迁移表结构
func dbAutoMigrate() {
DB.AutoMigrate(
_ = DB.AutoMigrate(
&model.User{},
&model.Role{},
&model.Group{},

View File

@ -2,6 +2,7 @@ package common
import (
"errors"
"fmt"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/model"
@ -228,6 +229,7 @@ func InitData() {
Status: 1,
Creator: "系统",
Roles: roles[:1],
UserDN: config.Conf.Ldap.LdapAdminDN,
},
}
@ -618,57 +620,85 @@ func InitData() {
newGroups := make([]model.Group, 0)
groups := []model.Group{
{
Model: gorm.Model{ID: 1},
GroupName: "eryajf",
Remark: "二丫讲梵有限公司",
Creator: "系统",
GroupType: "ou",
ParentId: 0,
Model: gorm.Model{ID: 1},
GroupName: config.Conf.DingTalk.DingTalkFlag + "root",
Remark: "钉钉根部门",
Creator: "system",
GroupType: "ou",
ParentId: 0,
SourceDeptId: fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkFlag, 1),
Source: config.Conf.DingTalk.DingTalkFlag,
SourceDeptParentId: fmt.Sprintf("%s_%d", config.Conf.DingTalk.DingTalkFlag, 0),
GroupDN: fmt.Sprintf("ou=%s,%s", config.Conf.DingTalk.DingTalkFlag+"root", config.Conf.Ldap.LdapBaseDN),
},
{
Model: gorm.Model{ID: 2},
GroupName: "jenkins",
Remark: "Jenkins对应权限组管理",
Creator: "系统",
GroupType: "ou",
ParentId: 0,
Model: gorm.Model{ID: 2},
GroupName: "wecomroot",
Remark: "企业微信根部门",
Creator: "system",
GroupType: "ou",
ParentId: 0,
SourceDeptId: "wechatwork_1",
Source: "wechatwork",
SourceDeptParentId: "wechatwork_0",
GroupDN: fmt.Sprintf("ou=%s,%s", "wechatworkroot", config.Conf.Ldap.LdapBaseDN),
},
{
Model: gorm.Model{ID: 3},
GroupName: "ceshizu",
Remark: "测试组",
Creator: "系统",
GroupType: "cn",
Users: users[:1],
ParentId: 1,
},
{
Model: gorm.Model{ID: 4},
GroupName: "yunweizu",
Remark: "运维组",
Creator: "系统",
GroupType: "cn",
Users: users[:1],
ParentId: 1,
},
{
Model: gorm.Model{ID: 5},
GroupName: "test-admin",
Remark: "admin测试环境",
Creator: "系统",
GroupType: "cn",
Users: users[:1],
ParentId: 2,
},
{
Model: gorm.Model{ID: 6},
GroupName: "prod-admin",
Remark: "admin正式环境",
Creator: "系统",
GroupType: "cn",
Users: users[:1],
ParentId: 2,
Model: gorm.Model{ID: 3},
GroupName: "feishuroot",
Remark: "飞书根部门",
Creator: "system",
GroupType: "ou",
ParentId: 0,
SourceDeptId: "feishu_1",
Source: "feishu",
SourceDeptParentId: "feishu_0",
GroupDN: fmt.Sprintf("ou=%s,%s", "feishuroot", config.Conf.Ldap.LdapBaseDN),
},
// {
// Model: gorm.Model{ID: 2},
// GroupName: "jenkins",
// Remark: "Jenkins对应权限组管理",
// Creator: "系统",
// GroupType: "ou",
// ParentId: 0,
// },
// {
// Model: gorm.Model{ID: 3},
// GroupName: "ceshizu",
// Remark: "测试组",
// Creator: "系统",
// GroupType: "cn",
// Users: users[:1],
// ParentId: 1,
// },
// {
// Model: gorm.Model{ID: 4},
// GroupName: "yunweizu",
// Remark: "运维组",
// Creator: "系统",
// GroupType: "cn",
// Users: users[:1],
// ParentId: 1,
// },
// {
// Model: gorm.Model{ID: 5},
// GroupName: "test-admin",
// Remark: "admin测试环境",
// Creator: "系统",
// GroupType: "cn",
// Users: users[:1],
// ParentId: 2,
// },
// {
// Model: gorm.Model{ID: 6},
// GroupName: "prod-admin",
// Remark: "admin正式环境",
// Creator: "系统",
// GroupType: "cn",
// Users: users[:1],
// ParentId: 2,
// },
}
for _, group := range groups {

View File

@ -10,7 +10,7 @@ import (
ldap "github.com/go-ldap/ldap/v3"
)
// 全局mysql数据库变量
// 全局ldap数据库变量
var LDAP *ldap.Conn
// Init 初始化连接

View File

@ -20,9 +20,9 @@ func ArrStrCmp(src []string, dest []string) ([]string, []string) {
for _, v := range dest {
l := len(mall)
mall[v] = 1
if l != len(mall) { //长度变化,即可以存
l = len(mall)
} else { //存不了,进并集
if l != len(mall) {
continue
} else {
set = append(set, v)
}
}
@ -32,7 +32,7 @@ func ArrStrCmp(src []string, dest []string) ([]string, []string) {
}
//4.此时mall是补集所有元素去源中找找到就是删除的找不到的必定能在目数组中找到即新加的
var added, deleted []string
for v, _ := range mall {
for v := range mall {
_, exist := msrc[v]
if exist {
deleted = append(deleted, v)
@ -57,9 +57,9 @@ func ArrUintCmp(src []uint, dest []uint) ([]uint, []uint) {
for _, v := range dest {
l := len(mall)
mall[v] = 1
if l != len(mall) { //长度变化,即可以存
l = len(mall)
} else { //存不了,进并集
if l != len(mall) {
continue
} else {
set = append(set, v)
}
}
@ -69,7 +69,7 @@ func ArrUintCmp(src []uint, dest []uint) ([]uint, []uint) {
}
//4.此时mall是补集所有元素去源中找找到就是删除
var added, deleted []uint
for v, _ := range mall {
for v := range mall {
_, exist := msrc[v]
if exist {
deleted = append(deleted, v)

View File

@ -2,7 +2,6 @@ package ildap
import (
"errors"
"fmt"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/model"
@ -14,13 +13,11 @@ import (
type GroupService struct{}
// Add 添加资源
func (x GroupService) Add(g *model.Group, pdn string) error { //organizationalUnit
parentDn := config.Conf.Ldap.LdapBaseDN
if pdn != "" {
parentDn = fmt.Sprintf("%s,%s", pdn, config.Conf.Ldap.LdapBaseDN)
func (x GroupService) Add(g *model.Group) error { //organizationalUnit
if g.Remark == "" {
g.Remark = g.GroupName
}
dn := fmt.Sprintf("%s=%s,%s", g.GroupType, g.GroupName, parentDn)
add := ldap.NewAddRequest(dn, nil)
add := ldap.NewAddRequest(g.GroupDN, nil)
if g.GroupType == "ou" {
add.Attribute("objectClass", []string{"organizationalUnit", "top"}) // 如果定义了 groupOfNAmes那么必须指定member否则报错如下object class 'groupOfNames' requires attribute 'member'
}
@ -35,24 +32,16 @@ func (x GroupService) Add(g *model.Group, pdn string) error { //organizationalUn
}
// UpdateGroup 更新一个分组
func (x GroupService) Update(g *model.Group, pdn string, oldGroupName, oldRemark string) error {
parentDn := "," + config.Conf.Ldap.LdapBaseDN
if pdn != "" {
parentDn = fmt.Sprintf("%s,%s", pdn, config.Conf.Ldap.LdapBaseDN)
}
//默认更新remark字段
if g.Remark != oldRemark {
modify := ldap.NewModifyRequest(parentDn, nil)
modify.Replace("description", []string{g.Remark})
err := common.LDAP.Modify(modify)
if err != nil {
return err
}
func (x GroupService) Update(oldGroup, newGroup *model.Group) error {
modify := ldap.NewModifyRequest(oldGroup.GroupDN, nil)
modify.Replace("description", []string{newGroup.Remark})
err := common.LDAP.Modify(modify)
if err != nil {
return err
}
// 如果配置文件允许修改分组名称,且分组名称发生了变化,那么执行修改分组名称
if config.Conf.Ldap.LdapGroupNameModify && g.GroupName != oldGroupName {
rdn := fmt.Sprintf("%s=%s", g.GroupType, g.GroupName)
modify := ldap.NewModifyDNRequest(parentDn, rdn, true, "")
if config.Conf.Ldap.LdapGroupNameModify && newGroup.GroupName != oldGroup.GroupName {
modify := ldap.NewModifyDNRequest(oldGroup.GroupDN, newGroup.GroupDN, true, "")
err := common.LDAP.ModifyDN(modify)
if err != nil {
return err
@ -62,8 +51,8 @@ func (x GroupService) Update(g *model.Group, pdn string, oldGroupName, oldRemark
}
// Delete 删除资源
func (x GroupService) Delete(pdn string) error {
del := ldap.NewDelRequest(pdn, nil)
func (x GroupService) Delete(gdn string) error {
del := ldap.NewDelRequest(gdn, nil)
return common.LDAP.Del(del)
}
@ -79,8 +68,7 @@ func (x GroupService) AddUserToGroup(dn, udn string) error {
}
// DelUserFromGroup 将用户从分组删除
func (x GroupService) RemoveUserFromGroup(gdn, user string) error {
udn := fmt.Sprintf("uid=%s,%s", user, config.Conf.Ldap.LdapUserDN)
func (x GroupService) RemoveUserFromGroup(gdn, udn string) error {
newmr := ldap.NewModifyRequest(gdn, nil)
newmr.Delete("uniqueMember", []string{udn})
return common.LDAP.Modify(newmr)

View File

@ -14,22 +14,7 @@ type UserService struct{}
// 创建资源
func (x UserService) Add(user *model.User) error {
if user.Departments == "" {
user.Departments = "研发中心"
}
if user.GivenName == "" {
user.GivenName = user.Nickname
}
if user.PostalAddress == "" {
user.PostalAddress = "没有填写地址"
}
if user.Position == "" {
user.Position = "技术"
}
if user.Introduction == "" {
user.Introduction = user.Nickname
}
add := ldap.NewAddRequest(fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN), nil)
add := ldap.NewAddRequest(user.UserDN, nil)
add.Attribute("objectClass", []string{"inetOrgPerson"})
add.Attribute("cn", []string{user.Username})
add.Attribute("sn", []string{user.Nickname})
@ -49,7 +34,7 @@ func (x UserService) Add(user *model.User) error {
// Update 更新资源
func (x UserService) Update(oldusername string, user *model.User) error {
modify := ldap.NewModifyRequest(fmt.Sprintf("uid=%s,%s", oldusername, config.Conf.Ldap.LdapUserDN), nil)
modify := ldap.NewModifyRequest(user.UserDN, nil)
modify.Replace("cn", []string{user.Nickname})
modify.Replace("sn", []string{oldusername})
modify.Replace("businessCategory", []string{user.Departments})
@ -73,21 +58,17 @@ func (x UserService) Update(oldusername string, user *model.User) error {
}
// Delete 删除资源
func (x UserService) Delete(username string) error {
del := ldap.NewDelRequest(fmt.Sprintf("uid=%s,%s", username, config.Conf.Ldap.LdapUserDN), nil)
func (x UserService) Delete(udn string) error {
del := ldap.NewDelRequest(udn, nil)
return common.LDAP.Del(del)
}
// ChangePwd 修改用户密码此处旧密码也可以为空ldap可以直接通过用户DN加上新密码来进行修改
func (u UserService) ChangePwd(username, oldpasswd, newpasswd string) error {
udn := fmt.Sprintf("uid=%s,%s", username, config.Conf.Ldap.LdapUserDN)
if username == "admin" {
udn = config.Conf.Ldap.LdapAdminDN
}
func (x UserService) ChangePwd(udn, oldpasswd, newpasswd string) error {
modifyPass := ldap.NewPasswordModifyRequest(udn, oldpasswd, newpasswd)
_, err := common.LDAP.PasswordModify(modifyPass)
if err != nil {
return fmt.Errorf("password modify failed for %s, err: %v", username, err)
return fmt.Errorf("password modify failed for %s, err: %v", udn, err)
}
return nil
}

View File

@ -6,9 +6,9 @@ import (
"strings"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/public/common"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/svc/request"
"gorm.io/gorm"
)
@ -124,7 +124,7 @@ func (s ApiService) Delete(ids []uint) error {
api := new(model.Api)
err := s.Find(tools.H{"id": id}, api)
if err != nil {
return errors.New(fmt.Sprintf("未获取到ID为%d的用户", id))
return fmt.Errorf("根据ID获取接口信息失败: %v", err)
}
apis = append(apis, *api)
}

View File

@ -6,9 +6,9 @@ import (
"strings"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/public/common"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/svc/request"
"gorm.io/gorm"
)
@ -87,30 +87,6 @@ func (s GroupService) ListAll(req *request.GroupListAllReq) ([]*model.Group, err
return list, err
}
// 拼装dn信息
func (s GroupService) GetGroupDn(groupId uint, oldDn string) (dn string, e error) {
depart := new(model.Group)
filter := tools.H{"id": int(groupId)}
err := s.Find(filter, depart)
if err != nil {
return "", tools.NewMySqlError(err)
}
if oldDn == "" {
dn = fmt.Sprintf("%s=%s", depart.GroupType, depart.GroupName)
} else {
dn = fmt.Sprintf("%s,%s=%s", oldDn, depart.GroupType, depart.GroupName)
}
if depart.ParentId > 0 {
tempDn, err := s.GetGroupDn(depart.ParentId, dn)
if err != nil {
return dn, err
}
dn = tempDn
fmt.Println(tempDn)
}
return dn, nil
}
// GenGroupTree 生成分组树
func GenGroupTree(parentId uint, groups []*model.Group) []*model.Group {
tree := make([]*model.Group, 0)
@ -155,8 +131,8 @@ func (s GroupService) Exist(filter map[string]interface{}) bool {
}
// Delete 批量删除
func (s GroupService) Delete(ids []uint) error {
return common.DB.Where("id IN (?)", ids).Select("Users").Unscoped().Delete(&model.Group{}).Error
func (s GroupService) Delete(groups []*model.Group) error {
return common.DB.Debug().Select("Users").Unscoped().Delete(&groups).Error
}
// GetApisById 根据接口ID获取接口列表
@ -177,12 +153,12 @@ func (s GroupService) RemoveUserFromGroup(group *model.Group, users []model.User
// DingTalkDeptIdsToGroupIds 将钉钉部门id转换为分组id
func (s GroupService) DingTalkDeptIdsToGroupIds(dingTalkIds []string) (groupIds []uint, err error) {
tempGroups := []model.Group{}
var tempGroups []model.Group
err = common.DB.Model(&model.Group{}).Where("source_dept_id IN (?)", dingTalkIds).Find(&tempGroups).Error
if err != nil {
return nil, err
}
tempGroupIds := []uint{}
var tempGroupIds []uint
for _, g := range tempGroups {
tempGroupIds = append(tempGroupIds, g.ID)
}

View File

@ -6,9 +6,9 @@ import (
"strings"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/public/common"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/svc/request"
"gorm.io/gorm"
)

View File

@ -6,9 +6,9 @@ import (
"strings"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/public/common"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/svc/request"
"gorm.io/gorm"
)

View File

@ -7,9 +7,9 @@ import (
"time"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/model/request"
"github.com/eryajf/go-ldap-admin/public/common"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/svc/request"
"github.com/gin-gonic/gin"
"github.com/patrickmn/go-cache"
@ -197,21 +197,32 @@ func (s UserService) Delete(ids []uint) error {
var users []model.User
for _, id := range ids {
// 根据ID获取用户
filter := tools.H{"id": id}
user := new(model.User)
err := s.Find(map[string]interface{}{"id": id}, user)
err := s.Find(filter, user)
if err != nil {
return errors.New(fmt.Sprintf("未获取到ID为%d的用户", id))
return fmt.Errorf("获取用户信息失败err: %v", err)
}
users = append(users, *user)
}
err := common.DB.Select("Roles").Unscoped().Delete(&users).Error
// 删除用户成功,则删除用户信息缓存
if err == nil {
err := common.DB.Debug().Select("Roles").Unscoped().Delete(&users).Error
if err != nil {
return err
} else {
// 删除用户成功,则删除用户信息缓存
for _, user := range users {
userInfoCache.Delete(user.Username)
}
}
// 删除用户在group的关联
err = common.DB.Debug().Exec("DELETE FROM group_users WHERE user_id IN (?)", ids).Error
if err != nil {
return err
}
return err
}