Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protected branches system #339

Merged
merged 6 commits into from
Feb 21, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ func runServ(c *cli.Context) error {
} else {
gitcmd = exec.Command(verb, repoPath)
}

os.Setenv(models.ProtectedBranchAccessMode, requestedMode.String())
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))

gitcmd.Dir = setting.RepoRootPath
gitcmd.Stdout = os.Stdout
gitcmd.Stdin = os.Stdin
Expand Down
21 changes: 21 additions & 0 deletions cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ package cmd

import (
"os"
"strconv"
"strings"

"github.com/urfave/cli"

"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
Expand Down Expand Up @@ -48,6 +51,24 @@ func runUpdate(c *cli.Context) error {
log.GitLogger.Fatal(2, "First argument 'refName' is empty, shouldn't use")
}

// protected branch check
branchName := strings.TrimPrefix(args[0], git.BranchPrefix)
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
log.GitLogger.Trace("pushing to", repoID, branchName)
accessMode := models.ParseAccessMode(os.Getenv(models.ProtectedBranchAccessMode))
// skip admin or owner AccessMode
if accessMode == models.AccessModeWrite {
protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
if err != nil {
log.GitLogger.Fatal(2, "retrieve protected branches information failed")
}

if protectBranch != nil {
log.GitLogger.Fatal(2, "protected branches can not be pushed to")
fail("protected branches can not be pushed to", "protected branches can not be pushed to")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is .Fatal not really fatal that you add a fail() call afterwards ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed a problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

}
}

task := models.UpdateTask{
UUID: os.Getenv("GITEA_UUID"),
RefName: args[0],
Expand Down
5 changes: 5 additions & 0 deletions cmd/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@ func runWeb(ctx *cli.Context) error {
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
m.Post("/delete", repo.DeleteCollaboration)
})
m.Group("/branches", func() {
m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost)
m.Post("/can_push", repo.ChangeProtectedBranch)
m.Post("/delete", repo.DeleteProtectedBranch)
})

m.Group("/hooks", func() {
m.Get("", repo.Webhooks)
Expand Down
153 changes: 153 additions & 0 deletions models/branches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
"fmt"
"strings"
"time"
)

// Protected metadata
const (
// Protected User ID
ProtectedBranchUserID = "GITEA_USER_ID"
// Protected Repo ID
ProtectedBranchRepoID = "GITEA_REPO_ID"
// Protected access mode
ProtectedBranchAccessMode = "GITEA_ACCESS_MODE"
)

// ProtectedBranch struct
type ProtectedBranch struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s)"`
BranchName string `xorm:"UNIQUE(s)"`
CanPush bool
Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
}

// BeforeInsert before protected branch insert create and update time
func (protectBranch *ProtectedBranch) BeforeInsert() {
protectBranch.CreatedUnix = time.Now().Unix()
protectBranch.UpdatedUnix = protectBranch.CreatedUnix
}

// BeforeUpdate before protected branch update time
func (protectBranch *ProtectedBranch) BeforeUpdate() {
protectBranch.UpdatedUnix = time.Now().Unix()
}

// GetProtectedBranchByRepoID getting protected branch by repo ID
func GetProtectedBranchByRepoID(RepoID int64) ([]*ProtectedBranch, error) {
protectedBranches := make([]*ProtectedBranch, 0)
return protectedBranches, x.Where("repo_id = ?", RepoID).Desc("updated_unix").Find(&protectedBranches)
}

// GetProtectedBranchBy getting protected branch by ID/Name
func GetProtectedBranchBy(repoID int64, BranchName string) (*ProtectedBranch, error) {
rel := &ProtectedBranch{RepoID: repoID, BranchName: strings.ToLower(BranchName)}
_, err := x.Get(rel)
return rel, err
}

// GetProtectedBranches get all protected btanches
func (repo *Repository) GetProtectedBranches() ([]*ProtectedBranch, error) {
protectedBranches := make([]*ProtectedBranch, 0)
return protectedBranches, x.Find(&protectedBranches, &ProtectedBranch{RepoID: repo.ID})
}

// AddProtectedBranch add protection to branch
func (repo *Repository) AddProtectedBranch(branchName string, canPush bool) error {
protectedBranch := &ProtectedBranch{
RepoID: repo.ID,
BranchName: branchName,
}

has, err := x.Get(protectedBranch)
if err != nil {
return err
} else if has {
return nil
}

sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
protectedBranch.CanPush = canPush
if _, err = sess.InsertOne(protectedBranch); err != nil {
return err
}

return sess.Commit()
}

