Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow admin to associate missing LFS objects for repositories #18143

Merged
merged 6 commits into from
Jan 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 38 additions & 15 deletions models/lfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ package models
import (
"context"
"errors"
"fmt"

"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"

"xorm.io/builder"
Expand Down Expand Up @@ -145,6 +147,11 @@ func LFSObjectAccessible(user *user_model.User, oid string) (bool, error) {
return count > 0, err
}

// LFSObjectIsAssociated checks if a provided Oid is associated
func LFSObjectIsAssociated(oid string) (bool, error) {
return db.GetEngine(db.DefaultContext).Exist(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
}

// LFSAutoAssociate auto associates accessible LFSMetaObjects
func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int64) error {
ctx, committer, err := db.TxContext()
Expand All @@ -162,23 +169,39 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
oidMap[meta.Oid] = meta
}

cond := builder.NewCond()
if !user.IsAdmin {
cond = builder.In("`lfs_meta_object`.repository_id",
builder.Select("`repository`.id").From("repository").Where(accessibleRepositoryCondition(user)))
}
newMetas := make([]*LFSMetaObject, 0, len(metas))
if err := sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas); err != nil {
return err
}
for i := range newMetas {
newMetas[i].Size = oidMap[newMetas[i].Oid].Size
newMetas[i].RepositoryID = repoID
}
if err := db.Insert(ctx, newMetas); err != nil {
return err
newMetas := make([]*LFSMetaObject, 0, len(metas))
cond := builder.In(
"`lfs_meta_object`.repository_id",
builder.Select("`repository`.id").From("repository").Where(accessibleRepositoryCondition(user)),
)
err = sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas)
if err != nil {
return err
}
if len(newMetas) != len(oidMap) {
return fmt.Errorf("unable collect all LFS objects from database, expected %d, actually %d", len(oidMap), len(newMetas))
}
for i := range newMetas {
newMetas[i].Size = oidMap[newMetas[i].Oid].Size
newMetas[i].RepositoryID = repoID
}
if err = db.Insert(ctx, newMetas); err != nil {
return err
}
} else {
// admin can associate any LFS object to any repository, and we do not care about errors (eg: duplicated unique key),
// even if error occurs, it won't hurt users and won't make things worse
for i := range metas {
_, err = sess.Insert(&LFSMetaObject{
Pointer: lfs.Pointer{Oid: metas[i].Oid, Size: metas[i].Size},
RepositoryID: repoID,
})
if err != nil {
log.Warn("failed to insert LFS meta object into database, err=%v", err)
}
zeripath marked this conversation as resolved.
Show resolved Hide resolved
}
}

return committer.Commit()
}

Expand Down
28 changes: 18 additions & 10 deletions routers/web/repo/lfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,13 @@ func LFSPointerFiles(ctx *context.Context) {
var numAssociated, numNoExist, numAssociatable int

type pointerResult struct {
SHA string
Oid string
Size int64
InRepo bool
Exists bool
Accessible bool
SHA string
Oid string
Size int64
InRepo bool
Exists bool
Accessible bool
Associatable bool
}

results := []pointerResult{}
Expand Down Expand Up @@ -461,22 +462,29 @@ func LFSPointerFiles(ctx *context.Context) {
// Can we fix?
// OK well that's "simple"
// - we need to check whether current user has access to a repo that has access to the file
result.Accessible, err = models.LFSObjectAccessible(ctx.User, pointerBlob.Oid)
result.Associatable, err = models.LFSObjectAccessible(ctx.User, pointerBlob.Oid)
if err != nil {
return err
}
} else {
result.Accessible = true
if !result.Associatable {
associated, err := models.LFSObjectIsAssociated(pointerBlob.Oid)
if err != nil {
return err
}
result.Associatable = !associated
}
zeripath marked this conversation as resolved.
Show resolved Hide resolved
}
}

result.Accessible = result.InRepo || result.Associatable

if result.InRepo {
numAssociated++
}
if !result.Exists {
numNoExist++
}
if !result.InRepo && result.Accessible {
if result.Associatable {
numAssociatable++
}

Expand Down
2 changes: 1 addition & 1 deletion templates/repo/settings/lfs_pointers.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<form class="ui form" method="post" action="{{$.Link}}/associate">
{{.CsrfTokenHtml}}
{{range .Pointers}}
{{if and (not .InRepo) .Exists .Accessible}}
{{if .Associatable}}
<input type="hidden" name="oid" value="{{.Oid}} {{.Size}}"/>
{{end}}
{{end}}
Expand Down