Merge branch 'main' into lunny/fix_wrong_tablename

This commit is contained in:
Lunny Xiao 2024-04-20 20:21:16 +08:00
commit c1a477d076
19 changed files with 120 additions and 59 deletions

View File

@ -118,7 +118,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error
// Remove watches from all users and now unaccessible repos
for _, user := range t.Members {
has, err := access_model.HasAccess(ctx, user.ID, repo)
has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
if err != nil {
return err
} else if has {
@ -544,7 +544,7 @@ func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Reposito
}
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
if has, err := access_model.HasAccess(ctx, user.ID, repo); err != nil || has {
if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
return err
}
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {

View File

@ -287,9 +287,10 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
// SearchVersions gets all versions of packages matching the search options
func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
sess := db.GetEngine(ctx).
Where(opts.ToConds()).
Select("package_version.*").
Table("package_version").
Join("INNER", "package", "package.id = package_version.package_id")
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.ToConds())
opts.configureOrderBy(sess)
@ -304,19 +305,18 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
// SearchLatestVersions gets the latest version of every package matching the search options
func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
cond := opts.ToConds().
And(builder.Expr("pv2.id IS NULL"))
joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))")
if opts.IsInternal.Has() {
joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()})
}
in := builder.
Select("MAX(package_version.id)").
From("package_version").
InnerJoin("package", "package.id = package_version.package_id").
Where(opts.ToConds()).
GroupBy("package_version.package_id")
sess := db.GetEngine(ctx).
Select("package_version.*").
Table("package_version").
Join("LEFT", "package_version pv2", joinCond).
Join("INNER", "package", "package.id = package_version.package_id").
Where(cond)
Where(builder.In("package_version.id", in))
opts.configureOrderBy(sess)

View File

@ -79,17 +79,17 @@ func TestHasAccess(t *testing.T) {
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.True(t, repo2.IsPrivate)
has, err := access_model.HasAccess(db.DefaultContext, user1.ID, repo1)
has, err := access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo1)
assert.NoError(t, err)
assert.True(t, has)
_, err = access_model.HasAccess(db.DefaultContext, user1.ID, repo2)
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo2)
assert.NoError(t, err)
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo1)
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo1)
assert.NoError(t, err)
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo2)
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo2)
assert.NoError(t, err)
}

View File

