fix(packages): restrict limited owner package access (#39043)

Apply restricted-viewer visibility rules when resolving package access.

---------

Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
bircni
2026-08-23 15:57:51 +08:00
committed by GitHub
co-authored by silverwind
parent adc3db1f27
commit 0bed1232ee
4 changed files with 56 additions and 4 deletions
+5 -1
View File
@@ -126,6 +126,10 @@ func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model
if user.IsAdmin {
return true, nil
}
ownerVisibilities := []structs.VisibleType{structs.VisibleTypePublic}
if !user.IsRestricted {
ownerVisibilities = append(ownerVisibilities, structs.VisibleTypeLimited)
}
maxTeamAuthorize := builder.
Select("max(team.authorize)").
@@ -144,7 +148,7 @@ func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model
// owner = user
builder.Eq{"`user`.id": user.ID}.
// user can see owner
Or(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}.Or(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited})).
Or(builder.In("`user`.visibility", ownerVisibilities)).
// owner is an organization and user has access to it
Or(builder.Eq{"`user`.type": user_model.UserTypeOrganization}.
And(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamAuthorize}.Or(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamUnitAccessMode}))),
+23
View File
@@ -7,6 +7,7 @@ import (
"testing"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -49,3 +50,25 @@ func TestGetOrInsertBlobConcurrent(t *testing.T) {
}
assert.Equal(t, numGoroutines-1, existedCount)
}
func TestIsBlobAccessibleForRestrictedUser(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 33})
pkg, err := TryInsertPackage(t.Context(), &Package{OwnerID: owner.ID, Type: TypeContainer, Name: "limited", LowerName: "limited"})
require.NoError(t, err)
version, err := GetOrInsertVersion(t.Context(), &PackageVersion{PackageID: pkg.ID, Version: "1", LowerVersion: "1"})
require.NoError(t, err)
blob, _, err := GetOrInsertBlob(t.Context(), &PackageBlob{Size: 1, HashMD5: "md5", HashSHA1: "sha1", HashSHA256: "sha256", HashSHA512: "sha512"})
require.NoError(t, err)
_, err = TryInsertFile(t.Context(), &PackageFile{VersionID: version.ID, BlobID: blob.ID, Name: "blob", LowerName: "blob"})
require.NoError(t, err)
accessible, err := IsBlobAccessibleForUser(t.Context(), blob.ID, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}))
require.NoError(t, err)
assert.True(t, accessible)
accessible, err = IsBlobAccessibleForUser(t.Context(), blob.ID, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}))
require.NoError(t, err)
assert.False(t, accessible)
}
+2 -3
View File
@@ -14,7 +14,6 @@ import (
"gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"gitea.dev/modules/structs"
"gitea.dev/modules/templates"
)
@@ -155,10 +154,10 @@ func determineAccessMode(ctx *Base, pkgOwner, doer *user_model.User) (perm.Acces
// 1. Check if user is package owner
if doer.ID == pkgOwner.ID {
accessMode = perm.AccessModeOwner
} else if pkgOwner.Visibility == structs.VisibleTypePublic || pkgOwner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
} else if pkgOwner.Visibility.IsPublic() || (pkgOwner.Visibility.IsLimited() && !doer.IsRestricted) { // 2. Check if package owner is visible to the doer
accessMode = perm.AccessModeRead
}
} else if pkgOwner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
} else if pkgOwner.Visibility.IsPublic() { // 3. Check if package owner is public
accessMode = perm.AccessModeRead
}
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package context
import (
"testing"
"gitea.dev/models/perm"
"gitea.dev/models/user"
"gitea.dev/modules/structs"
"github.com/stretchr/testify/assert"
)
func TestDeterminePackageAccessModeForLimitedOwner(t *testing.T) {
owner := &user.User{ID: 1, Visibility: structs.VisibleTypeLimited}
accessMode, err := determineAccessMode(&Base{}, owner, &user.User{ID: 2, IsActive: true})
assert.NoError(t, err)
assert.Equal(t, perm.AccessModeRead, accessMode)
accessMode, err = determineAccessMode(&Base{}, owner, &user.User{ID: 3, IsActive: true, IsRestricted: true})
assert.NoError(t, err)
assert.Equal(t, perm.AccessModeNone, accessMode)
}