Compare commits

...

9 Commits

Author SHA1 Message Date
lejianwen
ade6e6355a feat(peer): add alias field and support filtering by alias 2025-08-31 13:39:22 +08:00
lejianwen
9b769b99dc fix!: Update peer to use ID instead of UUID 2025-08-31 12:46:54 +08:00
lejianwen
c14c4d478b fix: The callback URL is based on the configured API SERVER because the project might be behind an Nginx reverse proxy. If the Origin/Host is forgotten to configure the reverse proxy, it will be incorrect 2025-08-10 15:48:11 +08:00
lejianwen
9d08c61390 fix: Normal user can not change the name of their own address book (#341) 2025-08-10 11:17:51 +08:00
Tao Chen
6f092472b1 feat: Optimize login workflow (#345)
* add "disable_pwd" and "auto_oidc" at /admin/login-options

* fix: build RedirectURL by host and scheme, not Origin
2025-07-31 10:46:11 +08:00
caicob
4876746f7a docs: README_EN.md (#340) 2025-07-31 10:42:56 +08:00
startgo
05d2d1642a feat: Update zh_TW.toml (#322)
Corrected translation to match Taiwanese Traditional Chinese usage
2025-07-19 21:08:40 +08:00
lejianwen
59fdd6424b feat(captcha): The captcha generates uppercase letter images, but it can only recognize them as lowercase (#319) 2025-07-14 20:36:33 +08:00
lejianwen
0feee5115f fix: Oauth callback url is fixed to host+/api/oidc/callback (#314) 2025-07-11 09:55:48 +08:00
23 changed files with 181 additions and 169 deletions

View File

@@ -94,8 +94,8 @@
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email``preferred_username` - 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email``preferred_username`
- `github oauth app``Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App` - `github oauth app``Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers) 中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback` - `Authorization callback URL`填写`http://<your server[:port]>/api/oidc/callback`
,比如`http://127.0.0.1:21114/api/oauth/callback` ,比如`http://127.0.0.1:21114/api/oidc/callback`
7. 登录日志 7. 登录日志
8. 链接日志 8. 链接日志
9. 文件传输日志 9. 文件传输日志

View File

@@ -94,8 +94,8 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
- For `OIDC`, you must set the `Issuer`. And `Scopes` is optional which default is `openid,email,profile`, please make sure this `Oauth App` can access `sub`, `email` and `preferred_username` - For `OIDC`, you must set the `Issuer`. And `Scopes` is optional which default is `openid,email,profile`, please make sure this `Oauth App` can access `sub`, `email` and `preferred_username`
- Create a `GitHub OAuth App` - Create a `GitHub OAuth App`
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers). at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`, - Set the `Authorization callback URL` to `http://<your server[:port]>/api/oidc/callback`,
e.g., `http://127.0.0.1:21114/api/oauth/callback`. e.g., `http://127.0.0.1:21114/api/oidc/callback`.
7. Login logs 7. Login logs
8. Connection logs 8. Connection logs
@@ -164,8 +164,7 @@ The table below does not list all configurations. Please refer to the configurat
| RUSTDESK_API_APP_DISABLE_PWD_LOGIN | disable password login | `false` | | RUSTDESK_API_APP_DISABLE_PWD_LOGIN | disable password login | `false` |
| RUSTDESK_API_APP_REGISTER_STATUS | register user default status ; 1 enabled , 2 disabled ; default 1 | `1` | | RUSTDESK_API_APP_REGISTER_STATUS | register user default status ; 1 enabled , 2 disabled ; default 1 | `1` |
| RUSTDESK_API_APP_CAPTCHA_THRESHOLD | captcha threshold; -1 disabled, 0 always enable, >0 threshold ;default `3` | `3` | | RUSTDESK_API_APP_CAPTCHA_THRESHOLD | captcha threshold; -1 disabled, 0 always enable, >0 threshold ;default `3` | `3` |
| RUSTDESK_API_APP_BAN_THRESHOLD | ban ip threshold; 0 disabled, >0 threshold ; default `0` | RUSTDESK_API_APP_BAN_THRESHOLD | ban ip threshold; 0 disabled, >0 threshold ; default `0` | `0` |
| `0` |
| ----- ADMIN Configuration----- | ---------- | ---------- | | ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` | | RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | | | RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
@@ -325,4 +324,4 @@ Thanks to everyone who contributed!
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" /> <img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
</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!

View File

@@ -23,7 +23,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const DatabaseVersion = 264 const DatabaseVersion = 265
// @title 管理系统API // @title 管理系统API
// @version 1.0 // @version 1.0

View File

@@ -954,35 +954,6 @@ const docTemplateapi = `{
} }
} }
}, },
"/oauth/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/oidc/auth": { "/oidc/auth": {
"post": { "post": {
"description": "OidcAuth", "description": "OidcAuth",
@@ -1041,6 +1012,35 @@ const docTemplateapi = `{
} }
} }
}, },
"/oidc/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/peers": { "/peers": {
"get": { "get": {
"security": [ "security": [

View File

@@ -947,35 +947,6 @@
} }
} }
}, },
"/oauth/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/oidc/auth": { "/oidc/auth": {
"post": { "post": {
"description": "OidcAuth", "description": "OidcAuth",
@@ -1034,6 +1005,35 @@
} }
} }
}, },
"/oidc/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/peers": { "/peers": {
"get": { "get": {
"security": [ "security": [

View File

@@ -792,25 +792,6 @@ paths:
summary: 登出 summary: 登出
tags: tags:
- 登录 - 登录
/oauth/callback:
get:
consumes:
- application/json
description: OauthCallback
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.LoginRes'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: OauthCallback
tags:
- Oauth
/oidc/auth: /oidc/auth:
post: post:
consumes: consumes:
@@ -849,6 +830,25 @@ paths:
summary: OidcAuthQuery summary: OidcAuthQuery
tags: tags:
- Oauth - Oauth
/oidc/callback:
get:
consumes:
- application/json
description: OauthCallback
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.LoginRes'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: OauthCallback
tags:
- Oauth
/peers: /peers:
get: get:
consumes: consumes:

View File

@@ -169,6 +169,8 @@ func (ct *Login) LoginOptions(c *gin.Context) {
"ops": ops, "ops": ops,
"register": global.Config.App.Register, "register": global.Config.App.Register,
"need_captcha": needCaptcha, "need_captcha": needCaptcha,
"disable_pwd": global.Config.App.DisablePwdLogin,
"auto_oidc": global.Config.App.DisablePwdLogin && len(ops) == 1,
}) })
} }
@@ -189,7 +191,7 @@ func (ct *Login) OidcAuth(c *gin.Context) {
return return
} }
err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(c, f.Op) err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
if err != nil { if err != nil {
response.Error(c, response.TranslateMsg(c, err.Error())) response.Error(c, response.TranslateMsg(c, err.Error()))
return return

View File

@@ -98,10 +98,10 @@ func (abc *AddressBookCollection) Update(c *gin.Context) {
return return
} }
u := service.AllService.UserService.CurUser(c) u := service.AllService.UserService.CurUser(c)
if f.UserId != u.Id { //if f.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess")) // response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return // return
} //}
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id) ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
if ex.Id == 0 { if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))

View File

@@ -44,7 +44,7 @@ func (o *Oauth) ToBind(c *gin.Context) {
return return
} }
err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(c, f.Op) err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
if err != nil { if err != nil {
response.Error(c, response.TranslateMsg(c, err.Error())) response.Error(c, response.TranslateMsg(c, err.Error()))
return return

View File

@@ -114,6 +114,9 @@ func (ct *Peer) List(c *gin.Context) {
if query.Ip != "" { if query.Ip != "" {
tx.Where("last_online_ip like ?", "%"+query.Ip+"%") tx.Where("last_online_ip like ?", "%"+query.Ip+"%")
} }
if query.Alias != "" {
tx.Where("alias like ?", "%"+query.Alias+"%")
}
}) })
response.Success(c, res) response.Success(c, res)
} }

View File

@@ -49,7 +49,7 @@ func (i *Index) Heartbeat(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{}) c.JSON(http.StatusOK, gin.H{})
return return
} }
peer := service.AllService.PeerService.FindByUuid(info.Uuid) peer := service.AllService.PeerService.FindById(info.Id)
if peer == nil || peer.RowId == 0 { if peer == nil || peer.RowId == 0 {
c.JSON(http.StatusOK, gin.H{}) c.JSON(http.StatusOK, gin.H{})
return return

View File

@@ -36,7 +36,7 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
oauthService := service.AllService.OauthService oauthService := service.AllService.OauthService
err, state, verifier, nonce, url := oauthService.BeginAuth(c, f.Op) err, state, verifier, nonce, url := oauthService.BeginAuth(f.Op)
if err != nil { if err != nil {
response.Error(c, response.TranslateMsg(c, err.Error())) response.Error(c, response.TranslateMsg(c, err.Error()))
return return
@@ -143,7 +143,7 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
// @Produce json // @Produce json
// @Success 200 {object} apiResp.LoginRes // @Success 200 {object} apiResp.LoginRes
// @Failure 500 {object} response.ErrorResponse // @Failure 500 {object} response.ErrorResponse
// @Router /oauth/callback [get] // @Router /oidc/callback [get]
func (o *Oauth) OauthCallback(c *gin.Context) { func (o *Oauth) OauthCallback(c *gin.Context) {
state := c.Query("state") state := c.Query("state")
if state == "" { if state == "" {
@@ -170,7 +170,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
var user *model.User var user *model.User
// 获取用户信息 // 获取用户信息
code := c.Query("code") code := c.Query("code")
err, oauthUser := oauthService.Callback(c, code, verifier, op, nonce) err, oauthUser := oauthService.Callback(code, verifier, op, nonce)
if err != nil { if err != nil {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{ c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "OauthFailed", "message": "OauthFailed",

View File

@@ -31,10 +31,10 @@ func (p *Peer) SysInfo(c *gin.Context) {
return return
} }
fpe := f.ToPeer() fpe := f.ToPeer()
pe := service.AllService.PeerService.FindByUuid(f.Uuid) pe := service.AllService.PeerService.FindById(f.Id)
if pe.RowId == 0 { if pe.RowId == 0 {
pe = f.ToPeer() pe = f.ToPeer()
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid) pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid, pe.Id)
err = service.AllService.PeerService.Create(pe) err = service.AllService.PeerService.Create(pe)
if err != nil { if err != nil {
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error()) response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
@@ -42,7 +42,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
} }
} else { } else {
if pe.UserId == 0 { if pe.UserId == 0 {
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid) pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid, pe.Id)
} }
fpe.RowId = pe.RowId fpe.RowId = pe.RowId
fpe.UserId = pe.UserId fpe.UserId = pe.UserId

View File

@@ -13,6 +13,7 @@ type PeerForm struct {
Uuid string `json:"uuid"` Uuid string `json:"uuid"`
Version string `json:"version"` Version string `json:"version"`
GroupId uint `json:"group_id"` GroupId uint `json:"group_id"`
Alias string `json:"alias"`
} }
type PeerBatchDeleteForm struct { type PeerBatchDeleteForm struct {
@@ -32,6 +33,7 @@ func (f *PeerForm) ToPeer() *model.Peer {
Uuid: f.Uuid, Uuid: f.Uuid,
Version: f.Version, Version: f.Version,
GroupId: f.GroupId, GroupId: f.GroupId,
Alias: f.Alias,
} }
} }
@@ -43,6 +45,7 @@ type PeerQuery struct {
Uuids string `json:"uuids" form:"uuids"` Uuids string `json:"uuids" form:"uuids"`
Ip string `json:"ip" form:"ip"` Ip string `json:"ip" form:"ip"`
Username string `json:"username" form:"username"` Username string `json:"username" form:"username"`
Alias string `json:"alias" form:"alias"`
} }
type SimpleDataQuery struct { type SimpleDataQuery struct {

View File

@@ -49,6 +49,10 @@ func ApiInit(g *gin.Engine) {
frg.GET("/oauth/callback", o.OauthCallback) frg.GET("/oauth/callback", o.OauthCallback)
frg.GET("/oauth/login", o.OauthCallback) frg.GET("/oauth/login", o.OauthCallback)
frg.GET("/oauth/msg", o.Message) frg.GET("/oauth/msg", o.Message)
frg.GET("/oidc/callback", o.OauthCallback)
frg.GET("/oidc/login", o.OauthCallback)
frg.GET("/oidc/msg", o.Message)
} }
{ {
pe := &api.Peer{} pe := &api.Peer{}

View File

@@ -41,6 +41,7 @@ type Oauth struct {
OauthType string `json:"oauth_type"` OauthType string `json:"oauth_type"`
ClientId string `json:"client_id"` ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"` ClientSecret string `json:"client_secret"`
//RedirectUrl string `json:"redirect_url"`
AutoRegister *bool `json:"auto_register"` AutoRegister *bool `json:"auto_register"`
Scopes string `json:"scopes"` Scopes string `json:"scopes"`
Issuer string `json:"issuer"` Issuer string `json:"issuer"`

View File

@@ -15,6 +15,7 @@ type Peer struct {
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"` LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
LastOnlineIp string `json:"last_online_ip" gorm:"default:'';not null;"` LastOnlineIp string `json:"last_online_ip" gorm:"default:'';not null;"`
GroupId uint `json:"group_id" gorm:"default:0;not null;index"` GroupId uint `json:"group_id" gorm:"default:0;not null;index"`
Alias string `json:"alias" gorm:"default:'';not null;index"`
TimeModel TimeModel
} }

View File

@@ -5,8 +5,8 @@ other = "測試2 {{.P0}}"
[ParamsError] [ParamsError]
description = "Params validation failed." description = "Params validation failed."
one = "引數錯誤。" one = "參數驗證失敗。"
other = "引數錯誤。" other = "參數驗證失敗。"
[OperationFailed] [OperationFailed]
description = "OperationFailed." description = "OperationFailed."
@@ -20,18 +20,18 @@ other = "操作成功。"
[ItemExists] [ItemExists]
description = "Item already exists." description = "Item already exists."
one = "資料已存在。" one = "項目已存在。"
other = "資料已存在。" other = "項目已存在。"
[ItemNotFound] [ItemNotFound]
description = "Item not found." description = "Item not found."
one = "資料不存在。" one = "找不到項目。"
other = "資料不存在。" other = "找不到項目。"
[NoAccess] [NoAccess]
description = "No access." description = "No access."
one = "無許可權。" one = "無權限存取。"
other = "無許可權。" other = "無權限存取。"
[NeedLogin] [NeedLogin]
description = "Need login." description = "Need login."
@@ -50,24 +50,23 @@ other = "系統錯誤。"
[ConfigNotFound] [ConfigNotFound]
description = "Config not found." description = "Config not found."
one = "配置不存在。" one = "找不到設定。"
other = "配置不存在。" other = "找不到設定。"
#授權過期
[OauthExpired] [OauthExpired]
description = "Oauth expired." description = "Oauth expired."
one = "授權過期,請重新授權。" one = "OAuth 已過期,請重。"
other = "授權過期,請重新授權。" other = "OAuth 已過期,請重。"
[OauthFailed] [OauthFailed]
description = "Oauth failed." description = "Oauth failed."
one = "授權失敗。" one = "OAuth 失敗。"
other = "授權失敗。" other = "OAuth 失敗。"
[OauthHasBindOtherUser] [OauthHasBindOtherUser]
description = "Oauth has bind other user." description = "Oauth has bind other user."
one = "授權已繫結其他使用者。" one = "OAuth 已綁定其他使用者。"
other = "授權已繫結其他使用者。" other = "OAuth 已綁定其他使用者。"
[ParamIsEmpty] [ParamIsEmpty]
description = "Param is empty." description = "Param is empty."
@@ -76,56 +75,64 @@ other = "{{.P0}} 為空。"
[BindFail] [BindFail]
description = "Bind fail." description = "Bind fail."
one = "繫結失敗。" one = "綁定失敗。"
other = "繫結失敗。" other = "綁定失敗。"
[BindSuccess] [BindSuccess]
description = "Bind success." description = "Bind success."
one = "繫結成功。" one = "綁定成功。"
other = "繫結成功。" other = "綁定成功。"
[OauthHasBeenSuccess] [OauthHasBeenSuccess]
description = "Oauth has been success." description = "Oauth has been success."
one = "授權已成功。" one = "OAuth 已成功。"
other = "授權已成功。" other = "OAuth 已成功。"
[OauthSuccess] [OauthSuccess]
description = "Oauth success." description = "Oauth success."
one = "授權成功。" one = "OAuth 成功。"
other = "授權成功。" other = "OAuth 成功。"
[OauthRegisterSuccess] [OauthRegisterSuccess]
description = "Oauth register success." description = "Oauth register success."
one = "授權註冊成功。" one = "OAuth 註冊成功。"
other = "授權註冊成功。" other = "OAuth 註冊成功。"
[OauthRegisterFailed] [OauthRegisterFailed]
description = "Oauth register failed." description = "Oauth register failed."
one = "授權註冊失敗。" one = "OAuth 註冊失敗。"
other = "授權註冊失敗。" other = "OAuth 註冊失敗。"
[GetOauthTokenError] [GetOauthTokenError]
description = "Get oauth token error." description = "Get oauth token error."
one = "獲取授權token失敗。" one = "取得 OAuth 權杖錯誤。"
other = "獲取授權token失敗。" other = "取得 OAuth 權杖錯誤。"
[GetOauthUserInfoError] [GetOauthUserInfoError]
description = "Get oauth user info error." description = "Get oauth user info error."
one = "獲取授權使用者資訊失敗。" one = "取得 OAuth 使用者資訊錯誤。"
other = "獲取授權使用者資訊失敗。" other = "取得 OAuth 使用者資訊錯誤。"
[DecodeOauthUserInfoError] [DecodeOauthUserInfoError]
description = "Decode oauth user info error." description = "Decode oauth user info error."
one = "解析授權使用者資訊失敗。" one = "解析 OAuth 使用者資訊錯誤。"
other = "解析授權使用者資訊失敗。" other = "解析 OAuth 使用者資訊錯誤。"
[OldPasswordError] [OldPasswordError]
description = "Old password error." description = "Old password error."
one = "舊密碼錯誤。" one = "舊密碼錯誤。"
other = "舊密碼錯誤。" other = "舊密碼錯誤。"
[DefaultGroup] [DefaultGroup]
description = "Default group." description = "Default group."
one = "預設組" one = "預設組"
other = "預設組" other = "預設組"
[ShareGroup] [ShareGroup]
description = "Share group." description = "Share group."
one = "共享組" one = "共享組"
other = "共享組" other = "共享組"
[RegisterClosed] [RegisterClosed]
description = "Register closed." description = "Register closed."
one = "註冊已關閉。" one = "註冊已關閉。"
@@ -143,20 +150,20 @@ other = "驗證碼錯誤。"
[PwdLoginDisabled] [PwdLoginDisabled]
description = "Password login disabled." description = "Password login disabled."
one = "密碼登錄已禁用。" one = "密碼登入已停用。"
other = "密碼登錄已禁用。" other = "密碼登入已停用。"
[CannotShareToSelf] [CannotShareToSelf]
description = "Cannot share to self." description = "Cannot share to self."
one = "無法享給自己。" one = "無法享給自己。"
other = "無法享給自己。" other = "無法享給自己。"
[Banned] [Banned]
description = "Banned." description = "Banned."
one = "禁止使用。" one = "已被禁用。"
other = "禁止使用。" other = "已被禁用。"
[RegisterSuccessWaitAdminConfirm] [RegisterSuccessWaitAdminConfirm]
description = "Register success wait admin confirm." description = "Register success, wait admin confirm."
one = "註冊成功,等待管理員確認。" one = "註冊成功,等待管理員確認。"
other = "註冊成功,等待管理員確認。" other = "註冊成功,等待管理員確認。"

View File

@@ -62,7 +62,7 @@
var title = 'OauthFailed' var title = 'OauthFailed'
var msg = '{{.message}}' var msg = '{{.message}}'
var btn = 'Close' var btn = 'Close'
document.writeln('<script src="/api/oauth/msg?lang=' + lang + '&msg=' + msg + '&title=OauthFailed"><\/script>'); document.writeln('<script src="/api/oidc/msg?lang=' + lang + '&msg=' + msg + '&title=OauthFailed"><\/script>');
</script> </script>
</head> </head>
<body> <body>

View File

@@ -61,7 +61,7 @@
var title = 'OauthSuccess' var title = 'OauthSuccess'
var msg = '{{.message}}' var msg = '{{.message}}'
var btn = 'Close' var btn = 'Close'
document.writeln('<script src="/api/oauth/msg?lang=' + lang + '&msg=' + msg + '&title=OauthSuccess"><\/script>'); document.writeln('<script src="/api/oidc/msg?lang=' + lang + '&msg=' + msg + '&title=OauthSuccess"><\/script>');
</script> </script>
</head> </head>
<body> <body>

View File

@@ -6,7 +6,6 @@ import (
"errors" "errors"
"github.com/coreos/go-oidc/v3/oidc" "github.com/coreos/go-oidc/v3/oidc"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/model" "github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/utils" "github.com/lejianwen/rustdesk-api/v2/utils"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@@ -96,20 +95,16 @@ func (os *OauthService) DeleteOauthCache(key string) {
OauthCache.Delete(key) OauthCache.Delete(key)
} }
func (os *OauthService) BeginAuth(c *gin.Context, op string) (error error, state, verifier, nonce, url string) { func (os *OauthService) BeginAuth(op string) (error error, state, verifier, nonce, url string) {
state = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10) state = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10)
verifier = "" verifier = ""
nonce = "" nonce = ""
if op == model.OauthTypeWebauth { if op == model.OauthTypeWebauth {
host := c.GetHeader("Origin") url = Config.Rustdesk.ApiServer + "/_admin/#/oauth/" + state
if host == "" {
host = Config.Rustdesk.ApiServer
}
url = host + "/_admin/#/oauth/" + state
//url = "http://localhost:8888/_admin/#/oauth/" + code //url = "http://localhost:8888/_admin/#/oauth/" + code
return nil, state, verifier, nonce, url return nil, state, verifier, nonce, url
} }
err, oauthInfo, oauthConfig, _ := os.GetOauthConfig(c, op) err, oauthInfo, oauthConfig, _ := os.GetOauthConfig(op)
if err == nil { if err == nil {
extras := make([]oauth2.AuthCodeOption, 0, 3) extras := make([]oauth2.AuthCodeOption, 0, 3)
@@ -174,20 +169,16 @@ func (os *OauthService) LinuxdoProvider() *oidc.Provider {
} }
// GetOauthConfig retrieves the OAuth2 configuration based on the provider name // GetOauthConfig retrieves the OAuth2 configuration based on the provider name
func (os *OauthService) GetOauthConfig(c *gin.Context, op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config, provider *oidc.Provider) { func (os *OauthService) GetOauthConfig(op string) (err error, oauthInfo *model.Oauth, oauthConfig *oauth2.Config, provider *oidc.Provider) {
//err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op) //err, oauthInfo, oauthConfig = os.getOauthConfigGeneral(op)
oauthInfo = os.InfoByOp(op) oauthInfo = os.InfoByOp(op)
if oauthInfo.Id == 0 || oauthInfo.ClientId == "" || oauthInfo.ClientSecret == "" { if oauthInfo.Id == 0 || oauthInfo.ClientId == "" || oauthInfo.ClientSecret == "" {
return errors.New("ConfigNotFound"), nil, nil, nil return errors.New("ConfigNotFound"), nil, nil, nil
} }
host := c.GetHeader("Origin")
if host == "" {
host = Config.Rustdesk.ApiServer
}
oauthConfig = &oauth2.Config{ oauthConfig = &oauth2.Config{
ClientID: oauthInfo.ClientId, ClientID: oauthInfo.ClientId,
ClientSecret: oauthInfo.ClientSecret, ClientSecret: oauthInfo.ClientSecret,
RedirectURL: host + "/api/oidc/callback", RedirectURL: Config.Rustdesk.ApiServer + "/api/oidc/callback",
} }
// Maybe should validate the oauthConfig here // Maybe should validate the oauthConfig here
@@ -342,8 +333,8 @@ func (os *OauthService) oidcCallback(oauthConfig *oauth2.Config, provider *oidc.
} }
// Callback: Get user information by code and op(Oauth provider) // Callback: Get user information by code and op(Oauth provider)
func (os *OauthService) Callback(c *gin.Context, code, verifier, op, nonce string) (err error, oauthUser *model.OauthUser) { func (os *OauthService) Callback(code, verifier, op, nonce string) (err error, oauthUser *model.OauthUser) {
err, oauthInfo, oauthConfig, provider := os.GetOauthConfig(c, op) err, oauthInfo, oauthConfig, provider := os.GetOauthConfig(op)
// oauthType is already validated in GetOauthConfig // oauthType is already validated in GetOauthConfig
if err != nil { if err != nil {
return err, nil return err, nil

View File

@@ -395,10 +395,10 @@ func (us *UserService) UserThirdInfo(userId uint, op string) *model.UserThird {
return ut return ut
} }
// FindLatestUserIdFromLoginLogByUuid 根据uuid查找最后登录的用户id // FindLatestUserIdFromLoginLogByUuid 根据uuid和设备id查找最后登录的用户id
func (us *UserService) FindLatestUserIdFromLoginLogByUuid(uuid string) uint { func (us *UserService) FindLatestUserIdFromLoginLogByUuid(uuid string, deviceId string) uint {
llog := &model.LoginLog{} llog := &model.LoginLog{}
DB.Where("uuid = ?", uuid).Order("id desc").First(llog) DB.Where("uuid = ? and device_id = ?", uuid, deviceId).Order("id desc").First(llog)
return llog.UserId return llog.UserId
} }

View File

@@ -5,7 +5,8 @@ import (
"time" "time"
) )
var capdString = base64Captcha.NewDriverString(50, 150, 0, 5, 4, "123456789abcdefghijklmnopqrstuvwxyz", nil, nil, nil) var capdString = base64Captcha.NewDriverString(50, 150, 0, 5, 4, "123456789abcdefghijklmnopqrstuvwxyz", nil, nil,
[]string{"3Dumb.ttf", "ApothecaryFont.ttf", "Comismsh.ttf", "Flim-Flam.ttf", "RitaSmith.ttf", "wqy-microhei.ttc"})
var capdMath = base64Captcha.NewDriverMath(50, 150, 3, 10, nil, nil, nil) var capdMath = base64Captcha.NewDriverMath(50, 150, 3, 10, nil, nil, nil)