mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-18 08:26:37 +08:00
limit 'LINUX DO' trust level now available
Signed-off-by: wozulong <>
This commit is contained in:
parent
17c409de23
commit
f35e63e3f3
2
.github/workflows/docker-image-amd64.yml
vendored
2
.github/workflows/docker-image-amd64.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
|||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
linux-do/new-api
|
pengzhile/new-api
|
||||||
ghcr.io/${{ github.repository }}
|
ghcr.io/${{ github.repository }}
|
||||||
|
|
||||||
- name: Build and push Docker images
|
- name: Build and push Docker images
|
||||||
|
2
.github/workflows/docker-image-arm64.yml
vendored
2
.github/workflows/docker-image-arm64.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
|||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
linux-do/new-api
|
pengzhile/new-api
|
||||||
ghcr.io/${{ github.repository }}
|
ghcr.io/${{ github.repository }}
|
||||||
|
|
||||||
- name: Build and push Docker images
|
- name: Build and push Docker images
|
||||||
|
@ -85,6 +85,7 @@ var GitHubClientSecret = ""
|
|||||||
|
|
||||||
var LinuxDoClientId = ""
|
var LinuxDoClientId = ""
|
||||||
var LinuxDoClientSecret = ""
|
var LinuxDoClientSecret = ""
|
||||||
|
var LinuxDoMinLevel = 0
|
||||||
|
|
||||||
var WeChatServerAddress = ""
|
var WeChatServerAddress = ""
|
||||||
var WeChatServerToken = ""
|
var WeChatServerToken = ""
|
||||||
|
@ -80,6 +80,9 @@ func getLinuxDoUserInfoByCode(code string) (*LinuxDoUser, error) {
|
|||||||
if linuxdoUser.ID == 0 {
|
if linuxdoUser.ID == 0 {
|
||||||
return nil, errors.New("返回值非法,用户字段为空,请稍后重试!")
|
return nil, errors.New("返回值非法,用户字段为空,请稍后重试!")
|
||||||
}
|
}
|
||||||
|
if linuxdoUser.TrustLevel < common.LinuxDoMinLevel {
|
||||||
|
return nil, errors.New("用户 LINUX DO 信任等级不足!")
|
||||||
|
}
|
||||||
return &linuxdoUser, nil
|
return &linuxdoUser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +120,7 @@ func LinuxDoOAuth(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
user := model.User{
|
user := model.User{
|
||||||
LinuxDoId: strconv.Itoa(linuxdoUser.ID),
|
LinuxDoId: strconv.Itoa(linuxdoUser.ID),
|
||||||
|
LinuxDoLevel: linuxdoUser.TrustLevel,
|
||||||
}
|
}
|
||||||
if model.IsLinuxDoIdAlreadyTaken(user.LinuxDoId) {
|
if model.IsLinuxDoIdAlreadyTaken(user.LinuxDoId) {
|
||||||
err := user.FillUserByLinuxDoId()
|
err := user.FillUserByLinuxDoId()
|
||||||
@ -127,6 +131,16 @@ func LinuxDoOAuth(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.LinuxDoLevel = linuxdoUser.TrustLevel
|
||||||
|
err = user.Update(false)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if common.RegisterEnabled {
|
if common.RegisterEnabled {
|
||||||
affCode := c.Query("aff")
|
affCode := c.Query("aff")
|
||||||
@ -186,6 +200,7 @@ func LinuxDoBind(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
user := model.User{
|
user := model.User{
|
||||||
LinuxDoId: strconv.Itoa(linuxdoUser.ID),
|
LinuxDoId: strconv.Itoa(linuxdoUser.ID),
|
||||||
|
LinuxDoLevel: linuxdoUser.TrustLevel,
|
||||||
}
|
}
|
||||||
if model.IsLinuxDoIdAlreadyTaken(user.LinuxDoId) {
|
if model.IsLinuxDoIdAlreadyTaken(user.LinuxDoId) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
@ -207,6 +222,7 @@ func LinuxDoBind(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.LinuxDoId = strconv.Itoa(linuxdoUser.ID)
|
user.LinuxDoId = strconv.Itoa(linuxdoUser.ID)
|
||||||
|
user.LinuxDoLevel = linuxdoUser.TrustLevel
|
||||||
err = user.Update(false)
|
err = user.Update(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
@ -63,6 +63,7 @@ func GetStatus(c *gin.Context) {
|
|||||||
"default_collapse_sidebar": common.DefaultCollapseSidebar,
|
"default_collapse_sidebar": common.DefaultCollapseSidebar,
|
||||||
"enable_online_topup": common.PayAddress != "" && common.EpayId != "" && common.EpayKey != "",
|
"enable_online_topup": common.PayAddress != "" && common.EpayId != "" && common.EpayKey != "",
|
||||||
"mj_notify_enabled": constant.MjNotifyEnabled,
|
"mj_notify_enabled": constant.MjNotifyEnabled,
|
||||||
|
"version": common.Version,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -65,6 +65,7 @@ func setupLogin(user *model.User, c *gin.Context) {
|
|||||||
session.Set("username", user.Username)
|
session.Set("username", user.Username)
|
||||||
session.Set("role", user.Role)
|
session.Set("role", user.Role)
|
||||||
session.Set("status", user.Status)
|
session.Set("status", user.Status)
|
||||||
|
session.Set("linuxdo_enable", user.LinuxDoId == "" || user.LinuxDoLevel >= common.LinuxDoMinLevel)
|
||||||
err := session.Save()
|
err := session.Save()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
@ -15,6 +15,7 @@ func authHelper(c *gin.Context, minRole int) {
|
|||||||
role := session.Get("role")
|
role := session.Get("role")
|
||||||
id := session.Get("id")
|
id := session.Get("id")
|
||||||
status := session.Get("status")
|
status := session.Get("status")
|
||||||
|
linuxDoEnable := session.Get("linuxdo_enable")
|
||||||
if username == nil {
|
if username == nil {
|
||||||
// Check access token
|
// Check access token
|
||||||
accessToken := c.Request.Header.Get("Authorization")
|
accessToken := c.Request.Header.Get("Authorization")
|
||||||
@ -33,6 +34,7 @@ func authHelper(c *gin.Context, minRole int) {
|
|||||||
role = user.Role
|
role = user.Role
|
||||||
id = user.Id
|
id = user.Id
|
||||||
status = user.Status
|
status = user.Status
|
||||||
|
linuxDoEnable = user.LinuxDoId == "" || user.LinuxDoLevel >= common.LinuxDoMinLevel
|
||||||
} else {
|
} else {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
@ -50,6 +52,14 @@ func authHelper(c *gin.Context, minRole int) {
|
|||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if nil != linuxDoEnable && !linuxDoEnable.(bool) {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "用户 LINUX DO 信任等级不足",
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
if role.(int) < minRole {
|
if role.(int) < minRole {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
@ -112,6 +122,15 @@ func TokenAuth() func(c *gin.Context) {
|
|||||||
abortWithOpenAiMessage(c, http.StatusForbidden, "用户已被封禁")
|
abortWithOpenAiMessage(c, http.StatusForbidden, "用户已被封禁")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
linuxDoEnabled, err := model.CacheIsLinuxDoEnabled(token.UserId)
|
||||||
|
if err != nil {
|
||||||
|
abortWithOpenAiMessage(c, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !linuxDoEnabled {
|
||||||
|
abortWithOpenAiMessage(c, http.StatusForbidden, "用户 LINUX DO 信任等级不足")
|
||||||
|
return
|
||||||
|
}
|
||||||
c.Set("id", token.UserId)
|
c.Set("id", token.UserId)
|
||||||
c.Set("token_id", token.Id)
|
c.Set("token_id", token.Id)
|
||||||
c.Set("token_name", token.Name)
|
c.Set("token_name", token.Name)
|
||||||
|
@ -204,6 +204,30 @@ func CacheIsUserEnabled(userId int) (bool, error) {
|
|||||||
return userEnabled, err
|
return userEnabled, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CacheIsLinuxDoEnabled(userId int) (bool, error) {
|
||||||
|
if !common.RedisEnabled {
|
||||||
|
return IsLinuxDoEnabled(userId)
|
||||||
|
}
|
||||||
|
enabled, err := common.RedisGet(fmt.Sprintf("linuxdo_enabled:%d", userId))
|
||||||
|
if err == nil {
|
||||||
|
return enabled == "1", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
linuxDoEnabled, err := IsLinuxDoEnabled(userId)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
enabled = "0"
|
||||||
|
if linuxDoEnabled {
|
||||||
|
enabled = "1"
|
||||||
|
}
|
||||||
|
err = common.RedisSet(fmt.Sprintf("linuxdo_enabled:%d", userId), enabled, time.Duration(UserId2StatusCacheSeconds)*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
common.SysError("Redis set linuxdo enabled error: " + err.Error())
|
||||||
|
}
|
||||||
|
return linuxDoEnabled, err
|
||||||
|
}
|
||||||
|
|
||||||
var group2model2channels map[string]map[string][]*Channel
|
var group2model2channels map[string]map[string][]*Channel
|
||||||
var channelsIDM map[int]*Channel
|
var channelsIDM map[int]*Channel
|
||||||
var channelSyncLock sync.RWMutex
|
var channelSyncLock sync.RWMutex
|
||||||
|
@ -69,6 +69,7 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["GitHubClientSecret"] = ""
|
common.OptionMap["GitHubClientSecret"] = ""
|
||||||
common.OptionMap["LinuxDoClientId"] = ""
|
common.OptionMap["LinuxDoClientId"] = ""
|
||||||
common.OptionMap["LinuxDoClientSecret"] = ""
|
common.OptionMap["LinuxDoClientSecret"] = ""
|
||||||
|
common.OptionMap["LinuxDoMinLevel"] = strconv.Itoa(common.LinuxDoMinLevel)
|
||||||
common.OptionMap["TelegramBotToken"] = ""
|
common.OptionMap["TelegramBotToken"] = ""
|
||||||
common.OptionMap["TelegramBotName"] = ""
|
common.OptionMap["TelegramBotName"] = ""
|
||||||
common.OptionMap["WeChatServerAddress"] = ""
|
common.OptionMap["WeChatServerAddress"] = ""
|
||||||
@ -230,6 +231,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.LinuxDoClientId = value
|
common.LinuxDoClientId = value
|
||||||
case "LinuxDoClientSecret":
|
case "LinuxDoClientSecret":
|
||||||
common.LinuxDoClientSecret = value
|
common.LinuxDoClientSecret = value
|
||||||
|
case "LinuxDoMinLevel":
|
||||||
|
common.LinuxDoMinLevel, _ = strconv.Atoi(value)
|
||||||
case "Footer":
|
case "Footer":
|
||||||
common.Footer = value
|
common.Footer = value
|
||||||
case "SystemName":
|
case "SystemName":
|
||||||
|
@ -22,6 +22,7 @@ type User struct {
|
|||||||
Email string `json:"email" gorm:"index" validate:"max=50"`
|
Email string `json:"email" gorm:"index" validate:"max=50"`
|
||||||
GitHubId string `json:"github_id" gorm:"column:github_id;index"`
|
GitHubId string `json:"github_id" gorm:"column:github_id;index"`
|
||||||
LinuxDoId string `json:"linuxdo_id" gorm:"column:linuxdo_id;index"`
|
LinuxDoId string `json:"linuxdo_id" gorm:"column:linuxdo_id;index"`
|
||||||
|
LinuxDoLevel int `json:"linuxdo_level" gorm:"column:linuxdo_level;type:int;default:0"`
|
||||||
WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"`
|
WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"`
|
||||||
TelegramId string `json:"telegram_id" gorm:"column:telegram_id;index"`
|
TelegramId string `json:"telegram_id" gorm:"column:telegram_id;index"`
|
||||||
VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database!
|
VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database!
|
||||||
@ -369,6 +370,18 @@ func IsUserEnabled(userId int) (bool, error) {
|
|||||||
return user.Status == common.UserStatusEnabled, nil
|
return user.Status == common.UserStatusEnabled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsLinuxDoEnabled(userId int) (bool, error) {
|
||||||
|
if userId == 0 {
|
||||||
|
return false, errors.New("user id is empty")
|
||||||
|
}
|
||||||
|
var user User
|
||||||
|
err := DB.Where("id = ?", userId).Select("linuxdo_id, linuxdo_level").Find(&user).Error
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return user.LinuxDoId == "" || user.LinuxDoLevel >= common.LinuxDoMinLevel, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateAccessToken(token string) (user *User) {
|
func ValidateAccessToken(token string) (user *User) {
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return nil
|
return nil
|
||||||
|
@ -454,7 +454,7 @@ const PersonalSetting = () => {
|
|||||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
value={userState.user && userState.user.linuxdo_id !== '' ? userState.user.linuxdo_id : '未绑定'}
|
value={userState.user && userState.user.linuxdo_id !== '' ? userState.user.linuxdo_id + '(' + userState.user.linuxdo_level + '级)' : '未绑定'}
|
||||||
readonly={true}
|
readonly={true}
|
||||||
></Input>
|
></Input>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,6 +13,7 @@ const SystemSetting = () => {
|
|||||||
LinuxDoOAuthEnabled: '',
|
LinuxDoOAuthEnabled: '',
|
||||||
LinuxDoClientId: '',
|
LinuxDoClientId: '',
|
||||||
LinuxDoClientSecret: '',
|
LinuxDoClientSecret: '',
|
||||||
|
LinuxDoMinLevel: 0,
|
||||||
Notice: '',
|
Notice: '',
|
||||||
SMTPServer: '',
|
SMTPServer: '',
|
||||||
SMTPPort: '',
|
SMTPPort: '',
|
||||||
@ -135,6 +136,7 @@ const SystemSetting = () => {
|
|||||||
name === 'GitHubClientSecret' ||
|
name === 'GitHubClientSecret' ||
|
||||||
name === 'LinuxDoClientId' ||
|
name === 'LinuxDoClientId' ||
|
||||||
name === 'LinuxDoClientSecret' ||
|
name === 'LinuxDoClientSecret' ||
|
||||||
|
name === 'LinuxDoMinLevel' ||
|
||||||
name === 'WeChatServerAddress' ||
|
name === 'WeChatServerAddress' ||
|
||||||
name === 'WeChatServerToken' ||
|
name === 'WeChatServerToken' ||
|
||||||
name === 'WeChatAccountQRCodeImageURL' ||
|
name === 'WeChatAccountQRCodeImageURL' ||
|
||||||
@ -259,6 +261,9 @@ const SystemSetting = () => {
|
|||||||
) {
|
) {
|
||||||
await updateOption('LinuxDoClientSecret', inputs.LinuxDoClientSecret);
|
await updateOption('LinuxDoClientSecret', inputs.LinuxDoClientSecret);
|
||||||
}
|
}
|
||||||
|
if (originInputs['LinuxDoMinLevel'] !== inputs.LinuxDoMinLevel) {
|
||||||
|
await updateOption('LinuxDoMinLevel', inputs.LinuxDoMinLevel);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitTelegramSettings = async () => {
|
const submitTelegramSettings = async () => {
|
||||||
@ -634,6 +639,16 @@ const SystemSetting = () => {
|
|||||||
value={inputs.LinuxDoClientSecret}
|
value={inputs.LinuxDoClientSecret}
|
||||||
placeholder='敏感信息不会发送到前端显示'
|
placeholder='敏感信息不会发送到前端显示'
|
||||||
/>
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='限制最低信任等级'
|
||||||
|
name='LinuxDoMinLevel'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
type='number'
|
||||||
|
min={0}
|
||||||
|
max={4}
|
||||||
|
value={inputs.LinuxDoMinLevel}
|
||||||
|
placeholder='输入允许使用的最低 LINUX DO 信任等级'
|
||||||
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={submitLinuxDoOAuth}>
|
<Form.Button onClick={submitLinuxDoOAuth}>
|
||||||
保存 LINUX DO OAuth 设置
|
保存 LINUX DO OAuth 设置
|
||||||
|
@ -14,13 +14,15 @@ const EditUser = (props) => {
|
|||||||
password: '',
|
password: '',
|
||||||
github_id: '',
|
github_id: '',
|
||||||
linuxdo_id: '',
|
linuxdo_id: '',
|
||||||
|
linuxdo_level: 0,
|
||||||
wechat_id: '',
|
wechat_id: '',
|
||||||
|
telegram_id: '',
|
||||||
email: '',
|
email: '',
|
||||||
quota: 0,
|
quota: 0,
|
||||||
group: 'default'
|
group: 'default'
|
||||||
});
|
});
|
||||||
const [groupOptions, setGroupOptions] = useState([]);
|
const [groupOptions, setGroupOptions] = useState([]);
|
||||||
const { username, display_name, password, github_id, linuxdo_id, wechat_id, telegram_id, email, quota, group } =
|
const { username, display_name, password, github_id, linuxdo_id, linuxdo_level, wechat_id, telegram_id, email, quota, group } =
|
||||||
inputs;
|
inputs;
|
||||||
const handleInputChange = (name, value) => {
|
const handleInputChange = (name, value) => {
|
||||||
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
||||||
@ -190,7 +192,7 @@ const EditUser = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
name='linuxdo_id'
|
name='linuxdo_id'
|
||||||
value={linuxdo_id}
|
value={linuxdo_id + '(' + linuxdo_level + '级)'}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
|
placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
|
||||||
readonly
|
readonly
|
||||||
|
Loading…
Reference in New Issue
Block a user