// ChangeProtectedBranch access mode sets new access mode for the ProtectedBranch.
func (repo *Repository) ChangeProtectedBranch(id int64, canPush bool) error {
ProtectedBranch := &ProtectedBranch{
RepoID: repo.ID,
ID: id,
}
has, err := x.Get(ProtectedBranch)
if err != nil {
return fmt.Errorf("get ProtectedBranch: %v", err)
} else if !has {
return nil
}

if ProtectedBranch.CanPush == canPush {
return nil
}
ProtectedBranch.CanPush = canPush

sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}

if _, err = sess.Id(ProtectedBranch.ID).AllCols().Update(ProtectedBranch); err != nil {
return fmt.Errorf("update ProtectedBranch: %v", err)
}

return sess.Commit()
}

// DeleteProtectedBranch removes ProtectedBranch relation between the user and repository.
func (repo *Repository) DeleteProtectedBranch(id int64) (err error) {
ProtectedBranch := &ProtectedBranch{
RepoID: repo.ID,
ID: id,
}

sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}

if has, err := sess.Delete(ProtectedBranch); err != nil || has == 0 {
return err
}

return sess.Commit()
}

// newProtectedBranch insert one queue
func newProtectedBranch(protectedBranch *ProtectedBranch) error {
_, err := x.InsertOne(protectedBranch)
return err
}

// UpdateProtectedBranch update queue
func UpdateProtectedBranch(protectedBranch *ProtectedBranch) error {
_, err := x.Update(protectedBranch)
return err
}
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ var migrations = []Migration{
NewMigration("create user column allow create organization", createAllowCreateOrganizationColumn),
// V16 -> v17
NewMigration("create repo unit table and add units for all repos", addUnitsToTables),
// v17 -> v18
NewMigration("set protect branches updated with created", setProtectedBranchUpdatedWithCreated),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an entirely new struct, that doesn't need any migration because of the anyway executed sync function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Sync on a migration is better safe. I have found the original method's bug, see #871. When you upgrade from an old version which haven't a column external_tracker_url, then a new version add this column by Sync not migration. but another new version will use this column, then the bug occupied.

}

// Migrate database to current version
Expand Down
29 changes: 29 additions & 0 deletions models/migrations/v17.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2016 Gitea. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"fmt"
"time"

"github.com/go-xorm/xorm"
)

func setProtectedBranchUpdatedWithCreated(x *xorm.Engine) (err error) {
type ProtectedBranch struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s)"`
BranchName string `xorm:"UNIQUE(s)"`
CanPush bool
Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
}
if err = x.Sync2(new(ProtectedBranch)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
return nil
}
6 changes: 6 additions & 0 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,12 @@ func (repo *Repository) HasAccess(u *User) bool {
return has
}

// UpdateDefaultBranch updates the default branch
func (repo *Repository) UpdateDefaultBranch() error {
_, err := x.ID(repo.ID).Cols("default_branch").Update(repo)
return err
}

