AccessToken 默认非管理员权限

This commit is contained in:
ryan
2026-06-10 22:36:17 +08:00
parent bff9e8811d
commit 983228227e
15 changed files with 107 additions and 21 deletions
+1
View File
@@ -8,6 +8,7 @@ package admin
// 管理后台错误消息常量
const (
AdminRequired = "未经授权访问"
TokenAdminRequired = "该访问令牌没有管理员权限,无法访问管理端点" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
InvalidAuthSourceID = "认证源 ID 无效"
InvalidCursorParam = "无效的 cursor 参数"
InvalidTaskExecutionID = "无效的任务执行记录 ID"
+9
View File
@@ -25,6 +25,15 @@ func LoginAdminRequired() gin.HandlerFunc {
user, _ := util.GetFromContext[*model.User](c, oauth.UserObjKey)
// 如果是通过 Access Token 鉴权,需要检查令牌本身是否具有管理员权限
if tokenAuth, _ := util.GetFromContext[bool](c, oauth.TokenAuthKey); tokenAuth {
tokenAdmin, _ := util.GetFromContext[bool](c, oauth.TokenAdminKey)
if !tokenAdmin {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error_msg": TokenAdminRequired, "data": nil})
return
}
}
if !user.IsAdmin {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error_msg": AdminRequired, "data": nil})
return
+2
View File
@@ -14,6 +14,8 @@ const (
UserNameKey = "username"
UserIDKey = "user_id"
UserObjKey = "user_obj"
TokenAuthKey = "token_auth" // 标记当前请求是否通过 Access Token 鉴权
TokenAdminKey = "token_admin" // Access Token 本身是否具有管理员权限
PendingOAuthSourceIDKey = "pending_oauth_source_id"
PendingOAuthExternalIDKey = "pending_oauth_external_id"
PendingOAuthExternalUsernameKey = "pending_oauth_external_username"
+6
View File
@@ -45,6 +45,8 @@ func LoginRequired() gin.HandlerFunc {
var user model.User
var authenticated bool
var tokenAuth bool
var tokenAdmin bool
if tokenStr != "" {
tokenHash := model.HashToken(tokenStr)
@@ -52,6 +54,8 @@ func LoginRequired() gin.HandlerFunc {
if err := db.DB(ctx).Where("token_hash = ?", tokenHash).First(&tokenRecord).Error; err == nil {
if err := db.DB(ctx).Where("id = ? AND is_active = ?", tokenRecord.UserID, true).First(&user).Error; err == nil {
authenticated = true
tokenAuth = true
tokenAdmin = tokenRecord.IsAdmin
// update token last used time
now := time.Now()
db.DB(ctx).Model(&tokenRecord).Update("last_used_at", &now)
@@ -80,6 +84,8 @@ func LoginRequired() gin.HandlerFunc {
// set user info
util.SetToContext(c, UserObjKey, &user)
util.SetToContext(c, TokenAuthKey, tokenAuth)
util.SetToContext(c, TokenAdminKey, tokenAdmin)
// next
c.Next()
+10 -2
View File
@@ -18,7 +18,8 @@ import (
)
type createTokenRequest struct {
Name string `json:"name"`
Name string `json:"name"`
IsAdmin bool `json:"is_admin"`
}
type tokenResponse struct {
@@ -51,7 +52,7 @@ func ListAccessTokens(c *gin.Context) {
// CreateAccessToken 创建一个新的 AccessToken
// @Summary 创建一个新的 AccessToken
// @Description 为当前用户新建一个 API 访问令牌,仅在此接口返回一次明文令牌值,请妥善保存。
// @Description 为当前用户新建一个 API 访问令牌,仅在此接口返回一次明文令牌值,请妥善保存。可通过 is_admin 字段赋予令牌管理员权限(仅管理员用户可设置)。
// @Tags user
// @Accept json
// @Produce json
@@ -76,6 +77,12 @@ func CreateAccessToken(c *gin.Context) {
return
}
// 只有管理员才能创建具有管理员权限的令牌
if req.IsAdmin && !currUser.IsAdmin {
c.JSON(http.StatusOK, util.Err(errAdminTokenRequiresAdmin))
return
}
// 检查最大限制(基于 ConfigKeyMaxAPIKeysPerUser 配置,默认值为 5)
maxLimit := 5
if val, err := model.GetIntByKey(ctx, model.ConfigKeyMaxAPIKeysPerUser); err == nil {
@@ -108,6 +115,7 @@ func CreateAccessToken(c *gin.Context) {
Name: req.Name,
TokenHash: tokenHash,
MaskedToken: maskedToken,
IsAdmin: req.IsAdmin,
}
if err := db.DB(ctx).Create(&tokenRecord).Error; err != nil {
+6 -5
View File
@@ -31,11 +31,12 @@ const (
errRenderEmailTemplateFailed = "渲染验证邮件模板失败:%w"
errGenerateEmailCodeFailed = "生成验证码失败,请重试"
errDispatchEmailTaskFailed = "投递验证邮件发送任务失败,请重试"
errTokenNameRequired = "令牌名称不能为空" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errAccessTokenLimitReached = "已达到访问令牌最大创建数量限制" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errGenerateTokenFailed = "生成令牌失败" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errInvalidTokenID = "无效的令牌ID" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errTokenNotFoundOrForbidden = "令牌不存在或无权操作" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errTokenNameRequired = "令牌名称不能为空" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errAccessTokenLimitReached = "已达到访问令牌最大创建数量限制" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errGenerateTokenFailed = "生成令牌失败" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errInvalidTokenID = "无效的令牌ID" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errTokenNotFoundOrForbidden = "令牌不存在或无权操作" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errAdminTokenRequiresAdmin = "只有管理员才能创建具有管理员权限的令牌" //nolint:gosec // false positive: this is an error message, not hardcoded credentials
errTaskPayloadRequired = "任务参数不能为空"
errInvalidJSONFormat = "无效的 JSON 格式: %w"
errEmailTaskFieldsRequired = "to、subject、body 不能为空"