Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
Signed-off-by: jolheiser <john.olheiser@gmail.com>
  • Loading branch information
jolheiser committed Dec 3, 2019
1 parent 23ef9ff commit ee8a2de
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 20 deletions.
3 changes: 3 additions & 0 deletions custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ ACCESS_CONTROL_ALLOW_ORIGIN =
USE_COMPAT_SSH_URI = false
; Close issues as long as a commit on any branch marks it as fixed
DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false
; Allow users to push local repositories to Gitea and have them automatically created for a user or an org
ENABLE_PUSH_CREATE_USER = false
ENABLE_PUSH_CREATE_ORG = false

[repository.editor]
; List of file extensions for which lines should be wrapped in the CodeMirror editor
Expand Down
2 changes: 2 additions & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
default is not to present. **WARNING**: This maybe harmful to you website if you do not
give it a right value.
- `DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH`: **false**: Close an issue if a commit on a non default branch marks it as closed.
- `ENABLE_PUSH_CREATE_USER`: **false**: Allow users to push local repositories to Gitea and have them automatically created for a user.
- `ENABLE_PUSH_CREATE_ORG`: **false**: Allow users to push local repositories to Gitea and have them automatically created for an org.

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

Expand Down
52 changes: 52 additions & 0 deletions integrations/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ func testGit(t *testing.T, u *url.URL) {
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
})

t.Run("PushCreate", doPushCreate(httpContext, u))
})
t.Run("SSH", func(t *testing.T) {
defer PrintCurrentTest(t)()
Expand Down Expand Up @@ -113,6 +115,8 @@ func testGit(t *testing.T, u *url.URL) {
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
})

t.Run("PushCreate", doPushCreate(sshContext, sshURL))
})
})
}
Expand Down Expand Up @@ -407,3 +411,51 @@ func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) fun

}
}

func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
return func(t *testing.T) {
defer PrintCurrentTest(t)()
ctx.Reponame = fmt.Sprintf("repo-tmp-push-create-%s", u.Scheme)
u.Path = ctx.GitPath()

tmpDir, err := ioutil.TempDir("", ctx.Reponame)
assert.NoError(t, err)

err = git.InitRepository(tmpDir, false)
assert.NoError(t, err)

_, err = os.Create(filepath.Join(tmpDir, "test.txt"))
assert.NoError(t, err)

err = git.AddChanges(tmpDir, true)
assert.NoError(t, err)

err = git.CommitChanges(tmpDir, git.CommitChangesOptions{
Committer: &git.Signature{
Email: "user2@example.com",
Name: "User Two",
When: time.Now(),
},
Author: &git.Signature{
Email: "user2@example.com",
Name: "User Two",
When: time.Now(),
},
Message: fmt.Sprintf("Testing push create @ %v", time.Now()),
})
assert.NoError(t, err)

_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
assert.NoError(t, err)

// Push to create disabled
setting.Repository.EnablePushCreateUser = false
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
assert.Error(t, err)

// Push to create enabled
setting.Repository.EnablePushCreateUser = true
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
assert.NoError(t, err)
}
}
4 changes: 4 additions & 0 deletions modules/setting/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ var (
AccessControlAllowOrigin string
UseCompatSSHURI bool
DefaultCloseIssuesViaCommitsInAnyBranch bool
EnablePushCreateUser bool
EnablePushCreateOrg bool

// Repository editor settings
Editor struct {
Expand Down Expand Up @@ -89,6 +91,8 @@ var (
AccessControlAllowOrigin: "",
UseCompatSSHURI: false,
DefaultCloseIssuesViaCommitsInAnyBranch: false,
EnablePushCreateUser: false,
EnablePushCreateOrg: false,

// Repository editor settings
Editor: struct {
Expand Down
91 changes: 71 additions & 20 deletions routers/repo/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,29 +100,29 @@ func HTTP(ctx *context.Context) {
return
}

repoExist := true
repo, err := models.GetRepositoryByName(owner.ID, reponame)
if err != nil {
if models.IsErrRepoNotExist(err) {
redirectRepoID, err := models.LookupRepoRedirect(owner.ID, reponame)
if err == nil {
if redirectRepoID, err := models.LookupRepoRedirect(owner.ID, reponame); err == nil {
context.RedirectToRepo(ctx, redirectRepoID)
} else {
ctx.NotFoundOrServerError("GetRepositoryByName", models.IsErrRepoRedirectNotExist, err)
return
}
repoExist = false
} else {
ctx.ServerError("GetRepositoryByName", err)
return
}
return
}

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

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

perm, err := models.GetUserRepoPermission(repo, authUser)
if err != nil {
ctx.ServerError("GetUserRepoPermission", err)
return
}
if repoExist {
perm, err := models.GetUserRepoPermission(repo, authUser)
if err != nil {
ctx.ServerError("GetUserRepoPermission", err)
return
}

if !perm.CanAccess(accessMode, unitType) {
ctx.HandleText(http.StatusForbidden, "User permission denied")
return
}
if !perm.CanAccess(accessMode, unitType) {
ctx.HandleText(http.StatusForbidden, "User permission denied")
return
}

if !isPull && repo.IsMirror {
ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
return
if !isPull && repo.IsMirror {
ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
return
}
}

environ = []string{
models.EnvRepoUsername + "=" + username,
models.EnvRepoName + "=" + reponame,
models.EnvPusherName + "=" + authUser.Name,
models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
models.EnvIsDeployKey + "=false",
}

Expand All @@ -279,6 +280,25 @@ func HTTP(ctx *context.Context) {
}
}

if !repoExist {
if owner.IsOrganization() && !setting.Repository.EnablePushCreateOrg {
ctx.HandleText(http.StatusForbidden, "Push to create is not enabled for organizations.")
return
}
if !owner.IsOrganization() && !setting.Repository.EnablePushCreateUser {
ctx.HandleText(http.StatusForbidden, "Push to create is not enabled for users.")
return
}
repo, err = pushCreateRepo(authUser, owner, reponame)
if err != nil {
log.Error("pushCreateRepo: %v", err)
ctx.Status(http.StatusNotFound)
return
}
}

environ = append(environ, models.ProtectedBranchRepoID+fmt.Sprintf("=%d", repo.ID))

w := ctx.Resp
r := ctx.Req.Request
cfg := &serviceConfig{
Expand Down Expand Up @@ -331,6 +351,37 @@ func HTTP(ctx *context.Context) {
ctx.NotFound("Smart Git HTTP", nil)
}

func pushCreateRepo(authUser, owner *models.User, repoName string) (*models.Repository, error) {
if !authUser.IsAdmin {
if owner.IsOrganization() {
team, err := owner.GetOwnerTeam()
if err != nil {
return nil, err
}
if !team.IsMember(authUser.ID) {
return nil, fmt.Errorf("non-owners cannot push-create repository for an org")
}
} else if authUser.ID != owner.ID {
return nil, fmt.Errorf("cannot push-create repository for another user")
}
}

repo, err := models.CreateRepository(authUser, owner, models.CreateRepoOptions{
Name: repoName,
IsPrivate: true,
})
if err == nil {
return repo, nil
}

if repo != nil {
if errDelete := models.DeleteRepository(authUser, owner.ID, repo.ID); errDelete != nil {
log.Error("DeleteRepository: %v", errDelete)
}
}
return repo, err
}

type serviceConfig struct {
UploadPack bool
ReceivePack bool
Expand Down

0 comments on commit ee8a2de

Please sign in to comment.