diff options
author | Unknwon <u@gogs.io> | 2017-02-17 15:10:50 -0500 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2017-02-17 15:10:50 -0500 |
commit | 7e09d210ba421a1baf03ef7ba8770bebe8d28b72 (patch) | |
tree | 1d209f6175c3e4d7e21c4bda62e65db0ac4608f5 /routers/repo | |
parent | dab768212af15f4e671e5403ad3def455117f699 (diff) |
Initial version of protected branches (#776)
- Able to restrict force push and deletion
- Able to restrict direct push
Diffstat (limited to 'routers/repo')
-rw-r--r-- | routers/repo/http.go | 50 | ||||
-rw-r--r-- | routers/repo/pull.go | 16 | ||||
-rw-r--r-- | routers/repo/setting.go | 150 | ||||
-rw-r--r-- | routers/repo/view.go | 13 |
4 files changed, 167 insertions, 62 deletions
diff --git a/routers/repo/http.go b/routers/repo/http.go index c3cec9e3..51e393d0 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -28,18 +28,20 @@ import ( ) const ( - ENV_AUTH_USER_ID = "AUTH_USER_ID" - ENV_AUTH_USER_NAME = "AUTH_USER_NAME" - ENV_REPO_OWNER_NAME = "REPO_OWNER_NAME" - ENV_REPO_OWNER_SALT_MD5 = "REPO_OWNER_SALT_MD5" - ENV_REPO_NAME = "REPO_NAME" - ENV_REPO_CUSTOM_HOOKS_PATH = "REPO_CUSTOM_HOOKS_PATH" + ENV_AUTH_USER_ID = "GOGS_AUTH_USER_ID" + ENV_AUTH_USER_NAME = "GOGS_AUTH_USER_NAME" + ENV_REPO_OWNER_NAME = "GOGS_REPO_OWNER_NAME" + ENV_REPO_OWNER_SALT_MD5 = "GOGS_REPO_OWNER_SALT_MD5" + ENV_REPO_ID = "GOGS_REPO_ID" + ENV_REPO_NAME = "GOGS_REPO_NAME" + ENV_REPO_CUSTOM_HOOKS_PATH = "GOGS_REPO_CUSTOM_HOOKS_PATH" ) type HTTPContext struct { *context.Context OwnerName string OwnerSalt string + RepoID int64 RepoName string AuthUser *models.User } @@ -143,6 +145,7 @@ func HTTPContexter() macaron.Handler { Context: ctx, OwnerName: ownerName, OwnerSalt: owner.Salt, + RepoID: repo.ID, RepoName: repoName, AuthUser: authUser, }) @@ -158,6 +161,7 @@ type serviceHandler struct { authUser *models.User ownerName string ownerSalt string + repoID int64 repoName string } @@ -189,15 +193,25 @@ func (h *serviceHandler) sendFile(contentType string) { http.ServeFile(h.w, h.r, reqFile) } -func ComposeHookEnvs(repoPath, ownerName, ownerSalt, repoName string, authUser *models.User) []string { +type ComposeHookEnvsOptions struct { + AuthUser *models.User + OwnerName string + OwnerSalt string + RepoID int64 + RepoName string + RepoPath string +} + +func ComposeHookEnvs(opts ComposeHookEnvsOptions) []string { envs := []string{ "SSH_ORIGINAL_COMMAND=1", - ENV_AUTH_USER_ID + "=" + com.ToStr(authUser.ID), - ENV_AUTH_USER_NAME + "=" + authUser.Name, - ENV_REPO_OWNER_NAME + "=" + ownerName, - ENV_REPO_OWNER_SALT_MD5 + "=" + base.EncodeMD5(ownerSalt), - ENV_REPO_NAME + "=" + repoName, - ENV_REPO_CUSTOM_HOOKS_PATH + "=" + path.Join(repoPath, "custom_hooks"), + ENV_AUTH_USER_ID + "=" + com.ToStr(opts.AuthUser.ID), + ENV_AUTH_USER_NAME + "=" + opts.AuthUser.Name, + ENV_REPO_OWNER_NAME + "=" + opts.OwnerName, + ENV_REPO_OWNER_SALT_MD5 + "=" + base.EncodeMD5(opts.OwnerSalt), + ENV_REPO_ID + "=" + com.ToStr(opts.RepoID), + ENV_REPO_NAME + "=" + opts.RepoName, + ENV_REPO_CUSTOM_HOOKS_PATH + "=" + path.Join(opts.RepoPath, "custom_hooks"), } return envs } @@ -229,7 +243,14 @@ func serviceRPC(h serviceHandler, service string) { var stderr bytes.Buffer cmd := exec.Command("git", service, "--stateless-rpc", h.dir) if service == "receive-pack" { - cmd.Env = append(os.Environ(), ComposeHookEnvs(h.dir, h.ownerName, h.ownerSalt, h.repoName, h.authUser)...) + cmd.Env = append(os.Environ(), ComposeHookEnvs(ComposeHookEnvsOptions{ + AuthUser: h.authUser, + OwnerName: h.ownerName, + OwnerSalt: h.ownerSalt, + RepoID: h.repoID, + RepoName: h.repoName, + RepoPath: h.dir, + })...) } cmd.Dir = h.dir cmd.Stdout = h.w @@ -392,6 +413,7 @@ func HTTP(ctx *HTTPContext) { authUser: ctx.AuthUser, ownerName: ctx.OwnerName, ownerSalt: ctx.OwnerSalt, + repoID: ctx.RepoID, repoName: ctx.RepoName, }) return diff --git a/routers/repo/pull.go b/routers/repo/pull.go index db8d70db..faf3b789 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -711,6 +711,22 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pullIssue.Index)) } +func parseOwnerAndRepo(ctx *context.Context) (*models.User, *models.Repository) { + owner, err := models.GetUserByName(ctx.Params(":username")) + if err != nil { + ctx.NotFoundOrServerError("GetUserByName", models.IsErrUserNotExist, err) + return nil, nil + } + + repo, err := models.GetRepositoryByName(owner.ID, ctx.Params(":reponame")) + if err != nil { + ctx.NotFoundOrServerError("GetRepositoryByName", models.IsErrRepoNotExist, err) + return nil, nil + } + + return owner, repo +} + func TriggerTask(ctx *context.Context) { pusherID := ctx.QueryInt64("pusher") branch := ctx.Query("branch") diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 8b8a25d5..1f3a18cf 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -5,6 +5,7 @@ package repo import ( + "fmt" "strings" "time" @@ -21,11 +22,13 @@ import ( ) const ( - SETTINGS_OPTIONS base.TplName = "repo/settings/options" - COLLABORATION base.TplName = "repo/settings/collaboration" - GITHOOKS base.TplName = "repo/settings/githooks" - GITHOOK_EDIT base.TplName = "repo/settings/githook_edit" - DEPLOY_KEYS base.TplName = "repo/settings/deploy_keys" + SETTINGS_OPTIONS base.TplName = "repo/settings/options" + SETTINGS_COLLABORATION base.TplName = "repo/settings/collaboration" + SETTINGS_BRANCHES base.TplName = "repo/settings/branches" + SETTINGS_PROTECTED_BRANCH base.TplName = "repo/settings/protected_branch" + SETTINGS_GITHOOKS base.TplName = "repo/settings/githooks" + SETTINGS_GITHOOK_EDIT base.TplName = "repo/settings/githook_edit" + SETTINGS_DEPLOY_KEYS base.TplName = "repo/settings/deploy_keys" ) func Settings(ctx *context.Context) { @@ -74,16 +77,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { repo.Name = newRepoName repo.LowerName = strings.ToLower(newRepoName) - if ctx.Repo.GitRepo.IsBranchExist(form.Branch) && - repo.DefaultBranch != form.Branch { - repo.DefaultBranch = form.Branch - if err := ctx.Repo.GitRepo.SetDefaultBranch(form.Branch); err != nil { - if !git.IsErrUnsupportedVersion(err) { - ctx.Handle(500, "SetDefaultBranch", err) - return - } - } - } repo.Description = form.Description repo.Website = form.Website @@ -295,7 +288,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { } } -func Collaboration(ctx *context.Context) { +func SettingsCollaboration(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsCollaboration"] = true @@ -306,10 +299,10 @@ func Collaboration(ctx *context.Context) { } ctx.Data["Collaborators"] = users - ctx.HTML(200, COLLABORATION) + ctx.HTML(200, SETTINGS_COLLABORATION) } -func CollaborationPost(ctx *context.Context) { +func SettingsCollaborationPost(ctx *context.Context) { name := strings.ToLower(ctx.Query("collaborator")) if len(name) == 0 || ctx.Repo.Owner.LowerName == name { ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) @@ -374,31 +367,102 @@ func DeleteCollaboration(ctx *context.Context) { }) } -func parseOwnerAndRepo(ctx *context.Context) (*models.User, *models.Repository) { - owner, err := models.GetUserByName(ctx.Params(":username")) +func SettingsBranches(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings.branches") + ctx.Data["PageIsSettingsBranches"] = true + + protectBranches, err := models.GetProtectBranchesByRepoID(ctx.Repo.Repository.ID) if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Handle(404, "GetUserByName", nil) - } else { - ctx.Handle(500, "GetUserByName", err) + ctx.Handle(500, "GetProtectBranchesByRepoID", err) + return + } + ctx.Data["ProtectBranches"] = protectBranches + + ctx.HTML(200, SETTINGS_BRANCHES) +} + +func UpdateDefaultBranch(ctx *context.Context) { + branch := ctx.Query("branch") + if ctx.Repo.GitRepo.IsBranchExist(branch) && + ctx.Repo.Repository.DefaultBranch != branch { + ctx.Repo.Repository.DefaultBranch = branch + if err := ctx.Repo.GitRepo.SetDefaultBranch(branch); err != nil { + if !git.IsErrUnsupportedVersion(err) { + ctx.Handle(500, "SetDefaultBranch", err) + return + } + } + } + + if err := models.UpdateRepository(ctx.Repo.Repository, false); err != nil { + ctx.Handle(500, "UpdateRepository", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.update_default_branch_success")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/branches") +} + +func SettingsProtectedBranch(ctx *context.Context) { + branch := ctx.Params("*") + if !ctx.Repo.GitRepo.IsBranchExist(branch) { + ctx.NotFound() + return + } + + ctx.Data["Title"] = ctx.Tr("repo.settings.protected_branches") + " - " + branch + ctx.Data["PageIsSettingsBranches"] = true + ctx.Data["IsOrgRepo"] = ctx.Repo.Owner.IsOrganization() + + protectBranch, err := models.GetProtectBranchOfRepoByName(ctx.Repo.Repository.ID, branch) + if err != nil { + if !models.IsErrBranchNotExist(err) { + ctx.Handle(500, "GetProtectBranchOfRepoByName", err) + return + } + + // No options found, create defaults. + protectBranch = &models.ProtectBranch{ + Name: branch, } - return nil, nil } - repo, err := models.GetRepositoryByName(owner.ID, ctx.Params(":reponame")) + ctx.Data["Branch"] = protectBranch + ctx.HTML(200, SETTINGS_PROTECTED_BRANCH) +} + +func SettingsProtectedBranchPost(ctx *context.Context, form auth.ProtectBranchForm) { + branch := ctx.Params("*") + if !ctx.Repo.GitRepo.IsBranchExist(branch) { + ctx.NotFound() + return + } + + protectBranch, err := models.GetProtectBranchOfRepoByName(ctx.Repo.Repository.ID, branch) if err != nil { - if models.IsErrRepoNotExist(err) { - ctx.Handle(404, "GetRepositoryByName", nil) - } else { - ctx.Handle(500, "GetRepositoryByName", err) + if !models.IsErrBranchNotExist(err) { + ctx.Handle(500, "GetProtectBranchOfRepoByName", err) + return } - return nil, nil + + // No options found, create defaults. + protectBranch = &models.ProtectBranch{ + RepoID: ctx.Repo.Repository.ID, + Name: branch, + } + } + + protectBranch.Protected = form.Protected + protectBranch.RequirePullRequest = form.RequirePullRequest + if err = models.UpdateProtectBranch(protectBranch); err != nil { + ctx.Handle(500, "UpdateProtectBranch", err) + return } - return owner, repo + ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch)) } -func GitHooks(ctx *context.Context) { +func SettingsGitHooks(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings.githooks") ctx.Data["PageIsSettingsGitHooks"] = true @@ -409,10 +473,10 @@ func GitHooks(ctx *context.Context) { } ctx.Data["Hooks"] = hooks - ctx.HTML(200, GITHOOKS) + ctx.HTML(200, SETTINGS_GITHOOKS) } -func GitHooksEdit(ctx *context.Context) { +func SettingsGitHooksEdit(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings.githooks") ctx.Data["PageIsSettingsGitHooks"] = true @@ -427,10 +491,10 @@ func GitHooksEdit(ctx *context.Context) { return } ctx.Data["Hook"] = hook - ctx.HTML(200, GITHOOK_EDIT) + ctx.HTML(200, SETTINGS_GITHOOK_EDIT) } -func GitHooksEditPost(ctx *context.Context) { +func SettingsGitHooksEditPost(ctx *context.Context) { name := ctx.Params(":name") hook, err := ctx.Repo.GitRepo.GetHook(name) if err != nil { @@ -449,7 +513,7 @@ func GitHooksEditPost(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git") } -func DeployKeys(ctx *context.Context) { +func SettingsDeployKeys(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") ctx.Data["PageIsSettingsKeys"] = true @@ -460,10 +524,10 @@ func DeployKeys(ctx *context.Context) { } ctx.Data["Deploykeys"] = keys - ctx.HTML(200, DEPLOY_KEYS) + ctx.HTML(200, SETTINGS_DEPLOY_KEYS) } -func DeployKeysPost(ctx *context.Context, form auth.AddSSHKeyForm) { +func SettingsDeployKeysPost(ctx *context.Context, form auth.AddSSHKeyForm) { ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") ctx.Data["PageIsSettingsKeys"] = true @@ -475,7 +539,7 @@ func DeployKeysPost(ctx *context.Context, form auth.AddSSHKeyForm) { ctx.Data["Deploykeys"] = keys if ctx.HasError() { - ctx.HTML(200, DEPLOY_KEYS) + ctx.HTML(200, SETTINGS_DEPLOY_KEYS) return } @@ -498,10 +562,10 @@ func DeployKeysPost(ctx *context.Context, form auth.AddSSHKeyForm) { switch { case models.IsErrKeyAlreadyExist(err): ctx.Data["Err_Content"] = true - ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), DEPLOY_KEYS, &form) + ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), SETTINGS_DEPLOY_KEYS, &form) case models.IsErrKeyNameAlreadyUsed(err): ctx.Data["Err_Title"] = true - ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), DEPLOY_KEYS, &form) + ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), SETTINGS_DEPLOY_KEYS, &form) default: ctx.Handle(500, "AddDeployKey", err) } diff --git a/routers/repo/view.go b/routers/repo/view.go index 623b7fee..db0adea2 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -108,8 +108,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { ctx.Data["LatestCommit"] = latestCommit ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit) - // Check permission to add or upload new file. - if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch { + if ctx.Repo.CanEnableEditor() { ctx.Data["CanAddFile"] = true ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled } @@ -142,6 +141,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files") } + canEnableEditor := ctx.Repo.CanEnableEditor() switch { case isTextFile: if blob.Size() >= setting.UI.MaxDisplayFileSize { @@ -186,7 +186,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["LineNums"] = gotemplate.HTML(output.String()) } - if ctx.Repo.CanEnableEditor() { + if canEnableEditor { ctx.Data["CanEditFile"] = true ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file") } else if !ctx.Repo.IsViewBranch { @@ -203,7 +203,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["IsImageFile"] = true } - if ctx.Repo.CanEnableEditor() { + if canEnableEditor { ctx.Data["CanDeleteFile"] = true ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file") } else if !ctx.Repo.IsViewBranch { @@ -216,7 +216,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st func setEditorconfigIfExists(ctx *context.Context) { ec, err := ctx.Repo.GetEditorconfig() if err != nil && !git.IsErrNotExist(err) { - log.Error(4, "Fail to get '.editorconfig' [%d]: %v", ctx.Repo.Repository.ID, err) + log.Trace("setEditorconfigIfExists.GetEditorconfig [%d]: %v", ctx.Repo.Repository.ID, err) return } ctx.Data["Editorconfig"] = ec @@ -228,6 +228,9 @@ func Home(ctx *context.Context) { title += ": " + ctx.Repo.Repository.Description } ctx.Data["Title"] = title + if ctx.Repo.BranchName != ctx.Repo.Repository.DefaultBranch { + ctx.Data["Title"] = title + " @ " + ctx.Repo.BranchName + } ctx.Data["PageIsViewCode"] = true ctx.Data["RequireHighlightJS"] = true |