Compare commits

...

18 Commits

Author SHA1 Message Date
lejianwen
6d9c245c81 fix(webclient): port 2025-02-16 14:17:37 +08:00
lejianwen
7fa9b79f31 fix(webclient): port 2025-02-16 14:11:36 +08:00
lejianwen
c7f3d13b7f fix(webclient): port 2025-02-16 13:33:06 +08:00
lejianwen
46f08a89d2 feat: Login by pwd can be disable
---

Closes: #141
2025-02-16 13:06:45 +08:00
lejianwen
0dcfedb4dc fix(webclient)!: Webclient path is /ws/(relay|id) (#73 #143 #140)
Webclient 的反代引发了很多问题,现在将在HTTPS下的path固定为`/ws/(relay|id)`

---

Closes: #143 #140
2025-02-16 12:41:32 +08:00
lejianwen
918bf85a2d style: middleware name 2025-02-12 22:09:52 +08:00
lejianwen
99db5f7190 fix(admin): Admin web title 2025-02-12 21:10:07 +08:00
lejianwen
18eff791b2 style: Module name 2025-02-12 19:46:39 +08:00
lejianwen
624dcacac5 style: generate 2025-02-12 16:56:41 +08:00
lejianwen
878d5fd27c style: Remove generate 2025-02-12 16:25:04 +08:00
lejianwen
4b893ce0e8 docs: Docs 2025-02-12 16:14:51 +08:00
lejianwen
472524f836 style: Module name 2025-02-12 16:07:51 +08:00
lejianwen
dbf8b23b15 fix: Config watchConfig (#135)
---
Closes: #135
2025-02-10 10:13:34 +08:00
lejianwen
79a5dd53ae fix: User disabled can not work (#133)
---
Closes: #133
2025-02-10 10:13:15 +08:00
Tao Chen
8a5b20685c fix: When OIDC and LDAP work togethar (#132 #134)
* fix OIDC create user if LDAP enable

* `newUser.GroupId = 1` for ldap

* fix
2025-02-10 10:08:49 +08:00
lejianwen
5a9c972de0 docs: Readme 2025-02-09 21:13:01 +08:00
Tao Chen
fc0e67122d docs: add LDAP info (#130) 2025-02-09 19:36:31 +08:00
lejianwen
eb642f66ca docs: Readme 2025-02-07 18:14:12 +08:00
97 changed files with 614 additions and 444 deletions

View File

@@ -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 @@
![rustdesk_command_advance](./docs/rustdesk_command_advance.png)
11. **LDAP 支持**, 当在API Server上设置了LDAP(已测试AD和LDAP),可以通过LDAP中的用户信息进行登录 https://github.com/lejianwen/rustdesk-api/issues/114 ,如果LDAP验证失败返回本地用户
### Web Client:
@@ -209,7 +214,7 @@ ldap:
下面表格并未全部列出,可以参考`conf/config.yaml`中的配置。
| 变量名 | 说明 | 示例 |
|---------------------------------------------------|--------------------------------------------------------------------------------|------------------------------|
|--------------------------------------------------------|--------------------------------------------------------------------------------|------------------------------|
| TZ | 时区 | Asia/Shanghai |
| RUSTDESK_API_LANG | 语言 | `en`,`zh-CN` |
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
@@ -238,7 +243,7 @@ ldap:
| 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` |
| 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` |
@@ -367,4 +372,4 @@ ldap:
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
</a>
## 如果觉得这个项目对你有帮助,请给一个star,谢谢!
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!

View File

@@ -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,7 +111,7 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
![rustdesk_command_advance](./docs/en_img/rustdesk_command_advance.png)
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:
@@ -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!

View File

@@ -1,19 +1,19 @@
package main
import (
"Gwen/config"
"Gwen/global"
"Gwen/http"
"Gwen/lib/cache"
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/logger"
"Gwen/lib/orm"
"Gwen/lib/upload"
"Gwen/model"
"Gwen/service"
"Gwen/utils"
"github.com/go-redis/redis/v8"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http"
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
"github.com/lejianwen/rustdesk-api/v2/lib/logger"
"github.com/lejianwen/rustdesk-api/v2/lib/orm"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/spf13/cobra"
"os"
@@ -186,12 +186,12 @@ func DatabaseAutoUpdate() {
// 获取底层的 *sql.DB 对象,并确保在程序退出时关闭连接
sqlDBWithoutDB, err := dbWithoutDB.DB()
if err != nil {
global.Logger.Error("获取底层 *sql.DB 对象失败: %v\n", err)
global.Logger.Errorf("获取底层 *sql.DB 对象失败: %v", err)
return
}
defer func() {
if err := sqlDBWithoutDB.Close(); err != nil {
global.Logger.Error("关闭连接失败: %v\n", err)
global.Logger.Errorf("关闭连接失败: %v", err)
}
}()

View File

@@ -1 +1 @@
### 👏👏👏 你好 ***{{username}}*** 欢迎使用 [RustDesk Api](https://github.com/lejianwen/rustdesk-api)
### 👏👏👏 你好 ***{{username}}*** 欢迎使用 [RustDesk API](https://github.com/lejianwen/rustdesk-api)

View File

@@ -5,6 +5,7 @@ app:
show-swagger: 0 # 1:启用 0:禁用
token-expire: 360000
web-sso: true #web auth sso
disable-pwd-login: false #禁用密码登录
admin:
title: "RustDesk Api Admin"
hello-file: "./conf/admin/hello.html" #优先使用file

View File

@@ -18,6 +18,7 @@ type App struct {
ShowSwagger int `mapstructure:"show-swagger"`
TokenExpire int `mapstructure:"token-expire"`
WebSso bool `mapstructure:"web-sso"`
DisablePwdLogin bool `mapstructure:"disable-pwd-login"`
}
type Admin struct {
Title string `mapstructure:"title"`
@@ -56,9 +57,10 @@ 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) {
//配置文件修改监听

View File

@@ -1783,7 +1783,7 @@ const docTemplateadmin = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Gwen_http_request_admin.Login"
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_http_request_admin.Login"
}
}
],
@@ -5104,27 +5104,6 @@ const docTemplateadmin = `{
}
},
"definitions": {
"Gwen_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"admin.AddressBookForm": {
"type": "object",
"required": [
@@ -5542,6 +5521,27 @@ const docTemplateadmin = `{
}
}
},
"github_com_lejianwen_rustdesk-api_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"model.AddressBook": {
"type": "object",
"properties": {

View File

@@ -1776,7 +1776,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Gwen_http_request_admin.Login"
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_http_request_admin.Login"
}
}
],
@@ -5097,27 +5097,6 @@
}
},
"definitions": {
"Gwen_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"admin.AddressBookForm": {
"type": "object",
"required": [
@@ -5535,6 +5514,27 @@
}
}
},
"github_com_lejianwen_rustdesk-api_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"model.AddressBook": {
"type": "object",
"properties": {

View File

@@ -1,19 +1,5 @@
basePath: /api
definitions:
Gwen_http_request_admin.Login:
properties:
captcha:
type: string
password:
type: string
platform:
type: string
username:
type: string
required:
- password
- username
type: object
admin.AddressBookForm:
properties:
alias:
@@ -292,6 +278,20 @@ definitions:
required:
- ids
type: object
github_com_lejianwen_rustdesk-api_http_request_admin.Login:
properties:
captcha:
type: string
password:
type: string
platform:
type: string
username:
type: string
required:
- password
- username
type: object
model.AddressBook:
properties:
alias:
@@ -1830,7 +1830,7 @@ paths:
name: body
required: true
schema:
$ref: '#/definitions/Gwen_http_request_admin.Login'
$ref: '#/definitions/github_com_lejianwen_rustdesk-api_http_request_admin.Login'
produces:
- application/json
responses:

View File

@@ -1,4 +1,4 @@
package Gwen
package main
//go:generate swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
//go:generate swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api

View File

@@ -1,3 +1,3 @@
package Gwen
package main
//go:generate go run cmd/apimain.go

View File

@@ -1,15 +1,15 @@
package global
import (
"Gwen/config"
"Gwen/lib/cache"
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/upload"
"github.com/gin-gonic/gin"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"github.com/go-redis/redis/v8"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"

View File

@@ -15,7 +15,6 @@ func InitI18n() {
fileInfos, err := os.ReadDir(dir)
if err != nil {
panic(err)
return
}
for _, fileInfo := range fileInfos {
//如果文件名不是.toml结尾

2
go.mod
View File

@@ -1,4 +1,4 @@
module Gwen
module github.com/lejianwen/rustdesk-api/v2
go 1.22

View File

@@ -1,13 +1,13 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json"
_ "encoding/json"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,10 +1,10 @@
package admin
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"os"
"strings"
)
@@ -61,6 +61,12 @@ func (co *Config) AppConfig(c *gin.Context) {
func (co *Config) AdminConfig(c *gin.Context) {
u := service.AllService.UserService.CurUser(c)
if u == nil || u.Id == 0 {
response.Success(c, &gin.H{
"title": global.Config.Admin.Title,
})
return
}
hello := global.Config.Admin.Hello
helloFile := global.Config.Admin.HelloFile
if helloFile != "" {

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/lib/upload"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"os"
"time"
)

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"strconv"
)

View File

@@ -1,16 +1,16 @@
package admin
import (
"Gwen/global"
"Gwen/http/controller/api"
"Gwen/http/request/admin"
apiReq "Gwen/http/request/api"
"Gwen/http/response"
adResp "Gwen/http/response/admin"
"Gwen/model"
"Gwen/service"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/mojocn/base64Captcha"
"sync"
"time"
@@ -152,6 +152,10 @@ var loginLimiter = NewLoginLimiter(3, 5*time.Minute)
// @Router /admin/login [post]
// @Security token
func (ct *Login) Login(c *gin.Context) {
if global.Config.App.DisablePwdLogin {
response.Fail(c, 101, response.TranslateMsg(c, "PwdLoginDisabled"))
return
}
f := &admin.Login{}
err := c.ShouldBindJSON(f)
clientIp := c.ClientIP()
@@ -182,15 +186,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,

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,10 +1,10 @@
package my
import (
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"time"
)

View File

@@ -1,11 +1,11 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,11 +1,11 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
adminReq "Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
adminReq "github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"strconv"
)

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
"time"

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
)
type Rustdesk struct {

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,13 +1,13 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
adResp "Gwen/http/response/admin"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,16 +1,16 @@
package api
import (
"Gwen/global"
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"Gwen/utils"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"net/http"
"strconv"
"strings"

View File

@@ -1,12 +1,12 @@
package api
import (
request "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
request "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"time"
)

View File

@@ -1,12 +1,12 @@
package api
import (
apiReq "Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)

View File

@@ -1,11 +1,11 @@
package api
import (
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
"os"
"time"

View File

@@ -1,15 +1,15 @@
package api
import (
"Gwen/global"
"Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)
@@ -27,6 +27,10 @@ type Login struct {
// @Failure 500 {object} response.ErrorResponse
// @Router /login [post]
func (l *Login) Login(c *gin.Context) {
if global.Config.App.DisablePwdLogin {
response.Error(c, response.TranslateMsg(c, "PwdLoginDisabled"))
return
}
f := &api.LoginForm{}
err := c.ShouldBindJSON(f)
//fmt.Println(f)
@@ -51,6 +55,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 != "" {

View File

@@ -1,13 +1,13 @@
package api
import (
"Gwen/global"
"Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)

View File

@@ -1,11 +1,11 @@
package api
import (
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)

View File

@@ -1,9 +1,9 @@
package api
import (
apiResp "Gwen/http/response/api"
"Gwen/service"
"github.com/gin-gonic/gin"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)

View File

@@ -1,11 +1,11 @@
package api
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/http/response/api"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/service"
"time"
)

View File

@@ -1,8 +1,8 @@
package web
import (
"Gwen/global"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"strconv"
)

View File

@@ -1,10 +1,10 @@
package http
import (
"Gwen/global"
"Gwen/http/middleware"
"Gwen/http/router"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
"github.com/lejianwen/rustdesk-api/v2/http/router"
"github.com/sirupsen/logrus"
"net/http"
"strings"

View File

@@ -1,13 +1,13 @@
package middleware
import (
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
)
// AdminAuth 后台权限验证中间件
func AdminAuth() gin.HandlerFunc {
// BackendUserAuth 后台权限验证中间件
func BackendUserAuth() gin.HandlerFunc {
return func(c *gin.Context) {
//测试先关闭
@@ -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自动续期

View File

@@ -1,9 +1,9 @@
package middleware
import (
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
)
// AdminPrivilege ...

View File

@@ -1,10 +1,10 @@
package middleware
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
)
func JwtAuth() gin.HandlerFunc {

View File

@@ -1,8 +1,8 @@
package middleware
import (
"Gwen/global"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/sirupsen/logrus"
)

View File

@@ -1,9 +1,9 @@
package middleware
import (
"Gwen/global"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/service"
)
func RustAuth() gin.HandlerFunc {

View File

@@ -1,8 +1,8 @@
package admin
import (
"Gwen/model"
"encoding/json"
"github.com/lejianwen/rustdesk-api/v2/model"
)
type AddressBookForm struct {

View File

@@ -1,6 +1,6 @@
package admin
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
type GroupForm struct {
Id uint `json:"id"`

View File

@@ -1,7 +1,7 @@
package admin
import (
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/model"
)
type BindOauthForm struct {

View File

@@ -1,6 +1,6 @@
package admin
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
type PeerForm struct {
RowId uint `json:"row_id" `

View File

@@ -1,6 +1,6 @@
package admin
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
type TagForm struct {
Id uint `json:"id"`

View File

@@ -1,7 +1,7 @@
package admin
import (
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/model"
)
type UserForm struct {

View File

@@ -1,9 +1,9 @@
package api
import (
"Gwen/global"
"Gwen/model"
"encoding/json"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"strconv"
)

View File

@@ -1,6 +1,6 @@
package api
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
type AddressBookFormData struct {
Tags []string `json:"tags"`

View File

@@ -1,6 +1,6 @@
package admin
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
type LoginPayload struct {
Username string `json:"username"`

View File

@@ -1,6 +1,6 @@
package api
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
type AbList struct {
Peers []*model.AddressBook `json:"peers,omitempty"`

View File

@@ -1,6 +1,6 @@
package api
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
/*
GroupPeerPayload

View File

@@ -1,6 +1,6 @@
package api
import "Gwen/model"
import "github.com/lejianwen/rustdesk-api/v2/model"
/*
pub enum UserStatus {

View File

@@ -1,7 +1,7 @@
package api
import (
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/model"
"time"
)

View File

@@ -1,9 +1,9 @@
package response
import (
"Gwen/global"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/nicksnyder/go-i18n/v2/i18n"
"net/http"
)

View File

@@ -1,12 +1,12 @@
package router
import (
_ "Gwen/docs/admin"
"Gwen/global"
"Gwen/http/controller/admin"
"Gwen/http/controller/admin/my"
"Gwen/http/middleware"
"github.com/gin-gonic/gin"
_ "github.com/lejianwen/rustdesk-api/v2/docs/admin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/admin"
"github.com/lejianwen/rustdesk-api/v2/http/controller/admin/my"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
@@ -22,7 +22,10 @@ func Init(g *gin.Engine) {
adg := g.Group("/api/admin")
LoginBind(adg)
adg.POST("/user/register", (&admin.User{}).Register)
adg.Use(middleware.AdminAuth())
ConfigBind(adg)
adg.Use(middleware.BackendUserAuth())
//FileBind(adg)
UserBind(adg)
GroupBind(adg)
@@ -35,7 +38,6 @@ func Init(g *gin.Engine) {
AddressBookCollectionBind(adg)
AddressBookCollectionRuleBind(adg)
UserTokenBind(adg)
ConfigBind(adg)
//deprecated by ConfigBind
//rs := &admin.Rustdesk{}
@@ -221,9 +223,13 @@ func UserTokenBind(rg *gin.RouterGroup) {
func ConfigBind(rg *gin.RouterGroup) {
aR := rg.Group("/config")
rs := &admin.Config{}
aR.GET("/admin", rs.AdminConfig)
aR.Use(middleware.BackendUserAuth())
aR.GET("/server", rs.ServerConfig)
aR.GET("/app", rs.AppConfig)
aR.GET("/admin", rs.AdminConfig)
}
/*

View File

@@ -1,11 +1,11 @@
package router
import (
_ "Gwen/docs/api"
"Gwen/global"
"Gwen/http/controller/api"
"Gwen/http/middleware"
"github.com/gin-gonic/gin"
_ "github.com/lejianwen/rustdesk-api/v2/docs/api"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"net/http"

View File

@@ -1,9 +1,9 @@
package router
import (
"Gwen/global"
"Gwen/http/controller/web"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/web"
"net/http"
)

View File

@@ -1,8 +1,8 @@
package orm
import (
"Gwen/global"
"fmt"
"github.com/lejianwen/rustdesk-api/v2/global"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"

View File

@@ -1,8 +1,8 @@
package orm
import (
"Gwen/global"
"fmt"
"github.com/lejianwen/rustdesk-api/v2/global"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"

View File

@@ -1,6 +1,6 @@
package model
import "Gwen/model/custom_types"
import "github.com/lejianwen/rustdesk-api/v2/model/custom_types"
// final String id;
// String hash; // personal ab hash password

View File

@@ -1,7 +1,7 @@
package model
import (
"Gwen/model/custom_types"
"github.com/lejianwen/rustdesk-api/v2/model/custom_types"
)
type StatusCode int

View File

@@ -133,3 +133,8 @@ other = "Captcha required."
description = "Captcha error."
one = "Captcha error."
other = "Captcha error."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Password login disabled."
other = "Password login disabled."

View File

@@ -142,3 +142,8 @@ other = "Captcha requerido."
description = "Captcha error."
one = "Error de captcha."
other = "Error de captcha."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Inicio de sesión con contraseña deshabilitado."
other = "Inicio de sesión con contraseña deshabilitado."

View File

@@ -142,3 +142,8 @@ other = "Captcha requis."
description = "Captcha error."
one = "Erreur de captcha."
other = "Erreur de captcha."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Connexion par mot de passe désactivée."
other = "Connexion par mot de passe désactivée."

View File

@@ -136,3 +136,8 @@ other = "Captcha가 필요합니다."
description = "Captcha error."
one = "Captcha 오류."
other = "Captcha 오류."
[PwdLoginDisabled]
description = "Password login disabled."
one = "비밀번호 로그인이 비활성화되었습니다."
other = "비밀번호 로그인이 비활성화되었습니다."

View File

@@ -142,3 +142,8 @@ other = "Требуется капча."
description = "Captcha error."
one = "Ошибка капчи."
other = "Ошибка капчи."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Вход по паролю отключен."
other = "Вход по паролю отключен."

View File

@@ -135,3 +135,8 @@ other = "需要验证码。"
description = "Captcha error."
one = "验证码错误。"
other = "验证码错误。"
[PwdLoginDisabled]
description = "Password login disabled."
one = "密码登录已禁用。"
other = "密码登录已禁用。"

View File

@@ -135,3 +135,8 @@ other = "需要驗證碼。"
description = "Captcha error."
one = "驗證碼錯誤。"
other = "驗證碼錯誤。"
[PwdLoginDisabled]
description = "Password login disabled."
one = "密碼登錄已禁用。"
other = "密碼登錄已禁用。"

View File

@@ -11090,16 +11090,24 @@ function R4(u = !1) {
function getUriFromRs(uri, isRelay = false, roffset = 0) {
const p = isHttps() ? "wss://" : "ws://"
const [domain, uriport] = uri.split(":")
if (isHttps() && (!uriport)) {
return p + domain + "/ws/" + (isRelay ? "relay" : "id");
}
if (!isHttps()) {
// http 直接走端口
if (uriport) {
const port = parseInt(uriport);
uri = domain + ":" + (port + (isRelay ? roffset || 3 : 2))
} else uri += ":" + (defaultIdServerPort + (isRelay ? 3 : 2));
return p + uri
const port = parseInt(uriport)
return p + domain + ":" + (port + (isRelay ? roffset || 3 : 2))
}
return p + domain + ":" + (defaultIdServerPort + (isRelay ? 3 : 2))
}
// https 分情况
if (!window.location.port) {
// 443
return p + domain + "/ws/" + (isRelay ? "relay" : "id")
}
// 非443
return p + domain + ":" + window.location.port + "/ws/" + (isRelay ? "relay" : "id")
}
function isHttps() {
return window.location.protocol === "https:"
}

View File

@@ -1,10 +1,10 @@
package service
import (
"Gwen/global"
"Gwen/model"
"encoding/json"
"github.com/google/uuid"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
"strings"
)

View File

@@ -1,8 +1,8 @@
package service
import (
"Gwen/global"
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
)

View File

@@ -1,8 +1,8 @@
package service
import (
"Gwen/global"
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
)

View File

@@ -4,13 +4,28 @@ import (
"crypto/tls"
"errors"
"fmt"
"github.com/go-ldap/ldap/v3"
"strconv"
"strings"
"Gwen/config"
"Gwen/global"
"Gwen/model"
"github.com/go-ldap/ldap/v3"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/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.
@@ -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 dont 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

View File

@@ -1,8 +1,8 @@
package service
import (
"Gwen/global"
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
)

View File

@@ -1,12 +1,12 @@
package service
import (
"Gwen/global"
"Gwen/model"
"Gwen/utils"
"context"
"encoding/json"
"errors"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/utils"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
// "golang.org/x/oauth2/google"

View File

@@ -1,8 +1,8 @@
package service
import (
"Gwen/global"
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
)

View File

@@ -1,9 +1,9 @@
package service
import (
"Gwen/global"
"Gwen/model"
"fmt"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"net"
"time"
)

View File

@@ -1,7 +1,7 @@
package service
import (
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
)

View File

@@ -1,8 +1,8 @@
package service
import (
"Gwen/global"
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
)

View File

@@ -1,8 +1,8 @@
package service
import (
"Gwen/global"
"Gwen/model"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"gorm.io/gorm"
)

View File

@@ -1,16 +1,17 @@
package service
import (
"Gwen/global"
"Gwen/model"
"Gwen/utils"
"errors"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/utils"
"math/rand"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type UserService struct {
@@ -51,7 +52,7 @@ func (us *UserService) InfoByUsernamePassword(username, password string) *model.
if err == nil {
return u
}
global.Logger.Error("LDAP authentication failed, %v", err)
global.Logger.Errorf("LDAP authentication failed, %v", err)
global.Logger.Warn("Fallback to local database")
}
u := &model.User{}
@@ -279,7 +280,7 @@ func (us *UserService) UpdatePassword(u *model.User, password string) error {
// IsAdmin 是否管理员
func (us *UserService) IsAdmin(u *model.User) bool {
return *u.IsAdmin
return u != nil && *u.IsAdmin
}
// RouteNames
@@ -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)
}