mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-20 00:19:00 +00:00
The token a request authenticated with now lives on the doer, so permission and visibility checks apply its scope from the doer alone. Replaces https://github.com/go-gitea/gitea/pull/39167, following https://github.com/go-gitea/gitea/pull/39167#issuecomment-6063839619. 1. Public-only tokens get 404 instead of 403 for hidden users, orgs and repos, like GitHub. 2. Org ownership, site admin, repo and org creation, generation and transfer endpoints reject public-only tokens. 3. API pull request responses set `head.repo` to `null` and `head.repo_id` to `-1` when the caller cannot see the head repository. 4. Public-only tokens see org memberships like anonymous callers, so concealed members stay hidden. 5. Site admin access in activity feeds, follower lists, org members, Actions run lists and packages needs a session or an `all`-scoped token. Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
135 lines
3.1 KiB
Go
135 lines
3.1 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package user
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gitea.dev/models/auth"
|
|
)
|
|
|
|
type ExtDoerData interface {
|
|
EncodeToString() string
|
|
DecodeFromString(string) error
|
|
}
|
|
|
|
type extDoerGiteaActions struct {
|
|
TaskID int64
|
|
}
|
|
|
|
var _ ExtDoerData = (*extDoerGiteaActions)(nil)
|
|
|
|
func (e *extDoerGiteaActions) EncodeToString() string {
|
|
return "gitea-actions:" + strconv.FormatInt(e.TaskID, 10)
|
|
}
|
|
|
|
func (e *extDoerGiteaActions) DecodeFromString(s string) (err error) {
|
|
idStr, _ := strings.CutPrefix(s, "gitea-actions:")
|
|
e.TaskID, err = strconv.ParseInt(idStr, 10, 64)
|
|
return err
|
|
}
|
|
|
|
type extDoerDeployKey struct {
|
|
DeployKeyID int64
|
|
}
|
|
|
|
var _ ExtDoerData = (*extDoerDeployKey)(nil)
|
|
|
|
func (e *extDoerDeployKey) EncodeToString() string {
|
|
return "deploy-key:" + strconv.FormatInt(e.DeployKeyID, 10)
|
|
}
|
|
|
|
func (e *extDoerDeployKey) DecodeFromString(s string) (err error) {
|
|
idStr, _ := strings.CutPrefix(s, "deploy-key:")
|
|
e.DeployKeyID, err = strconv.ParseInt(idStr, 10, 64)
|
|
return err
|
|
}
|
|
|
|
const (
|
|
CredentialAccessToken = "access-token"
|
|
CredentialOAuth2Grant = "oauth2-grant"
|
|
)
|
|
|
|
type extDoerToken struct {
|
|
credential string // empty for package registry tokens
|
|
scope auth.AccessTokenScope
|
|
publicOnly bool
|
|
}
|
|
|
|
var _ ExtDoerData = (*extDoerToken)(nil)
|
|
|
|
func NewTokenExtDoerData(credentialKind string, credentialID int64, scope auth.AccessTokenScope) ExtDoerData {
|
|
publicOnly, err := scope.PublicOnly()
|
|
e := &extDoerToken{scope: scope, publicOnly: publicOnly || err != nil}
|
|
if credentialKind != "" {
|
|
e.credential = credentialKind + ":" + strconv.FormatInt(credentialID, 10)
|
|
}
|
|
return e
|
|
}
|
|
|
|
func (e *extDoerToken) EncodeToString() string {
|
|
return e.credential + "|" + string(e.scope)
|
|
}
|
|
|
|
func (e *extDoerToken) DecodeFromString(s string) error {
|
|
credential, scope, ok := strings.Cut(s, "|")
|
|
if !ok {
|
|
return errors.New("invalid token doer data")
|
|
}
|
|
publicOnly, err := auth.AccessTokenScope(scope).PublicOnly()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.credential, e.scope, e.publicOnly = credential, auth.AccessTokenScope(scope), publicOnly
|
|
return nil
|
|
}
|
|
|
|
func getDoerToken(u *User) *extDoerToken {
|
|
if u == nil {
|
|
return nil
|
|
}
|
|
token, _ := u.ExtDoerData.(*extDoerToken)
|
|
return token
|
|
}
|
|
|
|
func GetDoerTokenScope(u *User) (auth.AccessTokenScope, bool) {
|
|
if token := getDoerToken(u); token != nil {
|
|
return token.scope, true
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func IsPublicOnlyDoer(u *User) bool {
|
|
token := getDoerToken(u)
|
|
return token != nil && token.publicOnly
|
|
}
|
|
|
|
func DoerCanViewPrivate(doer *User) bool {
|
|
return doer != nil && !IsPublicOnlyDoer(doer)
|
|
}
|
|
|
|
func DoerIsSiteAdminFullAccess(doer *User) bool {
|
|
if !DoerCanViewPrivate(doer) || !doer.IsAdmin {
|
|
return false
|
|
}
|
|
scope, hasScope := GetDoerTokenScope(doer)
|
|
return !hasScope || scope == auth.AccessTokenScopeAll
|
|
}
|
|
|
|
func DoerTokenAllowsOwner(doer, owner *User) bool {
|
|
return !IsPublicOnlyDoer(doer) || owner.Visibility.IsPublic()
|
|
}
|
|
|
|
func GetDoerCredential(u *User) string {
|
|
if u == nil || u.ExtDoerData == nil {
|
|
return ""
|
|
}
|
|
if token := getDoerToken(u); token != nil {
|
|
return token.credential
|
|
}
|
|
return u.ExtDoerData.EncodeToString()
|
|
}
|