// IsOwnedBy returns true when user owns this repository
func (repo *Repository) IsOwnedBy(userID int64) bool {
return repo.OwnerID == userID
Expand Down
1 change: 0 additions & 1 deletion modules/auth/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ type RepoSettingForm struct {
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Description string `binding:"MaxSize(255)"`
Website string `binding:"Url;MaxSize(255)"`
Branch string
Interval int
MirrorAddress string
Private bool
Expand Down
5 changes: 3 additions & 2 deletions options/locale/TRANSLATORS
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Cysioland
Damaris Padieu <damizx AT hotmail DOT fr>
Daniel Speichert <daniel AT speichert DOT pl>
David Yzaguirre <dvdyzag AT gmail DOT com>
Denis Denisov <denji0k AT gmail DOT com>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be added to all our repos ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed this.

Dmitriy Nogay <me AT catwhocode DOT ga>
Enrico Testori hypertesto AT gmail DOT com
Ezequiel Gonzalez Rial <gonrial AT gmail DOT com>
Expand Down Expand Up @@ -49,16 +50,16 @@ Muhammad Fawwaz Orabi <mfawwaz93 AT gmail DOT com>
Nakao Takamasa <at.mattenn AT gmail DOT com>
Natan Albuquerque <natanalbuquerque5 AT gmail DOT com>
Odilon Junior <odilon DOT junior93 AT gmail DOT com>
Pablo Saavedra <psaavedra AT igalia DOT com>
Richard Bukovansky <richard DOT bukovansky @ gmail DOT com>
Robert Nuske <robert DOT nuske AT web DOT de>
Robin Hübner <profan AT prfn DOT se>
SeongJae Park <sj38 DOT park AT gmail DOT com>
Thiago Avelino <thiago AT avelino DOT xxx>
Thomas Fanninger <gogs DOT thomas AT fanninger DOT at>
Tilmann Bach <tilmann AT outlook DOT com>
Toni Villena Jiménez <tonivj5 AT gmail DOT com>
Vladimir Jigulin mogaika AT yandex DOT ru
Vladimir Vissoultchev <wqweto AT gmail DOT com>
YJSoft <yjsoft AT yjsoft DOT pe DOT kr>
Łukasz Jan Niemier <lukasz AT niemier DOT pl>
Pablo Saavedra <psaavedra AT igalia DOT com>
Thiago Avelino <thiago AT avelino DOT xxx>
12 changes: 12 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,18 @@ settings.add_key_success = New deploy key '%s' has been added successfully!
settings.deploy_key_deletion = Delete Deploy Key
settings.deploy_key_deletion_desc = Deleting this deploy key will remove all related accesses for this repository. Do you want to continue?
settings.deploy_key_deletion_success = Deploy key has been deleted successfully!
settings.branches=Branches
settings.protected_branch=Branch Protection
settings.protected_branch_can_push=Allow push?
settings.protected_branch_can_push_yes=You can push
settings.protected_branch_can_push_no=You can not push
settings.add_protected_branch=Enable protection
settings.delete_protected_branch=Disable protection
settings.add_protected_branch_success=%s Locked successfully
settings.add_protected_branch_failed= %s Locked failed
settings.remove_protected_branch_success=%s Unlocked successfully
settings.protected_branch_deletion=To delete a protected branch
settings.protected_branch_deletion_desc=Anyone with write permissions will be able to push directly to this branch. Are you sure?

diff.browse_source = Browse Source
diff.parent = parent
Expand Down
12 changes: 12 additions & 0 deletions options/locale/locale_ru-RU.ini
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,18 @@ settings.add_key_success=Новый ключ развертывания '%s' у
settings.deploy_key_deletion=Удалить ключ развертывания
settings.deploy_key_deletion_desc=Удаление ключа развертывания приведет к удалению всех связанных прав доступа к репозиторию. Вы хотите продолжить?
settings.deploy_key_deletion_success=Ключ развертывания успешно удален!
settings.branches=Ветки
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we switched to crowdin this have to be done within crowdin, otherwise we get into issues while we try to sync it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed this.

settings.protected_branch=Ограничение Веток
settings.protected_branch_can_push=Разрешить пуш?
settings.protected_branch_can_push_yes=Можно пушить
settings.protected_branch_can_push_no=Нельзя пушить
settings.add_protected_branch=Включить защиту
settings.delete_protected_branch=Отключить защиту
settings.add_protected_branch_success=%s Заблокировано успешно
settings.add_protected_branch_failed= %s Заблокировано неуспешно
settings.remove_protected_branch_success=%s Разблокирован успешно
settings.protected_branch_deletion=Удалить защищенную ветку
settings.protected_branch_deletion_desc=Пользователь с разрешениями на запись может напрямую пушить в этой ветке. Вы уверены?

diff.browse_source=Просмотр исходного кода
diff.parent=Родитель
Expand Down
37 changes: 37 additions & 0 deletions public/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,42 @@ function initRepository() {
}
}

function initProtectedBranch() {
$('#protectedBranch').change(function () {
var $this = $(this);
$.post($this.data('url'), {
"_csrf": csrf,
"canPush": true,
"branchName": $this.val(),
},
function (data) {
if (data.redirect) {
window.location.href = data.redirect;
} else {
location.reload();
}
}
);
});

$('.rm').click(function () {
var $this = $(this);
$.post($this.data('url'), {
"_csrf": csrf,
"canPush": false,
"branchName": $this.val(),
},
function (data) {
if (data.redirect) {
window.location.href = data.redirect;
} else {
location.reload();
}
}
);
});
}

function initRepositoryCollaboration() {
console.log('initRepositoryCollaboration');

Expand Down Expand Up @@ -1400,6 +1436,7 @@ $(document).ready(function () {
initEditForm();
initEditor();
initOrganization();
initProtectedBranch();
initWebhook();
initAdmin();
initCodeView();
Expand Down
Loading