aboutsummaryrefslogtreecommitdiffstats
path: root/models/git/lfs_lock.go
blob: dc5b0a2ceddef0053d90d0c0fef92365083223a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package git

import (
	"context"
	"fmt"
	"path"
	"strings"
	"time"

	"code.gitea.io/gitea/models/db"
	"code.gitea.io/gitea/models/perm"
	access_model "code.gitea.io/gitea/models/perm/access"
	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/models/unit"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/setting"
)

// LFSLock represents a git lfs lock of repository.
type LFSLock struct {
	ID      int64     `xorm:"pk autoincr"`
	RepoID  int64     `xorm:"INDEX NOT NULL"`
	OwnerID int64     `xorm:"INDEX NOT NULL"`
	Path    string    `xorm:"TEXT"`
	Created time.Time `xorm:"created"`
}

func init() {
	db.RegisterModel(new(LFSLock))
}

// BeforeInsert is invoked from XORM before inserting an object of this type.
func (l *LFSLock) BeforeInsert() {
	l.Path = cleanPath(l.Path)
}

func cleanPath(p string) string {
	return path.Clean("/" + p)[1:]
}

// CreateLFSLock creates a new lock.
func CreateLFSLock(repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
	dbCtx, committer, err := db.TxContext(db.DefaultContext)
	if err != nil {
		return nil, err
	}
	defer committer.Close()

	if err := CheckLFSAccessForRepo(dbCtx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil {
		return nil, err
	}

	lock.Path = cleanPath(lock.Path)
	lock.RepoID = repo.ID

	l, err := GetLFSLock(dbCtx, repo, lock.Path)
	if err == nil {
		return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
	}
	if !IsErrLFSLockNotExist(err) {
		return nil, err
	}

	if err := db.Insert(dbCtx, lock); err != nil {
		return nil, err
	}

	return lock, committer.Commit()
}

// GetLFSLock returns release by given path.
func GetLFSLock(ctx context.Context, repo *repo_model.Repository, path string) (*LFSLock, error) {
	path = cleanPath(path)
	rel := &LFSLock{RepoID: repo.ID}
	has, err := db.GetEngine(ctx).Where("lower(path) = ?", strings.ToLower(path)).Get(rel)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, ErrLFSLockNotExist{0, repo.ID, path}
	}
	return rel, nil
}

// GetLFSLockByID returns release by given id.
func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) {
	lock := new(LFSLock)
	has, err := db.GetEngine(ctx).ID(id).Get(lock)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrLFSLockNotExist{id, 0, ""}
	}
	return lock, nil
}

// GetLFSLockByRepoID returns a list of locks of repository.
func GetLFSLockByRepoID(repoID int64, page, pageSize int) ([]*LFSLock, error) {
	e := db.GetEngine(db.DefaultContext)
	if page >= 0 && pageSize > 0 {
		start := 0
		if page > 0 {
			start = (page - 1) * pageSize
		}
		e.Limit(pageSize, start)
	}
	lfsLocks := make([]*LFSLock, 0, pageSize)
	return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID})
}

// GetTreePathLock returns LSF lock for the treePath
func GetTreePathLock(repoID int64, treePath string) (*LFSLock, error) {
	if !setting.LFS.StartServer {
		return nil, nil
	}

	locks, err := GetLFSLockByRepoID(repoID, 0, 0)
	if err != nil {
		return nil, err
	}
	for _, lock := range locks {
		if lock.Path == treePath {
			return lock, nil
		}
	}
	return nil, nil
}

// CountLFSLockByRepoID returns a count of all LFSLocks associated with a repository.
func CountLFSLockByRepoID(repoID int64) (int64, error) {
	return db.GetEngine(db.DefaultContext).Count(&LFSLock{RepoID: repoID})
}

// DeleteLFSLockByID deletes a lock by given ID.
func DeleteLFSLockByID(id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
	dbCtx, committer, err := db.TxContext(db.DefaultContext)
	if err != nil {
		return nil, err
	}
	defer committer.Close()

	lock, err := GetLFSLockByID(dbCtx, id)
	if err != nil {
		return nil, err
	}

	if err := CheckLFSAccessForRepo(dbCtx, u.ID, repo, perm.AccessModeWrite); err != nil {
		return nil, err
	}

	if !force && u.ID != lock.OwnerID {
		return nil, fmt.Errorf("user doesn't own lock and force flag is not set")
	}

	if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
		return nil, err
	}

	return lock, committer.Commit()
}

// CheckLFSAccessForRepo check needed access mode base on action
func CheckLFSAccessForRepo(ctx context.Context, ownerID int64, repo *repo_model.Repository, mode perm.AccessMode) error {
	if ownerID == 0 {
		return ErrLFSUnauthorizedAction{repo.ID, "undefined", mode}
	}
	u, err := user_model.GetUserByID(ctx, ownerID)
	if err != nil {
		return err
	}
	perm, err := access_model.GetUserRepoPermission(ctx, repo, u)
	if err != nil {
		return err
	}
	if !perm.CanAccess(mode, unit.TypeCode) {
		return ErrLFSUnauthorizedAction{repo.ID, u.DisplayName(), mode}
	}
	return nil
}