@ -24,6 +24,8 @@ type Permission struct {
units []*repo_model.RepoUnit
unitsMode map[unit.Type]perm_model.AccessMode
everyoneAccessMode map[unit.Type]perm_model.AccessMode
}
// IsOwner returns true if current user is the owner of repository.
@ -36,9 +38,24 @@ func (p *Permission) IsAdmin() bool {
return p.AccessMode >= perm_model.AccessModeAdmin
}
// HasAccess returns true if the current user might have at least read access to any unit of this repository
func (p *Permission) HasAccess() bool {
return len(p.unitsMode) > 0 || p.AccessMode >= perm_model.AccessModeRead
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
// It doesn't count the "everyone access mode".
func (p *Permission) HasAnyUnitAccess() bool {
for _, v := range p.unitsMode {
if v >= perm_model.AccessModeRead {
return true
}
}
return p.AccessMode >= perm_model.AccessModeRead
}
func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
for _, v := range p.everyoneAccessMode {
if v >= perm_model.AccessModeRead {
return true
}
}
return p.HasAnyUnitAccess()
}
// HasUnits returns true if the permission contains attached units
@ -56,16 +73,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
}
// UnitAccessMode returns current user access mode to the specify unit of the repository
// It also considers "everyone access mode"
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
if p.unitsMode != nil {
// if the units map contains the access mode, use it, but admin/owner mode could override it
if m, ok := p.unitsMode[unitType]; ok {
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
}
// if the units map contains the access mode, use it, but admin/owner mode could override it
if m, ok := p.unitsMode[unitType]; ok {
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
}
// if the units map does not contain the access mode, return the default access mode if the unit exists
unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
return util.Iif(hasUnit, p.AccessMode, perm_model.AccessModeNone)
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
}
func (p *Permission) SetUnitsWithDefaultAccessMode(units []*repo_model.RepoUnit, mode perm_model.AccessMode) {
@ -159,14 +176,15 @@ func (p *Permission) LogString() string {
}
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
if user != nil && user.ID > 0 {
for _, u := range perm.units {
if perm.unitsMode == nil {
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
}
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.unitsMode[u.Type] {
perm.unitsMode[u.Type] = u.EveryoneAccessMode
if user == nil || user.ID <= 0 {
return
}
for _, u := range perm.units {
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
if perm.everyoneAccessMode == nil {
perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
}
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
}
}
}
@ -373,8 +391,8 @@ func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.
perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil
}
// HasAccess returns true if user has access to repo
func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
// HasAnyUnitAccess see the comment of "perm.HasAnyUnitAccess"
func HasAnyUnitAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
var user *user_model.User
var err error
if userID > 0 {
@ -387,7 +405,7 @@ func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (
if err != nil {
return false, err
}
return perm.HasAccess(), nil
return perm.HasAnyUnitAccess(), nil
}
// getUsersWithAccessMode returns users that have at least given access mode to the repository.

View File

@ -14,16 +14,54 @@ import (
"github.com/stretchr/testify/assert"
)
func TestHasAnyUnitAccess(t *testing.T) {
perm := Permission{}
assert.False(t, perm.HasAnyUnitAccess())
perm = Permission{
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
}
assert.False(t, perm.HasAnyUnitAccess())
assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
perm = Permission{
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
}
assert.False(t, perm.HasAnyUnitAccess())
assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
perm = Permission{
AccessMode: perm_model.AccessModeRead,
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
}
assert.True(t, perm.HasAnyUnitAccess())
perm = Permission{
unitsMode: map[unit.Type]perm_model.AccessMode{unit.TypeWiki: perm_model.AccessModeRead},
}
assert.True(t, perm.HasAnyUnitAccess())
}
func TestApplyEveryoneRepoPermission(t *testing.T) {
perm := Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeNone},
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
applyEveryoneRepoPermission(nil, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
@ -40,8 +78,8 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
},
}
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanRead(unit.TypeWiki))
assert.False(t, perm.CanWrite(unit.TypeWiki)) // because there is no unit mode, so the everyone-mode is used as the unit's access mode
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
assert.True(t, perm.CanWrite(unit.TypeWiki))
perm = Permission{
units: []*repo_model.RepoUnit{

View File

@ -468,7 +468,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
_, _ = rd.Discard(1)
}
for {
modifier, err := rd.ReadSlice('\x00')
modifier, err := rd.ReadString('\x00')
if err != nil {
if err != io.EOF {
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)

View File

@ -218,7 +218,7 @@ func repoAssignment() func(ctx *context.APIContext) {
}
}
if !ctx.Repo.HasAccess() {
if !ctx.Repo.Permission.HasAnyUnitAccess() {
ctx.NotFound()
return
}
@ -412,7 +412,7 @@ func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) {
// reqAnyRepoReader user should have any permission to read repository or permissions of site admin
func reqAnyRepoReader() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.Repo.HasAccess() && !ctx.IsUserSiteAdmin() {
if !ctx.Repo.Permission.HasAnyUnitAccess() && !ctx.IsUserSiteAdmin() {
ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin")
return
}

View File

@ -585,7 +585,7 @@ func GetByID(ctx *context.APIContext) {
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
return
} else if !permission.HasAccess() {
} else if !permission.HasAnyUnitAccess() {
ctx.NotFound()
return
}

View File

@ -82,7 +82,7 @@ func ListPackages(ctx *context.Context) {
ctx.ServerError("GetUserRepoPermission", err)
return
}
repositoryAccessMap[pd.Repository.ID] = permission.HasAccess()
repositoryAccessMap[pd.Repository.ID] = permission.HasAnyUnitAccess()
}
hasPackages, err := packages_model.HasOwnerPackages(ctx, ctx.ContextUser.ID)
@ -276,7 +276,7 @@ func ViewPackageVersion(ctx *context.Context) {
ctx.ServerError("GetUserRepoPermission", err)
return
}
hasRepositoryAccess = permission.HasAccess()
hasRepositoryAccess = permission.HasAnyUnitAccess()
}
ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess

View File

@ -258,7 +258,7 @@ func Routes() *web.Route {
routes.Get("/metrics", append(mid, Metrics)...)
}
routes.Get("/robots.txt", append(mid, misc.RobotsTxt)...)
routes.Methods("GET,HEAD", "/robots.txt", append(mid, misc.RobotsTxt)...)
routes.Get("/ssh_info", misc.SSHInfo)
routes.Get("/api/healthz", healthcheck.Check)

View File

@ -78,6 +78,11 @@ func newNotifyInput(repo *repo_model.Repository, doer *user_model.User, event we
}
}
func newNotifyInputForSchedules(repo *repo_model.Repository) *notifyInput {
// the doer here will be ignored as we force using action user when handling schedules
return newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
}
func (input *notifyInput) WithDoer(doer *user_model.User) *notifyInput {
input.Doer = doer
return input
@ -485,7 +490,7 @@ func handleSchedules(
RepoID: input.Repo.ID,
OwnerID: input.Repo.OwnerID,
WorkflowID: dwf.EntryName,
TriggerUserID: input.Doer.ID,
TriggerUserID: user_model.ActionsUserID,
Ref: ref,
CommitSHA: commit.ID.String(),
Event: input.Event,
@ -527,7 +532,7 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository)
// We need a notifyInput to call handleSchedules
// if repo is a mirror, commit author maybe an external user,
// so we use action user as the Doer of the notifyInput
notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
notifyInput := newNotifyInputForSchedules(repo)
return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch)
}

