修改代码,使得action token具有与已登录用户等同的权限,对其他公开仓库有只读权限 (#59)
Some checks failed
DevStar Studio CI/CD Pipeline / DevStarStudio-CICD-Pipeline (push) Failing after 31m24s

改动文件:
api.go - API 请求的仓库权限分配
routers/web/repo/githttp.go - Git clone/fetch 操作的权限检查
services/lfs/server.go - LFS 大文件下载的认证检查
services/actions/modify_workflow.go 将action token塞到短链接里面
改动逻辑:
开放action token的权限等同于已登录用户,但只有只读权限,短链接action再自动把token塞进去
改动前:
<img width="917" alt="bc847e0bb4a8bf867f7bb66eb8a317d2.png" src="attachments/f30de26e-9a3a-462c-a482-0834124815ae">
改动后:
<img width="833" alt="371f2edf59fa055fc248950b2b88ce8e.png" src="attachments/b9119e12-afcb-48c0-868b-aed2bde07aad">

Co-authored-by: zhongyuezhang <zhongyue.zhang@momenta.ai>
Co-authored-by: hwy <1093970372@qq.com>
Co-authored-by: mengning <mengning@ustc.edu.cn>
Reviewed-on: #59
Co-authored-by: 张中跃24 <1787223498@qq.com>
Co-committed-by: 张中跃24 <1787223498@qq.com>
This commit is contained in:
2026-01-09 03:25:06 +00:00
committed by 孟宁
parent fdc0e05eb3
commit ed37451d81
4 changed files with 50 additions and 22 deletions

View File

@@ -197,21 +197,32 @@ func repoAssignment() func(ctx *context.APIContext) {
return return
} }
if task.RepoID != repo.ID { if task.RepoID != repo.ID {
ctx.APIErrorNotFound() isPublicRepo := !repo.IsPrivate && repo.Owner.Visibility != api.VisibleTypePrivate
return if !isPublicRepo {
} ctx.APIErrorNotFound()
return
if task.IsForkPullRequest { }
ctx.Repo.Permission.AccessMode = perm.AccessModeRead ctx.Repo.Permission.AccessMode = perm.AccessModeRead
if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, perm.AccessModeRead)
} else if task.IsForkPullRequest {
ctx.Repo.Permission.AccessMode = perm.AccessModeRead
if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, perm.AccessModeRead)
} else { } else {
ctx.Repo.Permission.AccessMode = perm.AccessModeWrite ctx.Repo.Permission.AccessMode = perm.AccessModeWrite
if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, perm.AccessModeWrite)
} }
if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, ctx.Repo.Permission.AccessMode)
} else { } else {
needTwoFactor, err := doerNeedTwoFactorAuth(ctx, ctx.Doer) needTwoFactor, err := doerNeedTwoFactorAuth(ctx, ctx.Doer)
if err != nil { if err != nil {

View File

@@ -202,11 +202,13 @@ func httpBase(ctx *context.Context) *serviceHandler {
return nil return nil
} }
if task.RepoID != repo.ID { if task.RepoID != repo.ID {
ctx.PlainText(http.StatusForbidden, "User permission denied") isPublicRepo := !repo.IsPrivate && repo.Owner.Visibility != structs.VisibleTypePrivate
return nil if !isPublicRepo || accessMode > perm.AccessModeRead {
} ctx.PlainText(http.StatusForbidden, "User permission denied")
return nil
if task.IsForkPullRequest { }
environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, perm.AccessModeRead))
} else if task.IsForkPullRequest {
if accessMode > perm.AccessModeRead { if accessMode > perm.AccessModeRead {
ctx.PlainText(http.StatusForbidden, "User permission denied") ctx.PlainText(http.StatusForbidden, "User permission denied")
return nil return nil

View File

@@ -127,17 +127,24 @@ func ReplaceShortFormActions(shortRefs []ActionReference, payload string, baseUR
// 移除 baseURL 末尾的斜杠(如果有) // 移除 baseURL 末尾的斜杠(如果有)
baseURL = strings.TrimSuffix(baseURL, "/") baseURL = strings.TrimSuffix(baseURL, "/")
// 使用 secrets.GITHUB_TOKEN 形式,让 runner 在运行时替换
authURL := baseURL
if strings.HasPrefix(baseURL, "http://") {
authURL = strings.Replace(baseURL, "http://", "http://x-access-token:${{ secrets.GITHUB_TOKEN }}@", 1)
} else if strings.HasPrefix(baseURL, "https://") {
authURL = strings.Replace(baseURL, "https://", "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@", 1)
}
log.Info("Using secrets.GITHUB_TOKEN for action clone authentication")
lines := strings.Split(payload, "\n") lines := strings.Split(payload, "\n")
for _, ref := range shortRefs { for _, ref := range shortRefs {
if ref.LineNumber <= len(lines) { if ref.LineNumber <= len(lines) {
fullURL := fmt.Sprintf("%s/%s", baseURL, ref.ShortRef) fullURL := fmt.Sprintf("%s/%s", authURL, ref.ShortRef)
lines[ref.LineNumber-1] = strings.Replace(lines[ref.LineNumber-1], ref.ShortRef, fullURL, 1) lines[ref.LineNumber-1] = strings.Replace(lines[ref.LineNumber-1], ref.ShortRef, fullURL, 1)
log.Info("Converted line %d: '%s' -> '%s'", ref.LineNumber, ref.ShortRef, fullURL) log.Info("Converted line %d: '%s' -> '%s'", ref.LineNumber, ref.ShortRef, fmt.Sprintf("%s/%s", baseURL, ref.ShortRef))
} }
} }
modifiedPayload := strings.Join(lines, "\n") modifiedPayload := strings.Join(lines, "\n")
return modifiedPayload, nil return modifiedPayload, nil
} }
@@ -166,7 +173,6 @@ func ModifyWorkflowPayload(ctx context.Context, payload []byte) ([]byte, error)
return payload, nil return payload, nil
} }
// 使用 setting.AppURL 执行替换
modifiedPayload, err := ReplaceShortFormActions(availableRefs, payloadStr, setting.AppURL) modifiedPayload, err := ReplaceShortFormActions(availableRefs, payloadStr, setting.AppURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to replace short-form actions: %w", err) return nil, fmt.Errorf("failed to replace short-form actions: %w", err)

View File

@@ -34,6 +34,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@@ -555,7 +556,15 @@ func authenticate(ctx *context.Context, repository *repo_model.Repository, autho
return false return false
} }
if task.RepoID != repository.ID { if task.RepoID != repository.ID {
return false if err := repository.LoadOwner(ctx); err != nil {
log.Error("Unable to LoadOwner for repository[%d] Error: %v", repository.ID, err)
return false
}
isPublicRepo := !repository.IsPrivate && repository.Owner.Visibility != structs.VisibleTypePrivate
if !isPublicRepo || accessMode > perm_model.AccessModeRead {
return false
}
return true
} }
if task.IsForkPullRequest { if task.IsForkPullRequest {