mirror of
https://github.com/lejianwen/rustdesk-api.git
synced 2026-02-13 01:30:53 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e318f1fd58 | ||
|
|
ee19eb9729 | ||
|
|
fcdea1ad6d | ||
|
|
c88d8cc359 |
14
README.md
14
README.md
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
[English Doc](README_EN.md)
|
[English Doc](README_EN.md)
|
||||||
|
|
||||||
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web Admin 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
|
本项目使用 Go 实现了 RustDesk 的 API,并包含了 Web Admin 和 Web 客户端。
|
||||||
|
|
||||||
|
|
||||||
<div align=center>
|
<div align=center>
|
||||||
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
|
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
|
||||||
@@ -13,6 +14,14 @@
|
|||||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## 搭配[lejianwen/rustdesk-server]使用更佳。
|
||||||
|
> [lejianwen/rustdesk-server]fork自RustDesk Server官方仓库
|
||||||
|
> 1. 解决了使用API链接超时问题
|
||||||
|
> 2. 可以强制登录后才能发起链接
|
||||||
|
> 3. 支持客户端websocket
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 特性
|
# 特性
|
||||||
|
|
||||||
- PC端API
|
- PC端API
|
||||||
@@ -182,6 +191,7 @@
|
|||||||
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
|
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
|
||||||
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
|
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
|
||||||
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
|
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
|
||||||
|
| RUSTDESK_API_MYSQL_TLS | 是否启用TLS, 可选值: `true`, `false`, `skip-verify`, `custom` | `false` |
|
||||||
| -----RUSTDESK配置----- | ---------- | ---------- |
|
| -----RUSTDESK配置----- | ---------- | ---------- |
|
||||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
|
| 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_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
|
||||||
@@ -325,3 +335,5 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!
|
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!
|
||||||
|
|
||||||
|
[lejianwen/rustdesk-server]: https://github.com/lejianwen/rustdesk-server
|
||||||
11
README_EN.md
11
README_EN.md
@@ -12,6 +12,13 @@ desktop software that provides self-hosted solutions.
|
|||||||
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## Better used with [lejianwen/rustdesk-server].
|
||||||
|
> [lejianwen/rustdesk-server] is a fork of the official RustDesk Server repository.
|
||||||
|
> 1. Solves the API connection timeout issue.
|
||||||
|
> 2. Can enforce login before initiating a connection.
|
||||||
|
> 3. Supports client websocket.
|
||||||
|
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- PC API
|
- PC API
|
||||||
@@ -181,6 +188,7 @@ The table below does not list all configurations. Please refer to the configurat
|
|||||||
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
|
||||||
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
|
||||||
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
|
||||||
|
| RUSTDESK_API_MYSQL_TLS | Whether to enable TLS, optional values: `true`, `false`, `skip-verify`, `custom` | `false` |
|
||||||
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
|
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
|
||||||
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
|
||||||
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
|
||||||
@@ -325,3 +333,6 @@ Thanks to everyone who contributed!
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Thanks for your support! If you find this project useful, please give it a ⭐️. Thank you!
|
## Thanks for your support! If you find this project useful, please give it a ⭐️. Thank you!
|
||||||
|
|
||||||
|
|
||||||
|
[lejianwen/rustdesk-server]: https://github.com/lejianwen/rustdesk-server
|
||||||
@@ -145,11 +145,12 @@ func InitGlobal() {
|
|||||||
//gorm
|
//gorm
|
||||||
if global.Config.Gorm.Type == config.TypeMysql {
|
if global.Config.Gorm.Type == config.TypeMysql {
|
||||||
|
|
||||||
dsn := fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
dsn := fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s",
|
||||||
global.Config.Mysql.Username,
|
global.Config.Mysql.Username,
|
||||||
global.Config.Mysql.Password,
|
global.Config.Mysql.Password,
|
||||||
global.Config.Mysql.Addr,
|
global.Config.Mysql.Addr,
|
||||||
global.Config.Mysql.Dbname,
|
global.Config.Mysql.Dbname,
|
||||||
|
global.Config.Mysql.Tls,
|
||||||
)
|
)
|
||||||
|
|
||||||
global.DB = orm.NewMysql(&orm.MysqlConfig{
|
global.DB = orm.NewMysql(&orm.MysqlConfig{
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ mysql:
|
|||||||
password: ""
|
password: ""
|
||||||
addr: ""
|
addr: ""
|
||||||
dbname: ""
|
dbname: ""
|
||||||
|
tls: "false" # true / false / skip-verify / custom
|
||||||
|
|
||||||
postgresql:
|
postgresql:
|
||||||
host: "127.0.0.1"
|
host: "127.0.0.1"
|
||||||
@@ -80,4 +81,4 @@ ldap:
|
|||||||
last-name: "sn"
|
last-name: "sn"
|
||||||
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
|
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
|
||||||
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
|
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
|
||||||
|
allow-group: "cn=users,dc=example,dc=com" # The group name of the users group, if the user is in this group, the user will be an login.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type Mysql struct {
|
|||||||
Username string `mapstructure:"username"`
|
Username string `mapstructure:"username"`
|
||||||
Password string `mapstructure:"password"`
|
Password string `mapstructure:"password"`
|
||||||
Dbname string `mapstructure:"dbname"`
|
Dbname string `mapstructure:"dbname"`
|
||||||
|
Tls string `mapstructure:"tls"` // true / false / skip-verify / custom
|
||||||
}
|
}
|
||||||
|
|
||||||
type Postgresql struct {
|
type Postgresql struct {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type LdapUser struct {
|
|||||||
LastName string `mapstructure:"last-name"`
|
LastName string `mapstructure:"last-name"`
|
||||||
Sync bool `mapstructure:"sync"` // Will sync the user's information to the internal database
|
Sync bool `mapstructure:"sync"` // Will sync the user's information to the internal database
|
||||||
AdminGroup string `mapstructure:"admin-group"` // Which group is the admin group
|
AdminGroup string `mapstructure:"admin-group"` // Which group is the admin group
|
||||||
|
AllowGroup string `mapstructure:"allow-group"` // Which group is allowed to login
|
||||||
}
|
}
|
||||||
|
|
||||||
// type LdapGroup struct {
|
// type LdapGroup struct {
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -25,6 +25,7 @@ require (
|
|||||||
github.com/swaggo/files v1.0.1
|
github.com/swaggo/files v1.0.1
|
||||||
github.com/swaggo/gin-swagger v1.6.0
|
github.com/swaggo/gin-swagger v1.6.0
|
||||||
github.com/swaggo/swag v1.16.3
|
github.com/swaggo/swag v1.16.3
|
||||||
|
golang.org/x/crypto v0.33.0
|
||||||
golang.org/x/oauth2 v0.23.0
|
golang.org/x/oauth2 v0.23.0
|
||||||
golang.org/x/text v0.22.0
|
golang.org/x/text v0.22.0
|
||||||
gorm.io/driver/mysql v1.5.7
|
gorm.io/driver/mysql v1.5.7
|
||||||
@@ -84,7 +85,6 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/crypto v0.33.0 // indirect
|
|
||||||
golang.org/x/image v0.13.0 // indirect
|
golang.org/x/image v0.13.0 // indirect
|
||||||
golang.org/x/net v0.34.0 // indirect
|
golang.org/x/net v0.34.0 // indirect
|
||||||
golang.org/x/sync v0.11.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ func (o *Oauth) OidcAuthQueryPre(c *gin.Context) (*model.User, *model.UserToken)
|
|||||||
|
|
||||||
// 如果 UserId 为 0,说明还在授权中
|
// 如果 UserId 为 0,说明还在授权中
|
||||||
if v.UserId == 0 {
|
if v.UserId == 0 {
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind"})
|
//fix: 1.4.2 webclient oidc
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind", "error": "No authed oidc is found"})
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
resources/web2/index.html
vendored
23
resources/web2/index.html
vendored
@@ -32,7 +32,7 @@
|
|||||||
<title>RustDesk</title>
|
<title>RustDesk</title>
|
||||||
<script src="/webclient-config/index.js"></script>
|
<script src="/webclient-config/index.js"></script>
|
||||||
<link rel="manifest" href="manifest.json"/>
|
<link rel="manifest" href="manifest.json"/>
|
||||||
<script type="module" crossorigin src="js/dist/index.js?v=ddbe54f1"></script>
|
<script type="module" crossorigin src="js/dist/index.js?v=bd4ac5e9"></script>
|
||||||
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e"/>
|
<link rel="modulepreload" href="js/dist/vendor.js?v=0b990c6e"/>
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
@@ -319,26 +319,5 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="libs/stream/ponyfill.min.js"></script>
|
<script src="libs/stream/ponyfill.min.js"></script>
|
||||||
<script src="libs/stream/StreamSaver.min.js"></script>
|
<script src="libs/stream/StreamSaver.min.js"></script>
|
||||||
<script src="libs/firebase-app.js?8.10.1"></script>
|
|
||||||
<script src="libs/firebase-analytics.js?8.10.1"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Your web app's Firebase configuration
|
|
||||||
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
|
||||||
const firebaseConfig = {
|
|
||||||
apiKey: "AIzaSyCgehIZk1aFP0E7wZtYRRqrfvNiNAF39-A",
|
|
||||||
authDomain: "rustdesk.firebaseapp.com",
|
|
||||||
databaseURL: "https://rustdesk.firebaseio.com",
|
|
||||||
projectId: "rustdesk",
|
|
||||||
storageBucket: "rustdesk.appspot.com",
|
|
||||||
messagingSenderId: "768133699366",
|
|
||||||
appId: "1:768133699366:web:d50faf0792cb208d7993e7",
|
|
||||||
measurementId: "G-9PEH85N6ZQ",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize Firebase
|
|
||||||
firebase.initializeApp(firebaseConfig);
|
|
||||||
firebase.analytics();
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
5825
resources/web2/js/dist/index.js
vendored
5825
resources/web2/js/dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
5862
resources/web2/js/dist/lang.js
vendored
5862
resources/web2/js/dist/lang.js
vendored
File diff suppressed because it is too large
Load Diff
188657
resources/web2/main.dart.js
vendored
188657
resources/web2/main.dart.js
vendored
File diff suppressed because one or more lines are too long
@@ -137,6 +137,17 @@ func (ls *LdapService) Authenticate(username, password string) (*model.User, err
|
|||||||
return nil, ErrLdapUserDisabled
|
return nil, ErrLdapUserDisabled
|
||||||
}
|
}
|
||||||
cfg := &Config.Ldap
|
cfg := &Config.Ldap
|
||||||
|
|
||||||
|
// Skip allow-group check for admins
|
||||||
|
isAdmin := ls.isUserAdmin(cfg, ldapUser)
|
||||||
|
|
||||||
|
// non-admins only check if allow-group is configured
|
||||||
|
if !isAdmin && cfg.User.AllowGroup != "" {
|
||||||
|
if !ls.isUserInGroup(cfg, ldapUser, cfg.User.AllowGroup) {
|
||||||
|
return nil, errors.New("user not in allowed group")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = ls.verifyCredentials(cfg, ldapUser.Dn, password)
|
err = ls.verifyCredentials(cfg, ldapUser.Dn, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -148,6 +159,46 @@ func (ls *LdapService) Authenticate(username, password string) (*model.User, err
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isUserInGroup checks if the user is a member of the specified group. by_sw
|
||||||
|
func (ls *LdapService) isUserInGroup(cfg *config.Ldap, ldapUser *LdapUser, groupDN string) bool {
|
||||||
|
// Check "memberOf" directly
|
||||||
|
if len(ldapUser.MemberOf) > 0 {
|
||||||
|
for _, group := range ldapUser.MemberOf {
|
||||||
|
if strings.EqualFold(group, groupDN) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For "member" attribute, perform a reverse search on the group
|
||||||
|
member := "member"
|
||||||
|
userDN := ldap.EscapeFilter(ldapUser.Dn)
|
||||||
|
groupDN = ldap.EscapeFilter(groupDN)
|
||||||
|
groupFilter := fmt.Sprintf("(%s=%s)", member, userDN)
|
||||||
|
|
||||||
|
// Create the LDAP search request
|
||||||
|
groupSearchRequest := ldap.NewSearchRequest(
|
||||||
|
groupDN,
|
||||||
|
ldap.ScopeWholeSubtree,
|
||||||
|
ldap.NeverDerefAliases,
|
||||||
|
0, // Unlimited search results
|
||||||
|
0, // No time limit
|
||||||
|
false, // Return both attributes and DN
|
||||||
|
groupFilter,
|
||||||
|
[]string{"dn"},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Perform the group search
|
||||||
|
groupResult, err := ls.searchResult(cfg, groupSearchRequest)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any results are returned, the user is part of the group
|
||||||
|
return len(groupResult.Entries) > 0
|
||||||
|
}
|
||||||
|
|
||||||
// mapToLocalUser checks whether the user exists locally; if not, creates one.
|
// mapToLocalUser checks whether the user exists locally; if not, creates one.
|
||||||
// If the user exists and Ldap.Sync is enabled, it updates local info.
|
// If the user exists and Ldap.Sync is enabled, it updates local info.
|
||||||
func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.User, error) {
|
func (ls *LdapService) mapToLocalUser(cfg *config.Ldap, lu *LdapUser) (*model.User, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user