fix: do permission check for repository redirect

This commit is contained in:
Gusted 2025-08-21 16:58:27 +02:00 committed by Earl Warren
commit ca7fcacddc
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
6 changed files with 46 additions and 19 deletions

View file

@ -16,6 +16,7 @@ import (
type ErrRedirectNotExist struct { type ErrRedirectNotExist struct {
OwnerID int64 OwnerID int64
RepoName string RepoName string
MissingPermission bool
} }
// IsErrRedirectNotExist check if an error is an ErrRepoRedirectNotExist. // IsErrRedirectNotExist check if an error is an ErrRepoRedirectNotExist.
@ -49,8 +50,8 @@ func init() {
db.RegisterModel(new(Redirect)) db.RegisterModel(new(Redirect))
} }
// LookupRedirect look up if a repository has a redirect name // GetRedirect returns the redirect for a given pair of ownerID and repository name.
func LookupRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) { func GetRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) {
repoName = strings.ToLower(repoName) repoName = strings.ToLower(repoName)
redirect := &Redirect{OwnerID: ownerID, LowerName: repoName} redirect := &Redirect{OwnerID: ownerID, LowerName: repoName}
if has, err := db.GetEngine(ctx).Get(redirect); err != nil { if has, err := db.GetEngine(ctx).Get(redirect); err != nil {

View file

@ -10,21 +10,9 @@ import (
repo_model "forgejo.org/models/repo" repo_model "forgejo.org/models/repo"
"forgejo.org/models/unittest" "forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestLookupRedirect(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
repoID, err := repo_model.LookupRedirect(db.DefaultContext, 2, "oldrepo1")
require.NoError(t, err)
assert.EqualValues(t, 1, repoID)
_, err = repo_model.LookupRedirect(db.DefaultContext, unittest.NonexistentID, "doesnotexist")
assert.True(t, repo_model.IsErrRedirectNotExist(err))
}
func TestNewRedirect(t *testing.T) { func TestNewRedirect(t *testing.T) {
// redirect to a completely new name // redirect to a completely new name
require.NoError(t, unittest.PrepareTestDatabase()) require.NoError(t, unittest.PrepareTestDatabase())

View file

@ -174,7 +174,7 @@ func repoAssignment() func(ctx *context.APIContext) {
repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName)
if err != nil { if err != nil {
if repo_model.IsErrRepoNotExist(err) { if repo_model.IsErrRepoNotExist(err) {
redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName) redirectRepoID, err := redirect_service.LookupRepoRedirect(ctx, ctx.Doer, owner.ID, repoName)
if err == nil { if err == nil {
context.RedirectToRepo(ctx.Base, redirectRepoID) context.RedirectToRepo(ctx.Base, redirectRepoID)
} else if repo_model.IsErrRedirectNotExist(err) { } else if repo_model.IsErrRedirectNotExist(err) {

View file

@ -31,6 +31,7 @@ import (
"forgejo.org/modules/structs" "forgejo.org/modules/structs"
"forgejo.org/modules/util" "forgejo.org/modules/util"
"forgejo.org/services/context" "forgejo.org/services/context"
redirect_service "forgejo.org/services/redirect"
repo_service "forgejo.org/services/repository" repo_service "forgejo.org/services/repository"
"github.com/go-chi/cors" "github.com/go-chi/cors"
@ -111,7 +112,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
return nil return nil
} }
if redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, reponame); err == nil { if redirectRepoID, err := redirect_service.LookupRepoRedirect(ctx, ctx.Doer, owner.ID, reponame); err == nil {
context.RedirectToRepo(ctx.Base, redirectRepoID) context.RedirectToRepo(ctx.Base, redirectRepoID)
return nil return nil
} }

View file

@ -520,7 +520,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName)
if err != nil { if err != nil {
if repo_model.IsErrRepoNotExist(err) { if repo_model.IsErrRepoNotExist(err) {
redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName) redirectRepoID, err := redirect_service.LookupRepoRedirect(ctx, ctx.Doer, owner.ID, repoName)
if err == nil { if err == nil {
RedirectToRepo(ctx.Base, redirectRepoID) RedirectToRepo(ctx.Base, redirectRepoID)
} else if repo_model.IsErrRedirectNotExist(err) { } else if repo_model.IsErrRedirectNotExist(err) {

37
services/redirect/repo.go Normal file
View file

@ -0,0 +1,37 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package redirect
import (
"context"
access_model "forgejo.org/models/perm/access"
repo_model "forgejo.org/models/repo"
user_model "forgejo.org/models/user"
)
// LookupRepoRedirect returns the repository ID if there's a redirect registered for
// the ownerID repository name pair. It checks if the doer has permission to view
// the new repository.
func LookupRepoRedirect(ctx context.Context, doer *user_model.User, ownerID int64, repoName string) (int64, error) {
redirectID, err := repo_model.GetRedirect(ctx, ownerID, repoName)
if err != nil {
return 0, err
}
redirectRepo, err := repo_model.GetRepositoryByID(ctx, redirectID)
if err != nil {
return 0, err
}
perm, err := access_model.GetUserRepoPermission(ctx, redirectRepo, doer)
if err != nil {
return 0, err
}
if !perm.HasAccess() {
return 0, repo_model.ErrRedirectNotExist{OwnerID: ownerID, RepoName: repoName, MissingPermission: true}
}
return redirectID, nil
}