Skip to content

Commit 6098800

Browse files
committed
...
1 parent 3b525d5 commit 6098800

File tree

6 files changed

+143
-122
lines changed

6 files changed

+143
-122
lines changed

cmd/serv.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,16 @@ func runServ(c *cli.Context) error {
258258
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, username, repo.Name)
259259

260260
now := time.Now()
261-
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
261+
claims := jwt.MapClaims{
262262
"repo": repo.ID,
263263
"op": lfsVerb,
264264
"exp": now.Add(5 * time.Minute).Unix(),
265265
"nbf": now.Unix(),
266-
})
266+
}
267+
if user != nil {
268+
claims["user"] = user.ID
269+
}
270+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
267271

268272
// Sign and get the complete encoded token as a string using the secret
269273
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)

integrations/api_repo_lfs_locks_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func TestAPILFSLocksNotLogin(t *testing.T) {
4141

4242
req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
4343
req.Header.Set("Accept", "application/vnd.git-lfs+json")
44-
resp := MakeRequest(t, req, http.StatusForbidden)
44+
resp := MakeRequest(t, req, http.StatusUnauthorized)
4545
var lfsLockError api.LFSLockError
4646
DecodeJSON(t, resp, &lfsLockError)
4747
assert.Equal(t, "You must have pull access to list locks : User undefined doesn't have rigth to list for lfs lock [rid: 1]", lfsLockError.Message)
@@ -68,8 +68,8 @@ func TestAPILFSLocksLogged(t *testing.T) {
6868
{user: user2, repo: repo1, path: "path/test", httpResult: http.StatusConflict},
6969
{user: user2, repo: repo1, path: "Foo/BaR.zip", httpResult: http.StatusConflict},
7070
{user: user2, repo: repo1, path: "Foo/Test/../subFOlder/../Relative/../BaR.zip", httpResult: http.StatusConflict},
71-
{user: user4, repo: repo1, path: "FoO/BaR.zip", httpResult: http.StatusForbidden},
72-
{user: user4, repo: repo1, path: "path/test-user4", httpResult: http.StatusForbidden},
71+
{user: user4, repo: repo1, path: "FoO/BaR.zip", httpResult: http.StatusUnauthorized},
72+
{user: user4, repo: repo1, path: "path/test-user4", httpResult: http.StatusUnauthorized},
7373
{user: user2, repo: repo1, path: "patH/Test-user4", httpResult: http.StatusCreated, addTime: []int{0}},
7474
{user: user2, repo: repo1, path: "some/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/path", httpResult: http.StatusCreated, addTime: []int{0}},
7575

models/error.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -530,21 +530,24 @@ func (err ErrLFSLockNotExist) Error() string {
530530
return fmt.Sprintf("lfs lock does not exist [id: %d, rid: %d, path: %s]", err.ID, err.RepoID, err.Path)
531531
}
532532

533-
// ErrLFSLockUnauthorizedAction represents a "LFSLockUnauthorizedAction" kind of error.
534-
type ErrLFSLockUnauthorizedAction struct {
535-
RepoID int64
536-
UserName string
537-
Action string
533+
// ErrLFSUnauthorizedAction represents a "LFSUnauthorizedAction" kind of error.
534+
type ErrLFSUnauthorizedAction struct {
535+
RepoID int64
536+
UserName string
537+
RequireWrite bool
538538
}
539539

540-
// IsErrLFSLockUnauthorizedAction checks if an error is a ErrLFSLockUnauthorizedAction.
541-
func IsErrLFSLockUnauthorizedAction(err error) bool {
542-
_, ok := err.(ErrLFSLockUnauthorizedAction)
540+
// IsErrLFSUnauthorizedAction checks if an error is a ErrLFSUnauthorizedAction.
541+
func IsErrLFSUnauthorizedAction(err error) bool {
542+
_, ok := err.(ErrLFSUnauthorizedAction)
543543
return ok
544544
}
545545

546-
func (err ErrLFSLockUnauthorizedAction) Error() string {
547-
return fmt.Sprintf("User %s doesn't have rigth to %s for lfs lock [rid: %d]", err.UserName, err.Action, err.RepoID)
546+
func (err ErrLFSUnauthorizedAction) Error() string {
547+
if err.RequireWrite {
548+
return fmt.Sprintf("User %s doesn't have write access for lfs lock [rid: %d]", err.UserName, err.RepoID)
549+
}
550+
return fmt.Sprintf("User %s doesn't have read access for lfs lock [rid: %d]", err.UserName, err.RepoID)
548551
}
549552

550553
// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error.

models/lfs_lock.go

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,26 @@ import (
1616

1717
// LFSLock represents a git lfs lock of repository.
1818
type LFSLock struct {
19-
ID int64 `xorm:"pk autoincr"`
20-
RepoID int64 `xorm:"INDEX NOT NULL"`
21-
Owner *User `xorm:"-"`
22-
OwnerID int64 `xorm:"INDEX NOT NULL"`
23-
Path string `xorm:"TEXT"`
24-
Created time.Time `xorm:"created"`
19+
ID int64 `xorm:"pk autoincr"`
20+
Repo *Repository `xorm:"-"`
21+
RepoID int64 `xorm:"INDEX NOT NULL"`
22+
Owner *User `xorm:"-"`
23+
OwnerID int64 `xorm:"INDEX NOT NULL"`
24+
Path string `xorm:"TEXT"`
25+
Created time.Time `xorm:"created"`
2526
}
2627

2728
// BeforeInsert is invoked from XORM before inserting an object of this type.
2829
func (l *LFSLock) BeforeInsert() {
2930
l.OwnerID = l.Owner.ID
31+
l.RepoID = l.Repo.ID
3032
l.Path = cleanPath(l.Path)
3133
}
3234

3335
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
3436
func (l *LFSLock) AfterLoad() {
3537
l.Owner, _ = GetUserByID(l.OwnerID)
38+
l.Repo, _ = GetRepositoryByID(l.RepoID)
3639
}
3740

3841
func cleanPath(p string) string {
@@ -53,12 +56,12 @@ func (l *LFSLock) APIFormat() *api.LFSLock {
5356

5457
// CreateLFSLock creates a new lock.
5558
func CreateLFSLock(lock *LFSLock) (*LFSLock, error) {
56-
err := CheckLFSAccessForRepo(lock.Owner, lock.RepoID, "create")
59+
err := CheckLFSAccessForRepo(lock.Owner, lock.Repo, true)
5760
if err != nil {
5861
return nil, err
5962
}
6063

61-
l, err := GetLFSLock(lock.RepoID, lock.Path)
64+
l, err := GetLFSLock(lock.Repo, lock.Path)
6265
if err == nil {
6366
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
6467
}
@@ -71,15 +74,15 @@ func CreateLFSLock(lock *LFSLock) (*LFSLock, error) {
7174
}
7275

7376
// GetLFSLock returns release by given path.
74-
func GetLFSLock(repoID int64, path string) (*LFSLock, error) {
77+
func GetLFSLock(repo *Repository, path string) (*LFSLock, error) {
7578
path = cleanPath(path)
76-
rel := &LFSLock{RepoID: repoID}
79+
rel := &LFSLock{RepoID: repo.ID}
7780
has, err := x.Where("lower(path) = ?", strings.ToLower(path)).Get(rel)
7881
if err != nil {
7982
return nil, err
8083
}
8184
if !has {
82-
return nil, ErrLFSLockNotExist{0, repoID, path}
85+
return nil, ErrLFSLockNotExist{0, repo.ID, path}
8386
}
8487
return rel, nil
8588
}
@@ -109,7 +112,7 @@ func DeleteLFSLockByID(id int64, u *User, force bool) (*LFSLock, error) {
109112
return nil, err
110113
}
111114

112-
err = CheckLFSAccessForRepo(u, lock.RepoID, "delete")
115+
err = CheckLFSAccessForRepo(u, lock.Repo, true)
113116
if err != nil {
114117
return nil, err
115118
}
@@ -123,24 +126,20 @@ func DeleteLFSLockByID(id int64, u *User, force bool) (*LFSLock, error) {
123126
}
124127

125128
//CheckLFSAccessForRepo check needed access mode base on action
126-
func CheckLFSAccessForRepo(u *User, repoID int64, action string) error {
129+
func CheckLFSAccessForRepo(u *User, repo *Repository, reqWrt bool) error {
127130
if u == nil {
128-
return ErrLFSLockUnauthorizedAction{repoID, "undefined", action}
131+
return ErrLFSUnauthorizedAction{repo.ID, "undefined", reqWrt}
129132
}
130133
mode := AccessModeRead
131-
if action == "create" || action == "delete" || action == "verify" {
134+
if reqWrt {
132135
mode = AccessModeWrite
133136
}
134137

135-
repo, err := GetRepositoryByID(repoID)
136-
if err != nil {
137-
return err
138-
}
139138
has, err := HasAccess(u.ID, repo, mode)
140139
if err != nil {
141140
return err
142141
} else if !has {
143-
return ErrLFSLockUnauthorizedAction{repo.ID, u.DisplayName(), action}
142+
return ErrLFSUnauthorizedAction{repo.ID, u.DisplayName(), reqWrt}
144143
}
145144
return nil
146145
}

modules/lfs/locks.go

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@ import (
1313
"code.gitea.io/gitea/modules/context"
1414
"code.gitea.io/gitea/modules/setting"
1515
api "code.gitea.io/sdk/gitea"
16-
17-
"gopkg.in/macaron.v1"
1816
)
1917

20-
func checkRequest(req macaron.Request, post bool) int {
18+
func checkRequest(ctx *context.Context, post bool) int {
2119
if !setting.LFS.StartServer {
2220
return 404
2321
}
24-
if !MetaMatcher(req) {
22+
if !MetaMatcher(ctx.Req) {
2523
return 400
2624
}
25+
if !ctx.IsSigned {
26+
user, _, _, err := parseToken(ctx.Req.Header.Get("Authorization"))
27+
if err != nil {
28+
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
29+
return 401
30+
}
31+
ctx.User = user
32+
}
2733
if post {
28-
mediaParts := strings.Split(req.Header.Get("Content-Type"), ";")
34+
mediaParts := strings.Split(ctx.Req.Header.Get("Content-Type"), ";")
2935
if mediaParts[0] != metaMediaType {
3036
return 400
3137
}
@@ -59,17 +65,18 @@ func handleLockListOut(ctx *context.Context, lock *models.LFSLock, err error) {
5965

6066
// GetListLockHandler list locks
6167
func GetListLockHandler(ctx *context.Context) {
62-
status := checkRequest(ctx.Req, false)
68+
status := checkRequest(ctx, false)
6369
if status != 200 {
6470
writeStatus(ctx, status)
6571
return
6672
}
6773
ctx.Resp.Header().Set("Content-Type", metaMediaType)
6874

69-
err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository.ID, "list")
75+
err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository, true)
7076
if err != nil {
71-
if models.IsErrLFSLockUnauthorizedAction(err) {
72-
ctx.JSON(403, api.LFSLockError{
77+
if models.IsErrLFSUnauthorizedAction(err) {
78+
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
79+
ctx.JSON(401, api.LFSLockError{
7380
Message: "You must have pull access to list locks : " + err.Error(),
7481
})
7582
return
@@ -96,7 +103,7 @@ func GetListLockHandler(ctx *context.Context) {
96103

97104
path := ctx.Query("path")
98105
if path != "" { //Case where we request a specific id
99-
lock, err := models.GetLFSLock(ctx.Repo.Repository.ID, path)
106+
lock, err := models.GetLFSLock(ctx.Repo.Repository, path)
100107
handleLockListOut(ctx, lock, err)
101108
return
102109
}
@@ -120,7 +127,7 @@ func GetListLockHandler(ctx *context.Context) {
120127

121128
// PostLockHandler create lock
122129
func PostLockHandler(ctx *context.Context) {
123-
status := checkRequest(ctx.Req, true)
130+
status := checkRequest(ctx, true)
124131
if status != 200 {
125132
writeStatus(ctx, status)
126133
return
@@ -136,9 +143,9 @@ func PostLockHandler(ctx *context.Context) {
136143
}
137144

138145
lock, err := models.CreateLFSLock(&models.LFSLock{
139-
RepoID: ctx.Repo.Repository.ID,
140-
Path: req.Path,
141-
Owner: ctx.User,
146+
Repo: ctx.Repo.Repository,
147+
Path: req.Path,
148+
Owner: ctx.User,
142149
})
143150
if err != nil {
144151
if models.IsErrLFSLockAlreadyExist(err) {
@@ -148,8 +155,9 @@ func PostLockHandler(ctx *context.Context) {
148155
})
149156
return
150157
}
151-
if models.IsErrLFSLockUnauthorizedAction(err) {
152-
ctx.JSON(403, api.LFSLockError{
158+
if models.IsErrLFSUnauthorizedAction(err) {
159+
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
160+
ctx.JSON(401, api.LFSLockError{
153161
Message: "You must have push access to create locks : " + err.Error(),
154162
})
155163
return
@@ -164,18 +172,19 @@ func PostLockHandler(ctx *context.Context) {
164172

165173
// VerifyLockHandler list locks for verification
166174
func VerifyLockHandler(ctx *context.Context) {
167-
status := checkRequest(ctx.Req, true)
175+
status := checkRequest(ctx, true)
168176
if status != 200 {
169177
writeStatus(ctx, status)
170178
return
171179
}
172180

173181
ctx.Resp.Header().Set("Content-Type", metaMediaType)
174182

175-
err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository.ID, "verify")
183+
err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository, true)
176184
if err != nil {
177-
if models.IsErrLFSLockUnauthorizedAction(err) {
178-
ctx.JSON(403, api.LFSLockError{
185+
if models.IsErrLFSUnauthorizedAction(err) {
186+
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
187+
ctx.JSON(401, api.LFSLockError{
179188
Message: "You must have push access to verify locks : " + err.Error(),
180189
})
181190
return
@@ -211,7 +220,7 @@ func VerifyLockHandler(ctx *context.Context) {
211220

212221
// UnLockHandler delete locks
213222
func UnLockHandler(ctx *context.Context) {
214-
status := checkRequest(ctx.Req, true)
223+
status := checkRequest(ctx, true)
215224
if status != 200 {
216225
writeStatus(ctx, status)
217226
return
@@ -228,8 +237,9 @@ func UnLockHandler(ctx *context.Context) {
228237

229238
lock, err := models.DeleteLFSLockByID(ctx.ParamsInt64("lid"), ctx.User, req.Force)
230239
if err != nil {
231-
if models.IsErrLFSLockUnauthorizedAction(err) {
232-
ctx.JSON(403, api.LFSLockError{
240+
if models.IsErrLFSUnauthorizedAction(err) {
241+
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
242+
ctx.JSON(401, api.LFSLockError{
233243
Message: "You must have push access to delete locks : " + err.Error(),
234244
})
235245
return

0 commit comments

Comments
 (0)