Skip to content

Commit ee8a2de

Browse files
committed
Refactor
Signed-off-by: jolheiser <john.olheiser@gmail.com>
1 parent 23ef9ff commit ee8a2de

File tree

5 files changed

+132
-20
lines changed

5 files changed

+132
-20
lines changed

custom/conf/app.ini.sample

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ ACCESS_CONTROL_ALLOW_ORIGIN =
3939
USE_COMPAT_SSH_URI = false
4040
; Close issues as long as a commit on any branch marks it as fixed
4141
DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false
42+
; Allow users to push local repositories to Gitea and have them automatically created for a user or an org
43+
ENABLE_PUSH_CREATE_USER = false
44+
ENABLE_PUSH_CREATE_ORG = false
4245

4346
[repository.editor]
4447
; List of file extensions for which lines should be wrapped in the CodeMirror editor

docs/content/doc/advanced/config-cheat-sheet.en-us.md

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
6666
default is not to present. **WARNING**: This maybe harmful to you website if you do not
6767
give it a right value.
6868
- `DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH`: **false**: Close an issue if a commit on a non default branch marks it as closed.
69+
- `ENABLE_PUSH_CREATE_USER`: **false**: Allow users to push local repositories to Gitea and have them automatically created for a user.
70+
- `ENABLE_PUSH_CREATE_ORG`: **false**: Allow users to push local repositories to Gitea and have them automatically created for an org.
6971

7072
### Repository - Pull Request (`repository.pull-request`)
7173

integrations/git_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ func testGit(t *testing.T, u *url.URL) {
7575
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
7676
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
7777
})
78+
79+
t.Run("PushCreate", doPushCreate(httpContext, u))
7880
})
7981
t.Run("SSH", func(t *testing.T) {
8082
defer PrintCurrentTest(t)()
@@ -113,6 +115,8 @@ func testGit(t *testing.T, u *url.URL) {
113115
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
114116
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
115117
})
118+
119+
t.Run("PushCreate", doPushCreate(sshContext, sshURL))
116120
})
117121
})
118122
}
@@ -407,3 +411,51 @@ func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) fun
407411

408412
}
409413
}
414+
415+
func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
416+
return func(t *testing.T) {
417+
defer PrintCurrentTest(t)()
418+
ctx.Reponame = fmt.Sprintf("repo-tmp-push-create-%s", u.Scheme)
419+
u.Path = ctx.GitPath()
420+
421+
tmpDir, err := ioutil.TempDir("", ctx.Reponame)
422+
assert.NoError(t, err)
423+
424+
err = git.InitRepository(tmpDir, false)
425+
assert.NoError(t, err)
426+
427+
_, err = os.Create(filepath.Join(tmpDir, "test.txt"))
428+
assert.NoError(t, err)
429+
430+
err = git.AddChanges(tmpDir, true)
431+
assert.NoError(t, err)
432+
433+
err = git.CommitChanges(tmpDir, git.CommitChangesOptions{
434+
Committer: &git.Signature{
435+
Email: "user2@example.com",
436+
Name: "User Two",
437+
When: time.Now(),
438+
},
439+
Author: &git.Signature{
440+
Email: "user2@example.com",
441+
Name: "User Two",
442+
When: time.Now(),
443+
},
444+
Message: fmt.Sprintf("Testing push create @ %v", time.Now()),
445+
})
446+
assert.NoError(t, err)
447+
448+
_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
449+
assert.NoError(t, err)
450+
451+
// Push to create disabled
452+
setting.Repository.EnablePushCreateUser = false
453+
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
454+
assert.Error(t, err)
455+
456+
// Push to create enabled
457+
setting.Repository.EnablePushCreateUser = true
458+
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
459+
assert.NoError(t, err)
460+
}
461+
}