View File

@ -374,8 +374,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
return
}
// Check access.
if !ctx.Repo.Permission.HasAccess() {
if !ctx.Repo.Permission.HasAnyUnitAccessOrEveryoneAccess() {
if ctx.FormString("go-get") == "1" {
EarlyResponseForGoGetMeta(ctx)
return

View File

@ -21,7 +21,7 @@ func ToPackage(ctx context.Context, pd *packages.PackageDescriptor, doer *user_m
return nil, err
}
if permission.HasAccess() {
if permission.HasAnyUnitAccess() {
repo = ToRepo(ctx, pd.Repository, permission)
}
}

View File

@ -374,7 +374,7 @@ func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *r
return fmt.Errorf("GetTeamMembers: %w", err)
}
for _, member := range teamMembers {
has, err := access_model.HasAccess(ctx, member.ID, repo)
has, err := access_model.HasAnyUnitAccess(ctx, member.ID, repo)
if err != nil {
return err
} else if has {

View File

@ -387,7 +387,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use
}
// In case the new owner would not have sufficient access to the repo, give access rights for read
hasAccess, err := access_model.HasAccess(ctx, newOwner.ID, repo)
hasAccess, err := access_model.HasAnyUnitAccess(ctx, newOwner.ID, repo)
if err != nil {
return err
}

View File

@ -67,13 +67,13 @@ func TestStartRepositoryTransferSetPermission(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
hasAccess, err := access_model.HasAccess(db.DefaultContext, recipient.ID, repo)
hasAccess, err := access_model.HasAnyUnitAccess(db.DefaultContext, recipient.ID, repo)
assert.NoError(t, err)
assert.False(t, hasAccess)
assert.NoError(t, StartRepositoryTransfer(db.DefaultContext, doer, recipient, repo, nil))
hasAccess, err = access_model.HasAccess(db.DefaultContext, recipient.ID, repo)
hasAccess, err = access_model.HasAnyUnitAccess(db.DefaultContext, recipient.ID, repo)
assert.NoError(t, err)
assert.True(t, hasAccess)

View File

@ -4,11 +4,12 @@
<title>Links for {{.PackageDescriptor.Package.Name}}</title>
</head>
<body>
{{- /* PEP 503 Simple Repository API: https://peps.python.org/pep-0503/ */ -}}
<h1>Links for {{.PackageDescriptor.Package.Name}}</h1>
{{range .PackageDescriptors}}
{{$p := .}}
{{$pd := .}}
{{range .Files}}
<a href="{{$.RegistryURL}}/files/{{$p.Package.LowerName}}/{{$p.Version.Version}}/{{.File.Name}}#sha256-{{.Blob.HashSHA256}}"{{if $p.Metadata.RequiresPython}} data-requires-python="{{$p.Metadata.RequiresPython}}"{{end}}>{{.File.Name}}</a><br>
<a href="{{$.RegistryURL}}/files/{{$pd.Package.LowerName}}/{{$pd.Version.Version}}/{{.File.Name}}#sha256={{.Blob.HashSHA256}}"{{if $pd.Metadata.RequiresPython}} data-requires-python="{{$pd.Metadata.RequiresPython}}"{{end}}>{{.File.Name}}</a><br>
{{end}}
{{end}}
</body>

View File

@ -164,7 +164,7 @@ func TestPackagePyPI(t *testing.T) {
nodes := htmlDoc.doc.Find("a").Nodes
assert.Len(t, nodes, 2)
hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256))
hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256=%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256))
for _, a := range nodes {
for _, att := range a.Attr {

View File

@ -222,7 +222,7 @@ func TestAPISearchRepo(t *testing.T) {
assert.Len(t, repoNames, expected.count)
for _, repo := range body.Data {
r := getRepo(t, repo.ID)
hasAccess, err := access_model.HasAccess(db.DefaultContext, userID, r)
hasAccess, err := access_model.HasAnyUnitAccess(db.DefaultContext, userID, r)
assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err)
assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName)