diff --git a/models/repo/redirect.go b/models/repo/redirect.go index 9c44a255d0..e5239a3684 100644 --- a/models/repo/redirect.go +++ b/models/repo/redirect.go @@ -14,8 +14,9 @@ import ( // ErrRedirectNotExist represents a "RedirectNotExist" kind of error. type ErrRedirectNotExist struct { - OwnerID int64 - RepoName string + OwnerID int64 + RepoName string + MissingPermission bool } // IsErrRedirectNotExist check if an error is an ErrRepoRedirectNotExist. @@ -49,8 +50,8 @@ func init() { db.RegisterModel(new(Redirect)) } -// LookupRedirect look up if a repository has a redirect name -func LookupRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) { +// GetRedirect returns the redirect for a given pair of ownerID and repository name. +func GetRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) { repoName = strings.ToLower(repoName) redirect := &Redirect{OwnerID: ownerID, LowerName: repoName} if has, err := db.GetEngine(ctx).Get(redirect); err != nil { diff --git a/models/repo/redirect_test.go b/models/repo/redirect_test.go index d84cbbed54..2f2210588f 100644 --- a/models/repo/redirect_test.go +++ b/models/repo/redirect_test.go @@ -10,21 +10,9 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" - "github.com/stretchr/testify/assert" "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) { // redirect to a completely new name require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 31c884fb88..53fb1e3f12 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -174,7 +174,7 @@ func repoAssignment() func(ctx *context.APIContext) { repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) if err != nil { 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 { context.RedirectToRepo(ctx.Base, redirectRepoID) } else if repo_model.IsErrRedirectNotExist(err) { diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index a60c213113..403245596d 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -31,6 +31,7 @@ import ( "forgejo.org/modules/structs" "forgejo.org/modules/util" "forgejo.org/services/context" + redirect_service "forgejo.org/services/redirect" repo_service "forgejo.org/services/repository" "github.com/go-chi/cors" @@ -111,7 +112,7 @@ func httpBase(ctx *context.Context) *serviceHandler { 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) return nil } diff --git a/services/context/repo.go b/services/context/repo.go index 72ba25828d..e01e1ddafc 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -520,7 +520,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) if err != nil { 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 { RedirectToRepo(ctx.Base, redirectRepoID) } else if repo_model.IsErrRedirectNotExist(err) { diff --git a/services/redirect/repo.go b/services/redirect/repo.go new file mode 100644 index 0000000000..7070ab4e2f --- /dev/null +++ b/services/redirect/repo.go @@ -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 +}