modules/setting/repository.go

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ var (
3535
AccessControlAllowOrigin string
3636
UseCompatSSHURI bool
3737
DefaultCloseIssuesViaCommitsInAnyBranch bool
38+
EnablePushCreateUser bool
39+
EnablePushCreateOrg bool
3840

3941
// Repository editor settings
4042
Editor struct {
@@ -89,6 +91,8 @@ var (
8991
AccessControlAllowOrigin: "",
9092
UseCompatSSHURI: false,
9193
DefaultCloseIssuesViaCommitsInAnyBranch: false,
94+
EnablePushCreateUser: false,
95+
EnablePushCreateOrg: false,
9296

9397
// Repository editor settings
9498
Editor: struct {

routers/repo/http.go

+71-20
Original file line numberDiff line numberDiff line change
@@ -100,29 +100,29 @@ func HTTP(ctx *context.Context) {
100100
return
101101
}
102102

103+
repoExist := true
103104
repo, err := models.GetRepositoryByName(owner.ID, reponame)
104105
if err != nil {
105106
if models.IsErrRepoNotExist(err) {
106-
redirectRepoID, err := models.LookupRepoRedirect(owner.ID, reponame)
107-
if err == nil {
107+
if redirectRepoID, err := models.LookupRepoRedirect(owner.ID, reponame); err == nil {
108108
context.RedirectToRepo(ctx, redirectRepoID)
109-
} else {
110-
ctx.NotFoundOrServerError("GetRepositoryByName", models.IsErrRepoRedirectNotExist, err)
109+
return
111110
}
111+
repoExist = false
112112
} else {
113113
ctx.ServerError("GetRepositoryByName", err)
114+
return
114115
}
115-
return
116116
}
117117

118118
// Don't allow pushing if the repo is archived
119-
if repo.IsArchived && !isPull {
119+
if repoExist && repo.IsArchived && !isPull {
120120
ctx.HandleText(http.StatusForbidden, "This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.")
121121
return
122122
}
123123

124124
// Only public pull don't need auth.
125-
isPublicPull := !repo.IsPrivate && isPull
125+
isPublicPull := repoExist && !repo.IsPrivate && isPull
126126
var (
127127
askAuth = !isPublicPull || setting.Service.RequireSignInView
128128
authUser *models.User
@@ -243,28 +243,29 @@ func HTTP(ctx *context.Context) {
243243
}
244244
}
245245

246-
perm, err := models.GetUserRepoPermission(repo, authUser)
247-
if err != nil {
248-
ctx.ServerError("GetUserRepoPermission", err)
249-
return
250-
}
246+
if repoExist {
247+
perm, err := models.GetUserRepoPermission(repo, authUser)
248+
if err != nil {
249+
ctx.ServerError("GetUserRepoPermission", err)
250+
return
251+
}
251252

252-
if !perm.CanAccess(accessMode, unitType) {
253-
ctx.HandleText(http.StatusForbidden, "User permission denied")
254-
return
255-
}
253+
if !perm.CanAccess(accessMode, unitType) {
254+
ctx.HandleText(http.StatusForbidden, "User permission denied")
255+
return
256+
}
256257

257-
if !isPull && repo.IsMirror {
258-
ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
259-
return
258+
if !isPull && repo.IsMirror {
259+
ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
260+
return
261+
}
260262
}
261263

262264
environ = []string{
263265
models.EnvRepoUsername + "=" + username,
264266
models.EnvRepoName + "=" + reponame,
265267
models.EnvPusherName + "=" + authUser.Name,
266268
models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
267-
models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
268269
models.EnvIsDeployKey + "=false",
269270
}
270271

@@ -279,6 +280,25 @@ func HTTP(ctx *context.Context) {
279280
}
280281
}
281282

283+
if !repoExist {
284+
if owner.IsOrganization() && !setting.Repository.EnablePushCreateOrg {
285+
ctx.HandleText(http.StatusForbidden, "Push to create is not enabled for organizations.")
286+
return
287+
}
288+
if !owner.IsOrganization() && !setting.Repository.EnablePushCreateUser {
289+
ctx.HandleText(http.StatusForbidden, "Push to create is not enabled for users.")
290+
return
291+
}
292+
repo, err = pushCreateRepo(authUser, owner, reponame)
293+
if err != nil {
294+
log.Error("pushCreateRepo: %v", err)
295+
ctx.Status(http.StatusNotFound)
296+
return
297+
}
298+
}
299+
300+
environ = append(environ, models.ProtectedBranchRepoID+fmt.Sprintf("=%d", repo.ID))
301+
282302
w := ctx.Resp
283303
r := ctx.Req.Request
284304
cfg := &serviceConfig{
@@ -331,6 +351,37 @@ func HTTP(ctx *context.Context) {
331351
ctx.NotFound("Smart Git HTTP", nil)
332352
}
333353

354+
func pushCreateRepo(authUser, owner *models.User, repoName string) (*models.Repository, error) {
355+
if !authUser.IsAdmin {
356+
if owner.IsOrganization() {
357+
team, err := owner.GetOwnerTeam()
358+
if err != nil {
359+
return nil, err
360+
}
361+
if !team.IsMember(authUser.ID) {
362+
return nil, fmt.Errorf("non-owners cannot push-create repository for an org")
363+
}
364+
} else if authUser.ID != owner.ID {
365+
return nil, fmt.Errorf("cannot push-create repository for another user")
366+
}
367+
}
368+
369+
repo, err := models.CreateRepository(authUser, owner, models.CreateRepoOptions{
370+
Name: repoName,
371+
IsPrivate: true,
372+
})
373+
if err == nil {
374+
return repo, nil
375+
}
376+
377+
if repo != nil {
378+
if errDelete := models.DeleteRepository(authUser, owner.ID, repo.ID); errDelete != nil {
379+
log.Error("DeleteRepository: %v", errDelete)
380+
}
381+
}
382+
return repo, err
383+
}
384+
334385
type serviceConfig struct {
335386
UploadPack bool
336387
ReceivePack bool

0 commit comments

Comments
 (0)