Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbf8b23b15 | ||
|
|
79a5dd53ae | ||
|
|
8a5b20685c | ||
|
|
5a9c972de0 | ||
|
|
fc0e67122d | ||
|
|
eb642f66ca |
83
README.md
83
README.md
@@ -20,7 +20,10 @@
|
||||
- 登录
|
||||
- 地址簿
|
||||
- 群组
|
||||
- 授权登录,支持`github`, `google` 和 `OIDC` 登录,支持`web后台`授权登录
|
||||
- 授权登录
|
||||
- 支持`github`, `google` 和 `OIDC` 登录,
|
||||
- 支持`web后台`授权登录
|
||||
- 支持`LDAP`(AD和OpenLDAP已测试), 如果API Server配置了LDAP
|
||||
- i18n
|
||||
- Web Admin
|
||||
- 用户管理
|
||||
@@ -29,6 +32,7 @@
|
||||
- 标签管理
|
||||
- 群组管理
|
||||
- Oauth 管理
|
||||
- 配置LDAP, 配置文件或者环境变量
|
||||
- 登录日志
|
||||
- 链接日志
|
||||
- 文件传输日志
|
||||
@@ -107,6 +111,7 @@
|
||||
|
||||

|
||||
|
||||
11. **LDAP 支持**, 当在API Server上设置了LDAP(已测试AD和LDAP),可以通过LDAP中的用户信息进行登录 https://github.com/lejianwen/rustdesk-api/issues/114 ,如果LDAP验证失败,返回本地用户
|
||||
|
||||
### Web Client:
|
||||
|
||||
@@ -208,43 +213,43 @@ ldap:
|
||||
环境变量和配置文件`conf/config.yaml`中的配置一一对应,变量名前缀是`RUSTDESK_API`
|
||||
下面表格并未全部列出,可以参考`conf/config.yaml`中的配置。
|
||||
|
||||
| 变量名 | 说明 | 示例 |
|
||||
|---------------------------------------------------|--------------------------------------------------------------------------------|------------------------------|
|
||||
| TZ | 时区 | Asia/Shanghai |
|
||||
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
|
||||
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
||||
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
||||
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
|
||||
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长(秒) | `3600` |
|
||||
| -----ADMIN配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
||||
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
||||
| RUSTDESK_API_ADMIN_HELLO_FILE | 后台欢迎语文件,如果内容多,使用文件更方便。<br>会覆盖`RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
||||
| -----GIN配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表,以`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 |
|
||||
| -----GORM配置----- | ---------- | --------------------------- |
|
||||
| RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql,默认sqlite | sqlite |
|
||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 |
|
||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 |
|
||||
| RUSTDESK_API_RUSTDESK_PERSONAL | 是否启用个人版API, 1:启用,0:不启用; 默认启用 | 1 |
|
||||
| -----MYSQL配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_MYSQL_USERNAME | mysql用户名 | root |
|
||||
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
|
||||
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
|
||||
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
|
||||
| -----RUSTDESK配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
|
||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
|
||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
||||
| RUSTDESK_API_RUSTDESK_WEBCLIENT_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
|
||||
| ----PROXY配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
||||
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
||||
| ----JWT配置---- | -------- | -------- |
|
||||
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT<br/>如果没使用`lejianwen/rustdesk-server`中的`MUST_LOGIN`,建议设置为空 | |
|
||||
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | 360000 |
|
||||
| 变量名 | 说明 | 示例 |
|
||||
|--------------------------------------------------------|--------------------------------------------------------------------------------|------------------------------|
|
||||
| TZ | 时区 | Asia/Shanghai |
|
||||
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
|
||||
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
|
||||
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
|
||||
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
|
||||
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长(秒) | `3600` |
|
||||
| -----ADMIN配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
|
||||
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
|
||||
| RUSTDESK_API_ADMIN_HELLO_FILE | 后台欢迎语文件,如果内容多,使用文件更方便。<br>会覆盖`RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
|
||||
| -----GIN配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_GIN_TRUST_PROXY | 信任的代理IP列表,以`,`分割,默认信任所有 | 192.168.1.2,192.168.1.3 |
|
||||
| -----GORM配置----- | ---------- | --------------------------- |
|
||||
| RUSTDESK_API_GORM_TYPE | 数据库类型sqlite或者mysql,默认sqlite | sqlite |
|
||||
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | 数据库最大空闲连接数 | 10 |
|
||||
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | 数据库最大打开连接数 | 100 |
|
||||
| RUSTDESK_API_RUSTDESK_PERSONAL | 是否启用个人版API, 1:启用,0:不启用; 默认启用 | 1 |
|
||||
| -----MYSQL配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_MYSQL_USERNAME | mysql用户名 | root |
|
||||
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
|
||||
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
|
||||
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
|
||||
| -----RUSTDESK配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
|
||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
|
||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk的api服务器地址 | http://192.168.1.66:21114 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
|
||||
| RUSTDESK_API_RUSTDESK_WEBCLIENT<br/>_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
|
||||
| ----PROXY配置----- | ---------- | ---------- |
|
||||
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
|
||||
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
|
||||
| ----JWT配置---- | -------- | -------- |
|
||||
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT<br/>如果没使用`lejianwen/rustdesk-server`中的`MUST_LOGIN`,建议设置为空 | |
|
||||
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | 360000 |
|
||||
|
||||
|
||||
### 运行
|
||||
@@ -367,4 +372,4 @@ ldap:
|
||||
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
|
||||
</a>
|
||||
|
||||
## 如果觉得这个项目对你有帮助,请给一个star,谢谢!
|
||||
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!
|
||||
|
||||
14
README_EN.md
14
README_EN.md
@@ -19,7 +19,10 @@ desktop software that provides self-hosted solutions.
|
||||
- Login
|
||||
- Address Book
|
||||
- Groups
|
||||
- Authorized login, supports `GitHub`, `Google` and `OIDC` login, supports `web admin` authorized login
|
||||
- Authorized login,
|
||||
- supports `GitHub`, `Google` and `OIDC` login,
|
||||
- supports `web admin` authorized login,
|
||||
- supports LDAP(test AD and openladp) if API Server config
|
||||
- i18n
|
||||
- Web Admin
|
||||
- User Management
|
||||
@@ -28,6 +31,7 @@ desktop software that provides self-hosted solutions.
|
||||
- Tag Management
|
||||
- Group Management
|
||||
- OAuth Management
|
||||
- LDAP Config by config file or ENV
|
||||
- Login Logs
|
||||
- Connection Logs
|
||||
- File Transfer Logs
|
||||
@@ -107,8 +111,8 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
11. **LDAP Support**, When you setup the LDAP(test for OpenLDAP and AD), you can login with the LDAP's user. https://github.com/lejianwen/rustdesk-api/issues/114 , if LDAP fail fallback local user
|
||||
|
||||
### Web Client:
|
||||
|
||||
1. If you're already logged into the admin panel, the web client will log in automatically.
|
||||
@@ -238,7 +242,7 @@ The table below does not list all configurations. Please refer to the configurat
|
||||
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
|
||||
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
|
||||
| RUSTDESK_API_RUSTDESK_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
|
||||
| RUSTDESK_API_RUSTDESK<br/>_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
|
||||
| ---- PROXY ----- | --------------- | ---------- |
|
||||
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
|
||||
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
|
||||
@@ -367,4 +371,4 @@ Thanks to everyone who contributed!
|
||||
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
|
||||
</a>
|
||||
|
||||
## If you find this project helpful, please give it a star, thank you!
|
||||
## Thanks for your support! If you find this project useful, please give it a ⭐️. Thank you!
|
||||
@@ -56,19 +56,20 @@ func Init(rowVal *Config, path string) *viper.Viper {
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
||||
}
|
||||
v.WatchConfig()
|
||||
|
||||
/*
|
||||
//监听配置修改没什么必要
|
||||
v.OnConfigChange(func(e fsnotify.Event) {
|
||||
//配置文件修改监听
|
||||
fmt.Println("config file changed:", e.Name)
|
||||
if err2 := v.Unmarshal(rowVal); err2 != nil {
|
||||
fmt.Println(err2)
|
||||
}
|
||||
rowVal.Rustdesk.LoadKeyFile()
|
||||
rowVal.Rustdesk.ParsePort()
|
||||
})
|
||||
v.WatchConfig()
|
||||
|
||||
|
||||
//监听配置修改没什么必要
|
||||
v.OnConfigChange(func(e fsnotify.Event) {
|
||||
//配置文件修改监听
|
||||
fmt.Println("config file changed:", e.Name)
|
||||
if err2 := v.Unmarshal(rowVal); err2 != nil {
|
||||
fmt.Println(err2)
|
||||
}
|
||||
rowVal.Rustdesk.LoadKeyFile()
|
||||
rowVal.Rustdesk.ParsePort()
|
||||
})
|
||||
*/
|
||||
if err := v.Unmarshal(rowVal); err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
@@ -182,15 +182,20 @@ func (ct *Login) Login(c *gin.Context) {
|
||||
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp))
|
||||
loginLimiter.RecordFailure(clientIp)
|
||||
if loginLimiter.NeedsCaptcha(clientIp) {
|
||||
// 移除原验证码,重新生成
|
||||
loginLimiter.RemoveCaptcha(clientIp)
|
||||
response.Fail(c, 110, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||
return
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
||||
return
|
||||
}
|
||||
|
||||
if !service.AllService.UserService.CheckUserEnable(u) {
|
||||
if loginLimiter.NeedsCaptcha(clientIp) {
|
||||
loginLimiter.RemoveCaptcha(clientIp)
|
||||
}
|
||||
response.Fail(c, 101, response.TranslateMsg(c, "UserDisabled"))
|
||||
return
|
||||
}
|
||||
|
||||
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
||||
UserId: u.Id,
|
||||
Client: model.LoginLogClientWebAdmin,
|
||||
|
||||
@@ -51,6 +51,11 @@ func (l *Login) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !service.AllService.UserService.CheckUserEnable(u) {
|
||||
response.Error(c, response.TranslateMsg(c, "UserDisabled"))
|
||||
return
|
||||
}
|
||||
|
||||
//根据refer判断是webclient还是app
|
||||
ref := c.GetHeader("referer")
|
||||
if ref != "" {
|
||||
|
||||
@@ -24,6 +24,14 @@ func AdminAuth() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if !service.AllService.UserService.CheckUserEnable(user) {
|
||||
c.JSON(401, gin.H{
|
||||
"error": "Unauthorized",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("curUser", user)
|
||||
c.Set("token", token)
|
||||
//如果时间小于1天,token自动续期
|
||||
|
||||
105
service/ldap.go
105
service/ldap.go
@@ -4,15 +4,30 @@ import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
|
||||
"Gwen/config"
|
||||
"Gwen/global"
|
||||
"Gwen/model"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLdapNotEnabled = errors.New("LdapNotEnabled")
|
||||
ErrLdapUserDisabled = errors.New("UserDisabledAtLdap")
|
||||
ErrLdapUserNotFound = errors.New("UserNotFound")
|
||||
ErrLdapMailNotMatch = errors.New("MailNotMatch")
|
||||
ErrLdapConnectFailed = errors.New("LdapConnectFailed")
|
||||
ErrLdapSearchFailed = errors.New("LdapSearchRequestFailed")
|
||||
ErrLdapTlsFailed = errors.New("LdapStartTLSFailed")
|
||||
ErrLdapBindService = errors.New("LdapBindServiceFailed")
|
||||
ErrLdapBindFailed = errors.New("LdapBindFailed")
|
||||
ErrLdapToLocalUserFailed = errors.New("LdapToLocalUserFailed")
|
||||
ErrLdapCreateUserFailed = errors.New("LdapCreateUserFailed")
|
||||
)
|
||||
|
||||
// LdapService is responsible for LDAP authentication and user synchronization.
|
||||
type LdapService struct {
|
||||
}
|
||||
@@ -43,6 +58,11 @@ func (lu *LdapUser) ToUser(u *model.User) *model.User {
|
||||
u.Username = lu.Username
|
||||
u.Email = lu.Email
|
||||
u.Nickname = lu.Name()
|
||||
if lu.Enabled {
|
||||
u.Status = model.COMMON_STATUS_ENABLE
|
||||
} else {
|
||||
u.Status = model.COMMON_STATUS_DISABLED
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
@@ -50,21 +70,21 @@ func (lu *LdapUser) ToUser(u *model.User) *model.User {
|
||||
func (ls *LdapService) connectAndBind(cfg *config.Ldap, username, password string) (*ldap.Conn, error) {
|
||||
conn, err := ldap.DialURL(cfg.Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial LDAP: %w", err)
|
||||
return nil, errors.Join(ErrLdapConnectFailed, err)
|
||||
}
|
||||
|
||||
if cfg.TLS {
|
||||
// WARNING: InsecureSkipVerify: true is not recommended for production
|
||||
if err = conn.StartTLS(&tls.Config{InsecureSkipVerify: !cfg.TlsVerify}); err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("failed to start TLS: %w", err)
|
||||
return nil, errors.Join(ErrLdapTlsFailed, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind as the "service" user
|
||||
if err = conn.Bind(username, password); err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("failed to bind with service account: %w", err)
|
||||
return nil, errors.Join(ErrLdapBindService, err)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
@@ -87,29 +107,17 @@ func (ls *LdapService) verifyCredentials(cfg *config.Ldap, username, password st
|
||||
// Authenticate checks the provided username and password against LDAP.
|
||||
// Returns the corresponding *model.User if successful, or an error if not.
|
||||
func (ls *LdapService) Authenticate(username, password string) (*model.User, error) {
|
||||
cfg := &global.Config.Ldap
|
||||
// 1. Use a service bind to search for the user DN
|
||||
sr, err := ls.usernameSearchResult(cfg, username)
|
||||
ldapUser, err := ls.GetUserInfoByUsernameLdap(username)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LDAP search request failed: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(sr.Entries) != 1 {
|
||||
return nil, errors.New("user does not exist or too many entries returned")
|
||||
}
|
||||
entry := sr.Entries[0]
|
||||
userDN := entry.DN
|
||||
|
||||
err = ls.verifyCredentials(cfg, userDN, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LDAP authentication failed: %w", err)
|
||||
}
|
||||
ldapUser := ls.userResultToLdapUser(cfg, entry)
|
||||
if !ldapUser.Enabled {
|
||||
return nil, errors.New("UserDisabledAtLdap")
|
||||
return nil, ErrLdapUserDisabled
|
||||
}
|
||||
cfg := &global.Config.Ldap
|
||||
user, err := ls.mapToLocalUser(cfg, ldapUser)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to map LDAP user to local user: %w", err)
|
||||
return nil, errors.Join(ErrLdapToLocalUserFailed, err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
@@ -126,8 +134,9 @@ func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.Us
|
||||
// Typically, you don’t store LDAP user passwords locally.
|
||||
// If needed, you can set a random password here.
|
||||
newUser.IsAdmin = &isAdmin
|
||||
newUser.GroupId = 1
|
||||
if err := global.DB.Create(newUser).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to create new user: %w", err)
|
||||
return nil, errors.Join(ErrLdapCreateUserFailed, err)
|
||||
}
|
||||
return userService.InfoByUsername(lu.Username), nil
|
||||
}
|
||||
@@ -137,6 +146,7 @@ func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.Us
|
||||
originalEmail := localUser.Email
|
||||
originalNickname := localUser.Nickname
|
||||
originalIsAdmin := localUser.IsAdmin
|
||||
originalStatus := localUser.Status
|
||||
lu.ToUser(localUser) // merges LDAP data into the existing user
|
||||
localUser.IsAdmin = &isAdmin
|
||||
if err := userService.Update(localUser); err != nil {
|
||||
@@ -144,6 +154,7 @@ func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.Us
|
||||
localUser.Email = originalEmail
|
||||
localUser.Nickname = originalNickname
|
||||
localUser.IsAdmin = originalIsAdmin
|
||||
localUser.Status = originalStatus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +188,56 @@ func (ls *LdapService) IsEmailExists(email string) bool {
|
||||
return len(sr.Entries) > 0
|
||||
}
|
||||
|
||||
// GetUserInfoByUsernameLdap returns the user info from LDAP for the given username.
|
||||
func (ls *LdapService) GetUserInfoByUsernameLdap(username string) (*LdapUser, error) {
|
||||
cfg := &global.Config.Ldap
|
||||
if !cfg.Enable {
|
||||
return nil, ErrLdapNotEnabled
|
||||
}
|
||||
sr, err := ls.usernameSearchResult(cfg, username)
|
||||
if err != nil {
|
||||
return nil, errors.Join(ErrLdapSearchFailed, err)
|
||||
}
|
||||
if len(sr.Entries) != 1 {
|
||||
return nil, ErrLdapUserNotFound
|
||||
}
|
||||
return ls.userResultToLdapUser(cfg, sr.Entries[0]), nil
|
||||
}
|
||||
|
||||
// GetUserInfoByUsernameLocal returns the user info from LDAP for the given username. If the user exists, it will sync the user info to the local database.
|
||||
func (ls *LdapService) GetUserInfoByUsernameLocal(username string) (*model.User, error) {
|
||||
ldapUser, err := ls.GetUserInfoByUsernameLdap(username)
|
||||
if err != nil {
|
||||
return &model.User{}, err
|
||||
}
|
||||
return ls.mapToLocalUser(&global.Config.Ldap, ldapUser)
|
||||
}
|
||||
|
||||
// GetUserInfoByEmailLdap returns the user info from LDAP for the given email.
|
||||
func (ls *LdapService) GetUserInfoByEmailLdap(email string) (*LdapUser, error) {
|
||||
cfg := &global.Config.Ldap
|
||||
if !cfg.Enable {
|
||||
return nil, ErrLdapNotEnabled
|
||||
}
|
||||
sr, err := ls.emailSearchResult(cfg, email)
|
||||
if err != nil {
|
||||
return nil, errors.Join(ErrLdapSearchFailed, err)
|
||||
}
|
||||
if len(sr.Entries) != 1 {
|
||||
return nil, ErrLdapUserNotFound
|
||||
}
|
||||
return ls.userResultToLdapUser(cfg, sr.Entries[0]), nil
|
||||
}
|
||||
|
||||
// GetUserInfoByEmailLocal returns the user info from LDAP for the given email. if the user exists, it will synchronize the user information to local database.
|
||||
func (ls *LdapService) GetUserInfoByEmailLocal(email string) (*model.User, error) {
|
||||
ldapUser, err := ls.GetUserInfoByEmailLdap(email)
|
||||
if err != nil {
|
||||
return &model.User{}, err
|
||||
}
|
||||
return ls.mapToLocalUser(&global.Config.Ldap, ldapUser)
|
||||
}
|
||||
|
||||
// usernameSearchResult returns the search result for the given username.
|
||||
func (ls *LdapService) usernameSearchResult(cfg *config.Ldap, username string) (*ldap.SearchResult, error) {
|
||||
// Build the combined filter for the username
|
||||
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
"Gwen/model"
|
||||
"Gwen/utils"
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
@@ -322,7 +323,16 @@ func (us *UserService) RegisterByOauth(oauthUser *model.OauthUser, op string) (e
|
||||
email = strings.ToLower(email)
|
||||
// update email to oauthUser, in case it contain upper case
|
||||
oauthUser.Email = email
|
||||
user := us.InfoByEmail(email)
|
||||
// call this, if find user by email, it will update the email to local database
|
||||
user, ldapErr := AllService.LdapService.GetUserInfoByEmailLocal(email)
|
||||
// If we enable ldap, and the error is not ErrLdapUserNotFound, return the error because we could not sure if the user is not found in ldap
|
||||
if !(errors.Is(ldapErr, ErrLdapNotEnabled) || errors.Is(ldapErr, ErrLdapUserNotFound) || ldapErr == nil) {
|
||||
return ldapErr, user
|
||||
}
|
||||
if user.Id == 0 {
|
||||
// this means the user is not found in ldap, maybe ldao is not enabled
|
||||
user = us.InfoByEmail(email)
|
||||
}
|
||||
if user.Id != 0 {
|
||||
ut.FromOauthUser(user.Id, oauthUser, oauthType, op)
|
||||
global.DB.Create(ut)
|
||||
@@ -491,8 +501,15 @@ func (us *UserService) VerifyJWT(token string) (uint, error) {
|
||||
|
||||
// IsUsernameExists 判断用户名是否存在, it will check the internal database and LDAP(if enabled)
|
||||
func (us *UserService) IsUsernameExists(username string) bool {
|
||||
return us.IsUsernameExistsLocal(username) || AllService.LdapService.IsUsernameExists(username)
|
||||
}
|
||||
|
||||
func (us *UserService) IsUsernameExistsLocal(username string) bool {
|
||||
u := &model.User{}
|
||||
global.DB.Where("username = ?", username).First(u)
|
||||
existsInLdap := AllService.LdapService.IsUsernameExists(username)
|
||||
return u.Id != 0 || existsInLdap
|
||||
return u.Id != 0
|
||||
}
|
||||
|
||||
func (us *UserService) IsEmailExistsLdap(email string) bool {
|
||||
return AllService.LdapService.IsEmailExists(email)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user