Commit d98566dc authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into feature/svg-badge-template

* master: (52 commits)
  remove offending empty line
  Namespace EnableDeployKeyService under Projects
  Update version_sorter and use new interface for faster tag sorting
  Avoid to show the original password field when password is automatically seted
  Support pending invitation project members importing projects
  Added concern for a faster "cache_key" method
  Update templates
  "This file is managed by gitlab-ctl. Manual changes will be erased!"
  Remove legacy Ci::StaticModel we do not use anymore
  Revert "Defend against 'Host' header injection"
  Simplify feature introduction note
  Add migration-related tips to the "Merge Request Guidelines" doc
  Enable Style/SpaceAroundEqualsInParameterDefault cop
  Enable Style/EmptyLinesAroundClassBody cop
  Enable Style/EmptyLinesAroundModuleBody cop
  Ensure we are looking for the right dropdown inside the form wrapper
  Set for for labels and ID for dropdowns on create form
  Fix .panel-title style
  Refine selector for form submit button
  Fix spelling. `braches` to `branches`
  ...
parents 89f2be7d 551ffc0a
...@@ -149,19 +149,19 @@ Style/EmptyLinesAroundAccessModifier: ...@@ -149,19 +149,19 @@ Style/EmptyLinesAroundAccessModifier:
# Keeps track of empty lines around block bodies. # Keeps track of empty lines around block bodies.
Style/EmptyLinesAroundBlockBody: Style/EmptyLinesAroundBlockBody:
Enabled: false Enabled: true
# Keeps track of empty lines around class bodies. # Keeps track of empty lines around class bodies.
Style/EmptyLinesAroundClassBody: Style/EmptyLinesAroundClassBody:
Enabled: false Enabled: true
# Keeps track of empty lines around module bodies. # Keeps track of empty lines around module bodies.
Style/EmptyLinesAroundModuleBody: Style/EmptyLinesAroundModuleBody:
Enabled: false Enabled: true
# Keeps track of empty lines around method bodies. # Keeps track of empty lines around method bodies.
Style/EmptyLinesAroundMethodBody: Style/EmptyLinesAroundMethodBody:
Enabled: false Enabled: true
# Avoid the use of END blocks. # Avoid the use of END blocks.
Style/EndBlock: Style/EndBlock:
...@@ -373,6 +373,10 @@ Style/SpaceAfterNot: ...@@ -373,6 +373,10 @@ Style/SpaceAfterNot:
Style/SpaceAfterSemicolon: Style/SpaceAfterSemicolon:
Enabled: true Enabled: true
# Use space around equals in parameter default
Style/SpaceAroundEqualsInParameterDefault:
Enabled: true
# Use a space around keywords if appropriate. # Use a space around keywords if appropriate.
Style/SpaceAroundKeyword: Style/SpaceAroundKeyword:
Enabled: true Enabled: true
......
...@@ -339,13 +339,6 @@ Style/SingleLineBlockParams: ...@@ -339,13 +339,6 @@ Style/SingleLineBlockParams:
Style/SingleLineMethods: Style/SingleLineMethods:
Enabled: false Enabled: false
# Offense count: 14
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Style/SpaceAroundEqualsInParameterDefault:
Enabled: false
# Offense count: 119 # Offense count: 119
# Cop supports --auto-correct. # Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles. # Configuration parameters: EnforcedStyle, SupportedStyles.
......
...@@ -6,6 +6,7 @@ v 8.11.0 (unreleased) ...@@ -6,6 +6,7 @@ v 8.11.0 (unreleased)
- Fix the title of the toggle dropdown button. !5515 (herminiotorres) - Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- Improve diff performance by eliminating redundant checks for text blobs - Improve diff performance by eliminating redundant checks for text blobs
- Convert switch icon into icon font (ClemMakesApps) - Convert switch icon into icon font (ClemMakesApps)
- API: Endpoints for enabling and disabling deploy keys
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell) - Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell) - Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- Ignore URLs starting with // in Markdown links !5677 (winniehell) - Ignore URLs starting with // in Markdown links !5677 (winniehell)
...@@ -24,21 +25,26 @@ v 8.11.0 (unreleased) ...@@ -24,21 +25,26 @@ v 8.11.0 (unreleased)
- Remove unused images (ClemMakesApps) - Remove unused images (ClemMakesApps)
- Limit git rev-list output count to one in forced push check - Limit git rev-list output count to one in forced push check
- Clean up unused routes (Josef Strzibny) - Clean up unused routes (Josef Strzibny)
- Fix issue on empty project to allow developers to only push to protected branches if given permission
- Add green outline to New Branch button. !5447 (winniehell) - Add green outline to New Branch button. !5447 (winniehell)
- Optimize generating of cache keys for issues and notes
- Improve performance of syntax highlighting Markdown code blocks - Improve performance of syntax highlighting Markdown code blocks
- Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects - Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
- Remove delay when hitting "Reply..." button on page with a lot of discussions - Remove delay when hitting "Reply..." button on page with a lot of discussions
- Retrieve rendered HTML from cache in one request - Retrieve rendered HTML from cache in one request
- Fix renaming repository when name contains invalid chararacters under project settings - Fix renaming repository when name contains invalid chararacters under project settings
- Fix devise deprecation warnings. - Fix devise deprecation warnings.
- Update version_sorter and use new interface for faster tag sorting
- Optimize checking if a user has read access to a list of issues !5370 - Optimize checking if a user has read access to a list of issues !5370
- Nokogiri's various parsing methods are now instrumented - Nokogiri's various parsing methods are now instrumented
- Add simple identifier to public SSH keys (muteor) - Add simple identifier to public SSH keys (muteor)
- Admin page now references docs instead of a specific file !5600 (AnAverageHuman)
- Add a way to send an email and create an issue based on private personal token. Find the email address from issues page. !3363 - Add a way to send an email and create an issue based on private personal token. Find the email address from issues page. !3363
- Fix filter input alignment (ClemMakesApps) - Fix filter input alignment (ClemMakesApps)
- Include old revision in merge request update hooks (Ben Boeckel) - Include old revision in merge request update hooks (Ben Boeckel)
- Add build event color in HipChat messages (David Eisner) - Add build event color in HipChat messages (David Eisner)
- Make fork counter always clickable. !5463 (winniehell) - Make fork counter always clickable. !5463 (winniehell)
- Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon)
- Gitlab::Highlight is now instrumented - Gitlab::Highlight is now instrumented
- All created issues, API or WebUI, can be submitted to Akismet for spam check !5333 - All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
- The overhead of instrumented method calls has been reduced - The overhead of instrumented method calls has been reduced
...@@ -73,6 +79,9 @@ v 8.11.0 (unreleased) ...@@ -73,6 +79,9 @@ v 8.11.0 (unreleased)
- Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac) - Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
- Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker - Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
- Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko) - Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
- Adds support for pending invitation project members importing projects
- Update devise initializer to turn on changed password notification emails. !5648 (tombell)
- Avoid to show the original password field when password is automatically set. !5712 (duduribeiro)
v 8.10.5 (unreleased) v 8.10.5 (unreleased)
......
...@@ -336,6 +336,10 @@ request is as follows: ...@@ -336,6 +336,10 @@ request is as follows:
1. If your code creates new files on disk please read the 1. If your code creates new files on disk please read the
[shared files guidelines](doc/development/shared_files.md). [shared files guidelines](doc/development/shared_files.md).
1. When writing commit messages please follow [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [guidelines](http://chris.beams.io/posts/git-commit/). 1. When writing commit messages please follow [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [guidelines](http://chris.beams.io/posts/git-commit/).
1. If your merge request adds one or more migrations, make sure to execute all
migrations on a fresh database before the MR is reviewed. If the review leads
to large changes in the MR, do this again once the review is complete.
1. For more complex migrations, write tests.
The **official merge window** is in the beginning of the month from the 1st to The **official merge window** is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get the 7th day of the month. This is the best time to submit an MR and get
...@@ -461,6 +465,7 @@ merge request: ...@@ -461,6 +465,7 @@ merge request:
- multi-line method chaining style **Option B**: dot `.` on previous line - multi-line method chaining style **Option B**: dot `.` on previous line
- string literal quoting style **Option A**: single quoted by default - string literal quoting style **Option A**: single quoted by default
1. [Rails](https://github.com/bbatsov/rails-style-guide) 1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Newlines styleguide][newlines-styleguide]
1. [Testing](doc/development/testing.md) 1. [Testing](doc/development/testing.md)
1. [JavaScript (ES6)](https://github.com/airbnb/javascript) 1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/master/es5) 1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/master/es5)
...@@ -533,6 +538,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor ...@@ -533,6 +538,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming [rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide" [doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide" [scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design [gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12 [free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/ [`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
......
...@@ -154,7 +154,7 @@ gem 'settingslogic', '~> 2.0.9' ...@@ -154,7 +154,7 @@ gem 'settingslogic', '~> 2.0.9'
# Misc # Misc
gem 'version_sorter', '~> 2.0.0' gem 'version_sorter', '~> 2.1.0'
# Cache # Cache
gem 'redis-rails', '~> 4.0.0' gem 'redis-rails', '~> 4.0.0'
......
...@@ -778,7 +778,7 @@ GEM ...@@ -778,7 +778,7 @@ GEM
uniform_notifier (1.10.0) uniform_notifier (1.10.0)
uuid (2.3.8) uuid (2.3.8)
macaddr (~> 1.0) macaddr (~> 1.0)
version_sorter (2.0.0) version_sorter (2.1.0)
virtus (1.0.5) virtus (1.0.5)
axiom-types (~> 0.1) axiom-types (~> 0.1)
coercible (~> 1.0) coercible (~> 1.0)
...@@ -989,7 +989,7 @@ DEPENDENCIES ...@@ -989,7 +989,7 @@ DEPENDENCIES
unf (~> 0.1.4) unf (~> 0.1.4)
unicorn (~> 4.9.0) unicorn (~> 4.9.0)
unicorn-worker-killer (~> 0.4.2) unicorn-worker-killer (~> 0.4.2)
version_sorter (~> 2.0.0) version_sorter (~> 2.1.0)
virtus (~> 1.0.1) virtus (~> 1.0.1)
vmstat (~> 2.1.1) vmstat (~> 2.1.1)
web-console (~> 2.0) web-console (~> 2.0)
......
...@@ -173,8 +173,8 @@ ...@@ -173,8 +173,8 @@
new Search(); new Search();
break; break;
case 'projects:protected_branches:index': case 'projects:protected_branches:index':
new ProtectedBranchesAccessSelect($(".new_protected_branch"), false, true); new gl.ProtectedBranchCreate();
new ProtectedBranchesAccessSelect($(".protected-branches-list"), true, false); new gl.ProtectedBranchEditList();
break; break;
} }
switch (path.first()) { switch (path.first()) {
......
...@@ -607,7 +607,7 @@ ...@@ -607,7 +607,7 @@
return this.dropdown.before($input); return this.dropdown.before($input);
}; };
GitLabDropdown.prototype.selectRowAtIndex = function(e, index) { GitLabDropdown.prototype.selectRowAtIndex = function(index) {
var $el, selector; var $el, selector;
selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(" + index + ") a"; selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(" + index + ") a";
if (this.dropdown.find(".dropdown-toggle-page").length) { if (this.dropdown.find(".dropdown-toggle-page").length) {
...@@ -615,8 +615,6 @@ ...@@ -615,8 +615,6 @@
} }
$el = $(selector, this.dropdown); $el = $(selector, this.dropdown);
if ($el.length) { if ($el.length) {
e.preventDefault();
e.stopImmediatePropagation();
return $el.first().trigger('click'); return $el.first().trigger('click');
} }
}; };
...@@ -653,7 +651,7 @@ ...@@ -653,7 +651,7 @@
return false; return false;
} }
if (currentKeyCode === 13 && currentIndex !== -1) { if (currentKeyCode === 13 && currentIndex !== -1) {
return _this.selectRowAtIndex(e, $('.is-focused', _this.dropdown).closest('li').index() - 1); return _this.selectRowAtIndex($('.is-focused', _this.dropdown).closest('li').index() - 1);
} }
}; };
})(this)); })(this));
......
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchAccessDropdown = class {
constructor(options) {
const { $dropdown, data, onSelect } = options;
$dropdown.glDropdown({
data: data,
selectable: true,
inputId: $dropdown.data('input-id'),
fieldName: $dropdown.data('field-name'),
toggleLabel(item) {
return item.text;
},
clicked(item, $el, e) {
e.preventDefault();
onSelect();
}
});
}
}
})(window);
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchCreate = class {
constructor() {
this.$wrap = this.$form = $('#new_protected_branch');
this.buildDropdowns();
}
buildDropdowns() {
const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
// Cache callback
this.onSelectCallback = this.onSelect.bind(this);
// Allowed to Merge dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: $allowedToMergeDropdown,
data: gon.merge_access_levels,
onSelect: this.onSelectCallback
});
// Allowed to Push dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: $allowedToPushDropdown,
data: gon.push_access_levels,
onSelect: this.onSelectCallback
});
// Select default
$allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
$allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
// Protected branch dropdown
new ProtectedBranchDropdown({
$dropdown: this.$wrap.find('.js-protected-branch-select'),
onSelect: this.onSelectCallback
});
}
// This will run after clicked callback
onSelect() {
// Enable submit button
const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_level_attributes][access_level]"]');
const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_level_attributes][access_level]"]');
if ($branchInput.val() && $allowedToMergeInput.val() && $allowedToPushInput.val()){
this.$form.find('input[type="submit"]').removeAttr('disabled');
}
}
}
})(window);
class ProtectedBranchDropdown {
constructor(options) {
this.onSelect = options.onSelect;
this.$dropdown = options.$dropdown;
this.$dropdownContainer = this.$dropdown.parent();
this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer');
this.$protectedBranch = this.$dropdownContainer.find('.create-new-protected-branch');
this.buildDropdown();
this.bindEvents();
// Hide footer
this.$dropdownFooter.addClass('hidden');
}
buildDropdown() {
this.$dropdown.glDropdown({
data: this.getProtectedBranches.bind(this),
filterable: true,
remote: false,
search: {
fields: ['title']
},
selectable: true,
toggleLabel(selected) {
return (selected && 'id' in selected) ? selected.title : 'Protected Branch';
},
fieldName: 'protected_branch[name]',
text(protectedBranch) {
return _.escape(protectedBranch.title);
},
id(protectedBranch) {
return _.escape(protectedBranch.id);
},
onFilter: this.toggleCreateNewButton.bind(this),
clicked: (item, $el, e) => {
e.preventDefault();
this.onSelect();
}
});
}
bindEvents() {
this.$protectedBranch.on('click', this.onClickCreateWildcard.bind(this));
}
onClickCreateWildcard() {
this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('glDropdown').selectRowAtIndex(0);
}
getProtectedBranches(term, callback) {
if (this.selectedBranch) {
callback(gon.open_branches.concat(this.selectedBranch));
} else {
callback(gon.open_branches);
}
}
toggleCreateNewButton(branchName) {
this.selectedBranch = {
title: branchName,
id: branchName,
text: branchName
};
if (branchName) {
this.$dropdownContainer
.find('.create-new-protected-branch code')
.text(branchName);
}
this.$dropdownFooter.toggleClass('hidden', !branchName);
}
}
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchEdit = class {
constructor(options) {
this.$wrap = options.$wrap;
this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
this.buildDropdowns();
}
buildDropdowns() {
// Allowed to merge dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToMergeDropdown,
data: gon.merge_access_levels,
onSelect: this.onSelect.bind(this)
});
// Allowed to push dropdown
new gl.ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToPushDropdown,
data: gon.push_access_levels,
onSelect: this.onSelect.bind(this)
});
}
onSelect() {
const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
$.ajax({
type: 'POST',
url: this.$wrap.data('url'),
dataType: 'json',
data: {
_method: 'PATCH',
id: this.$wrap.data('banchId'),
protected_branch: {
merge_access_level_attributes: {
access_level: $allowedToMergeInput.val()
},
push_access_level_attributes: {
access_level: $allowedToPushInput.val()
}
}
},
success: () => {
this.$wrap.effect('highlight');
},
error() {
$.scrollTo(0);
new Flash('Failed to update branch!');
}
});
}
}
})(window);
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchEditList = class {
constructor() {
this.$wrap = $('.protected-branches-list');
// Build edit forms
this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
new gl.ProtectedBranchEdit({
$wrap: $(el)
});
});
}
}
})(window);
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.ProtectedBranchSelect = (function() {
function ProtectedBranchSelect(currentProject) {
this.toggleCreateNewButton = bind(this.toggleCreateNewButton, this);
this.getProtectedBranches = bind(this.getProtectedBranches, this);
$('.dropdown-footer').hide();
this.dropdown = $('.js-protected-branch-select').glDropdown({
data: this.getProtectedBranches,
filterable: true,
remote: false,
search: {
fields: ['title']
},
selectable: true,
toggleLabel: function(selected) {
if (selected && 'id' in selected) {
return selected.title;
} else {
return 'Protected Branch';
}
},
fieldName: 'protected_branch[name]',
text: function(protected_branch) {
return _.escape(protected_branch.title);
},
id: function(protected_branch) {
return _.escape(protected_branch.id);
},
onFilter: this.toggleCreateNewButton,
clicked: function() {
return $('.protect-branch-btn').attr('disabled', false);
}
});
$('.create-new-protected-branch').on('click', (function(_this) {
return function(event) {
_this.dropdown.data('glDropdown').remote.execute();
return _this.dropdown.data('glDropdown').selectRowAtIndex(event, 0);
};
})(this));
}
ProtectedBranchSelect.prototype.getProtectedBranches = function(term, callback) {
if (this.selectedBranch) {
return callback(gon.open_branches.concat(this.selectedBranch));
} else {
return callback(gon.open_branches);
}
};
ProtectedBranchSelect.prototype.toggleCreateNewButton = function(branchName) {
this.selectedBranch = {
title: branchName,
id: branchName,
text: branchName
};
if (branchName === '') {
$('.protected-branch-select-footer-list').addClass('hidden');
return $('.dropdown-footer').hide();
} else {
$('.create-new-protected-branch').text("Create Protected Branch: " + branchName);
$('.protected-branch-select-footer-list').removeClass('hidden');
return $('.dropdown-footer').show();
}
};
return ProtectedBranchSelect;
})();
}).call(this);
class ProtectedBranchesAccessSelect {
constructor(container, saveOnSelect, selectDefault) {
this.container = container;
this.saveOnSelect = saveOnSelect;
this.container.find(".allowed-to-merge").each((i, element) => {
var fieldName = $(element).data('field-name');
var dropdown = $(element).glDropdown({
data: gon.merge_access_levels,
selectable: true,
fieldName: fieldName,
clicked: _.chain(this.onSelect).partial(element).bind(this).value()
});
if (selectDefault) {
dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
}
});
this.container.find(".allowed-to-push").each((i, element) => {
var fieldName = $(element).data('field-name');
var dropdown = $(element).glDropdown({
data: gon.push_access_levels,
selectable: true,
fieldName: fieldName,
clicked: _.chain(this.onSelect).partial(element).bind(this).value()
});
if (selectDefault) {
dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
}
});
}
onSelect(dropdown, selected, element, e) {
$(dropdown).find('.dropdown-toggle-text').text(selected.text);
if (this.saveOnSelect) {
return $.ajax({
type: "POST",
url: $(dropdown).data('url'),
dataType: "json",
data: {
_method: 'PATCH',
id: $(dropdown).data('id'),
protected_branch: {
["" + ($(dropdown).data('type')) + "_attributes"]: {
"access_level": selected.id
}
}
},
success: function() {
var row;
row = $(e.target);
return row.closest('tr').effect('highlight');
},
error: function() {
return new Flash("Failed to update branch!", "alert");
}
});
}
}
}
...@@ -13,14 +13,15 @@ ...@@ -13,14 +13,15 @@
} }
$('.js-user-search').each((function(_this) { $('.js-user-search').each((function(_this) {
return function(i, dropdown) { return function(i, dropdown) {
var options = {};
var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser; var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser;
$dropdown = $(dropdown); $dropdown = $(dropdown);
_this.projectId = $dropdown.data('project-id'); options.projectId = $dropdown.data('project-id');
_this.showCurrentUser = $dropdown.data('current-user'); options.showCurrentUser = $dropdown.data('current-user');
showNullUser = $dropdown.data('null-user'); showNullUser = $dropdown.data('null-user');
showAnyUser = $dropdown.data('any-user'); showAnyUser = $dropdown.data('any-user');
firstUser = $dropdown.data('first-user'); firstUser = $dropdown.data('first-user');
_this.authorId = $dropdown.data('author-id'); options.authorId = $dropdown.data('author-id');
selectedId = $dropdown.data('selected'); selectedId = $dropdown.data('selected');
defaultLabel = $dropdown.data('default-label'); defaultLabel = $dropdown.data('default-label');
issueURL = $dropdown.data('issueUpdate'); issueURL = $dropdown.data('issueUpdate');
...@@ -75,7 +76,7 @@ ...@@ -75,7 +76,7 @@
data: function(term, callback) { data: function(term, callback) {
var isAuthorFilter; var isAuthorFilter;
isAuthorFilter = $('.js-author-search'); isAuthorFilter = $('.js-author-search');
return _this.users(term, function(users) { return _this.users(term, options, function(users) {
var anyUser, index, j, len, name, obj, showDivider; var anyUser, index, j, len, name, obj, showDivider;
if (term.length === 0) { if (term.length === 0) {
showDivider = 0; showDivider = 0;
...@@ -185,11 +186,14 @@ ...@@ -185,11 +186,14 @@
$('.ajax-users-select').each((function(_this) { $('.ajax-users-select').each((function(_this) {
return function(i, select) { return function(i, select) {
var firstUser, showAnyUser, showEmailUser, showNullUser; var firstUser, showAnyUser, showEmailUser, showNullUser;
_this.projectId = $(select).data('project-id'); var options = {};
_this.groupId = $(select).data('group-id'); options.skipLdap = $(select).hasClass('skip_ldap');
_this.showCurrentUser = $(select).data('current-user'); options.projectId = $(select).data('project-id');
_this.authorId = $(select).data('author-id'); options.groupId = $(select).data('group-id');
_this.skipUsers = $(select).data('skip-users'); options.showCurrentUser = $(select).data('current-user');
options.pushCodeToProtectedBranches = $(select).data('push-code-to-protected-branches');
options.authorId = $(select).data('author-id');
options.skipUsers = $(select).data('skip-users');
showNullUser = $(select).data('null-user'); showNullUser = $(select).data('null-user');
showAnyUser = $(select).data('any-user'); showAnyUser = $(select).data('any-user');
showEmailUser = $(select).data('email-user'); showEmailUser = $(select).data('email-user');
...@@ -199,7 +203,7 @@ ...@@ -199,7 +203,7 @@
multiple: $(select).hasClass('multiselect'), multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0, minimumInputLength: 0,
query: function(query) { query: function(query) {
return _this.users(query.term, function(users) { return _this.users(query.term, options, function(users) {
var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref; var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref;
data = { data = {
results: users results: users
...@@ -309,7 +313,7 @@ ...@@ -309,7 +313,7 @@
}); });
}; };
UsersSelect.prototype.users = function(query, callback) { UsersSelect.prototype.users = function(query, options, callback) {
var url; var url;
url = this.buildUrl(this.usersPath); url = this.buildUrl(this.usersPath);
return $.ajax({ return $.ajax({
...@@ -318,11 +322,13 @@ ...@@ -318,11 +322,13 @@
search: query, search: query,
per_page: 20, per_page: 20,
active: true, active: true,
project_id: this.projectId, project_id: options.projectId || null,
group_id: this.groupId, group_id: options.groupId || null,
current_user: this.showCurrentUser, skip_ldap: options.skipLdap || null,
author_id: this.authorId, current_user: options.showCurrentUser || null,
skip_users: this.skipUsers push_code_to_protected_branches: options.pushCodeToProtectedBranches || null,
author_id: options.authorId || null,
skip_users: options.skipUsers || null
}, },
dataType: "json" dataType: "json"
}).done(function(users) { }).done(function(users) {
......
...@@ -72,6 +72,14 @@ ...@@ -72,6 +72,14 @@
&.large { &.large {
width: 200px; width: 200px;
} }
&.wide {
width: 100%;
+ .dropdown-select {
width: 100%;
}
}
} }
.dropdown-menu, .dropdown-menu,
......
...@@ -23,4 +23,9 @@ ...@@ -23,4 +23,9 @@
margin-top: $gl-padding; margin-top: $gl-padding;
} }
} }
.panel-title {
font-size: inherit;
line-height: inherit;
}
} }
...@@ -656,13 +656,9 @@ pre.light-well { ...@@ -656,13 +656,9 @@ pre.light-well {
} }
.new_protected_branch { .new_protected_branch {
.dropdown {
display: inline;
margin-left: 15px;
}
label { label {
min-width: 120px; margin-top: 6px;
font-weight: normal;
} }
} }
...@@ -678,6 +674,21 @@ pre.light-well { ...@@ -678,6 +674,21 @@ pre.light-well {
font-weight: 600; font-weight: 600;
} }
} }
.settings-message {
margin: 0;
border-radius: 0 0 1px 1px;
padding: 20px 0;
border: none;
}
.table-bordered {
border-radius: 1px;
th:not(:last-child), td:not(:last-child) {
border-right: solid 1px transparent;
}
}
} }
.custom-notifications-form { .custom-notifications-form {
......
...@@ -50,6 +50,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController ...@@ -50,6 +50,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController
flash[:notice] = "Password was successfully updated. Please login with it" flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path redirect_to new_user_session_path
else else
@user.reload
render 'edit' render 'edit'
end end
end end
......
...@@ -12,8 +12,7 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -12,8 +12,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def new def new
redirect_to namespace_project_deploy_keys_path(@project.namespace, redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
@project)
end end
def create def create
...@@ -21,19 +20,16 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -21,19 +20,16 @@ class Projects::DeployKeysController < Projects::ApplicationController
set_index_vars set_index_vars
if @key.valid? && @project.deploy_keys << @key if @key.valid? && @project.deploy_keys << @key
redirect_to namespace_project_deploy_keys_path(@project.namespace, redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
@project)
else else
render "index" render "index"
end end
end end
def enable def enable
@key = accessible_keys.find(params[:id]) Projects::EnableDeployKeyService.new(@project, current_user, params).execute
@project.deploy_keys << @key
redirect_to namespace_project_deploy_keys_path(@project.namespace, redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
@project)
end end
def disable def disable
...@@ -45,9 +41,9 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -45,9 +41,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
protected protected
def set_index_vars def set_index_vars
@enabled_keys ||= @project.deploy_keys @enabled_keys ||= @project.deploy_keys
@available_keys ||= accessible_keys - @enabled_keys @available_keys ||= current_user.accessible_deploy_keys - @enabled_keys
@available_project_keys ||= current_user.project_deploy_keys - @enabled_keys @available_project_keys ||= current_user.project_deploy_keys - @enabled_keys
@available_public_keys ||= DeployKey.are_public - @enabled_keys @available_public_keys ||= DeployKey.are_public - @enabled_keys
...@@ -56,10 +52,6 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -56,10 +52,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
@available_public_keys -= @available_project_keys @available_public_keys -= @available_project_keys
end end
def accessible_keys
@accessible_keys ||= current_user.accessible_deploy_keys
end
def deploy_key_params def deploy_key_params
params.require(:deploy_key).permit(:key, :title) params.require(:deploy_key).permit(:key, :title)
end end
......
...@@ -20,9 +20,9 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -20,9 +20,9 @@ class Projects::GitHttpController < Projects::ApplicationController
elsif receive_pack? && receive_pack_allowed? elsif receive_pack? && receive_pack_allowed?
render_ok render_ok
elsif http_blocked? elsif http_blocked?
render_not_allowed render_http_not_allowed
else else
render_not_found render_denied
end end
end end
...@@ -31,7 +31,7 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -31,7 +31,7 @@ class Projects::GitHttpController < Projects::ApplicationController
if upload_pack? && upload_pack_allowed? if upload_pack? && upload_pack_allowed?
render_ok render_ok
else else
render_not_found render_denied
end end
end end
...@@ -40,7 +40,7 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -40,7 +40,7 @@ class Projects::GitHttpController < Projects::ApplicationController
if receive_pack? && receive_pack_allowed? if receive_pack? && receive_pack_allowed?
render_ok render_ok
else else
render_not_found render_denied
end end
end end
...@@ -156,8 +156,17 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -156,8 +156,17 @@ class Projects::GitHttpController < Projects::ApplicationController
render plain: 'Not Found', status: :not_found render plain: 'Not Found', status: :not_found
end end
def render_not_allowed def render_http_not_allowed
render plain: download_access.message, status: :forbidden render plain: access_check.message, status: :forbidden
end
def render_denied
if user && user.can?(:read_project, project)
render plain: 'Access denied', status: :forbidden
else
# Do not leak information about project existence
render_not_found
end
end end
def ci? def ci?
...@@ -168,22 +177,20 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -168,22 +177,20 @@ class Projects::GitHttpController < Projects::ApplicationController
return false unless Gitlab.config.gitlab_shell.upload_pack return false unless Gitlab.config.gitlab_shell.upload_pack
if user if user
download_access.allowed? access_check.allowed?
else else
ci? || project.public? ci? || project.public?
end end
end end
def access def access
return @access if defined?(@access) @access ||= Gitlab::GitAccess.new(user, project, 'http')
@access = Gitlab::GitAccess.new(user, project, 'http')
end end
def download_access def access_check
return @download_access if defined?(@download_access) # Use the magic string '_any' to indicate we do not know what the
# changes are. This is also what gitlab-shell does.
@download_access = access.check('git-upload-pack') @access_check ||= access.check(git_command, '_any')
end end
def http_blocked? def http_blocked?
...@@ -193,8 +200,6 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -193,8 +200,6 @@ class Projects::GitHttpController < Projects::ApplicationController
def receive_pack_allowed? def receive_pack_allowed?
return false unless Gitlab.config.gitlab_shell.receive_pack return false unless Gitlab.config.gitlab_shell.receive_pack
# Skip user authorization on upload request. access_check.allowed?
# It will be done by the pre-receive hook in the repository.
user.present?
end end
end end
...@@ -33,7 +33,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -33,7 +33,7 @@ class RegistrationsController < Devise::RegistrationsController
protected protected
def build_resource(hash=nil) def build_resource(hash = nil)
super super
end end
......
module AvatarsHelper module AvatarsHelper
def author_avatar(commit_or_event, options = {}) def author_avatar(commit_or_event, options = {})
user_avatar(options.merge({ user_avatar(options.merge({
user: commit_or_event.author, user: commit_or_event.author,
...@@ -26,5 +25,4 @@ module AvatarsHelper ...@@ -26,5 +25,4 @@ module AvatarsHelper
mail_to(options[:user_email], avatar) mail_to(options[:user_email], avatar)
end end
end end
end end
module ExploreHelper module ExploreHelper
def filter_projects_path(options={}) def filter_projects_path(options = {})
exist_opts = { exist_opts = {
sort: params[:sort], sort: params[:sort],
scope: params[:scope], scope: params[:scope],
......
...@@ -107,7 +107,7 @@ module SearchHelper ...@@ -107,7 +107,7 @@ module SearchHelper
Sanitize.clean(str) Sanitize.clean(str)
end end
def search_filter_path(options={}) def search_filter_path(options = {})
exist_opts = { exist_opts = {
search: params[:search], search: params[:search],
project_id: params[:project_id], project_id: params[:project_id],
......
module FasterCacheKeys
# A faster version of Rails' "cache_key" method.
#
# Rails' default "cache_key" method uses all kind of complex logic to figure
# out the cache key. In many cases this complexity and overhead may not be
# needed.
#
# This method does not do any timestamp parsing as this process is quite
# expensive and not needed when generating cache keys. This method also relies
# on the table name instead of the cache namespace name as the latter uses
# complex logic to generate the exact same value (as when using the table
# name) in 99% of the cases.
def cache_key
"#{self.class.table_name}/#{id}-#{read_attribute_before_type_cast(:updated_at)}"
end
end
...@@ -7,6 +7,7 @@ class Issue < ActiveRecord::Base ...@@ -7,6 +7,7 @@ class Issue < ActiveRecord::Base
include Sortable include Sortable
include Taskable include Taskable
include Spammable include Spammable
include FasterCacheKeys
DueDateStruct = Struct.new(:title, :name).freeze DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
......
...@@ -36,7 +36,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -36,7 +36,7 @@ class MergeRequestDiff < ActiveRecord::Base
real_size.presence || raw_diffs.size real_size.presence || raw_diffs.size
end end
def raw_diffs(options={}) def raw_diffs(options = {})
if options[:ignore_whitespace_change] if options[:ignore_whitespace_change]
@raw_diffs_no_whitespace ||= begin @raw_diffs_no_whitespace ||= begin
compare = Gitlab::Git::Compare.new( compare = Gitlab::Git::Compare.new(
......
...@@ -5,6 +5,7 @@ class Note < ActiveRecord::Base ...@@ -5,6 +5,7 @@ class Note < ActiveRecord::Base
include Mentionable include Mentionable
include Awardable include Awardable
include Importable include Importable
include FasterCacheKeys
# Attribute containing rendered and redacted Markdown as generated by # Attribute containing rendered and redacted Markdown as generated by
# Banzai::ObjectRenderer. # Banzai::ObjectRenderer.
......
...@@ -865,10 +865,16 @@ class Project < ActiveRecord::Base ...@@ -865,10 +865,16 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system # Check if current branch name is marked as protected in the system
def protected_branch?(branch_name) def protected_branch?(branch_name)
return true if empty_repo? && default_branch_protected?
@protected_branches ||= self.protected_branches.to_a @protected_branches ||= self.protected_branches.to_a
ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present? ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present?
end end
def user_can_push_to_empty_repo?(user)
!default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
end
def forked? def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end end
...@@ -1260,6 +1266,11 @@ class Project < ActiveRecord::Base ...@@ -1260,6 +1266,11 @@ class Project < ActiveRecord::Base
private private
def default_branch_protected?
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
end
def authorized_for_user_by_group?(user, min_access_level) def authorized_for_user_by_group?(user, min_access_level)
member = user.group_members.find_by(source_id: group) member = user.group_members.find_by(source_id: group)
......
...@@ -601,7 +601,7 @@ class Repository ...@@ -601,7 +601,7 @@ class Repository
commit(sha) commit(sha)
end end
def next_branch(name, opts={}) def next_branch(name, opts = {})
branch_ids = self.branch_names.map do |n| branch_ids = self.branch_names.map do |n|
next 1 if n == name next 1 if n == name
result = n.match(/\A#{name}-([0-9]+)\z/) result = n.match(/\A#{name}-([0-9]+)\z/)
...@@ -636,9 +636,7 @@ class Repository ...@@ -636,9 +636,7 @@ class Repository
def tags_sorted_by(value) def tags_sorted_by(value)
case value case value
when 'name' when 'name'
# Would be better to use `sort_by` but `version_sorter` only exposes VersionSorter.rsort(tags) { |tag| tag.name }
# `sort` and `rsort`
VersionSorter.rsort(tag_names).map { |tag_name| find_tag(tag_name) }
when 'updated_desc' when 'updated_desc'
tags_sorted_by_committed_date.reverse tags_sorted_by_committed_date.reverse
when 'updated_asc' when 'updated_asc'
......
module Projects
class EnableDeployKeyService < BaseService
def execute
key = accessible_keys.find_by(id: params[:key_id] || params[:id])
return unless key
project.deploy_keys << key
key
end
private
def accessible_keys
current_user.accessible_deploy_keys
end
end
end
...@@ -366,7 +366,9 @@ ...@@ -366,7 +366,9 @@
.col-sm-10 .col-sm-10
= f.select :repository_storage, repository_storage_options_for_select, {}, class: 'form-control' = f.select :repository_storage, repository_storage_options_for_select, {}, class: 'form-control'
.help-block .help-block
You can manage the repository storage paths in your gitlab.yml configuration file Manage repository storage paths. Learn more in the
= succeed "." do
= link_to "repository storages documentation", help_page_path("administration/repository_storages")
%fieldset %fieldset
%legend Repository Checks %legend Repository Checks
......
%h5.prepend-top-0 .panel.panel-default.protected-branches-list
Already Protected (#{@protected_branches.size}) - if @protected_branches.empty?
- if @protected_branches.empty? .panel-heading
%p.settings-message.text-center %h3.panel-title
No branches are protected, protect a branch with the form above. Protected branch (#{@protected_branches.size})
- else %p.settings-message.text-center
- can_admin_project = can?(current_user, :admin_project, @project) There are currently no protected branches, protect a branch with the form above.
- else
- can_admin_project = can?(current_user, :admin_project, @project)
%table.table.protected-branches-list %table.table.table-bordered
%colgroup %colgroup
%col{ width: "20%" } %col{ width: "25%" }
%col{ width: "30%" } %col{ width: "30%" }
%col{ width: "25%" } %col{ width: "25%" }
%col{ width: "25%" } %col{ width: "20%" }
%thead %thead
%tr %tr
%th Branch %th Protected branch (#{@protected_branches.size})
%th Last commit %th Last commit
%th Allowed to merge %th Allowed to merge
%th Allowed to push %th Allowed to push
- if can_admin_project - if can_admin_project
%th %th
%tbody %tbody
= render partial: @protected_branches, locals: { can_admin_project: can_admin_project } = render partial: @protected_branches, locals: { can_admin_project: can_admin_project }
= paginate @protected_branches, theme: 'gitlab' = paginate @protected_branches, theme: 'gitlab'
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
.panel.panel-default
.panel-heading
%h3.panel-title
Protect a branch
.panel-body
.form-horizontal
.form-group
= f.label :name, class: 'col-md-2 text-right' do
Branch:
.col-md-10
= render partial: "dropdown", locals: { f: f }
.help-block
= link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches')
such as
%code *-stable
or
%code production/*
are supported
.form-group
%label.col-md-2.text-right{ for: 'merge_access_level_attributes' }
Allowed to merge:
.col-md-10
= dropdown_tag('Select',
options: { toggle_class: 'js-allowed-to-merge wide',
data: { field_name: 'protected_branch[merge_access_level_attributes][access_level]', input_id: 'merge_access_level_attributes' }})
.form-group
%label.col-md-2.text-right{ for: 'push_access_level_attributes' }
Allowed to push:
.col-md-10
= dropdown_tag('Select',
options: { toggle_class: 'js-allowed-to-push wide',
data: { field_name: 'protected_branch[push_access_level_attributes][access_level]', input_id: 'push_access_level_attributes' }})
.panel-footer
= f.submit 'Protect', class: 'btn-create btn', disabled: true
= f.hidden_field(:name) = f.hidden_field(:name)
= dropdown_tag("Protected Branch", = dropdown_tag('Select branch or create wildcard',
options: { title: "Pick protected branch", toggle_class: 'js-protected-branch-select js-filter-submit', options: { toggle_class: 'js-protected-branch-select js-filter-submit wide',
filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search protected branches", filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search protected branches",
footer_content: true, footer_content: true,
data: { show_no: true, show_any: true, show_upcoming: true, data: { show_no: true, show_any: true, show_upcoming: true,
selected: params[:protected_branch_name], selected: params[:protected_branch_name],
project_id: @project.try(:id) } }) do project_id: @project.try(:id) } }) do
%ul.dropdown-footer-list.hidden.protected-branch-select-footer-list %ul.dropdown-footer-list
%li %li
= link_to '#', title: "New Protected Branch", class: "create-new-protected-branch" do = link_to '#', title: "New Protected Branch", class: "create-new-protected-branch" do
Create new Create wildcard
%code
:javascript
new ProtectedBranchSelect();
- url = namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) %tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch), branch_id: protected_branch.id } }
%tr
%td %td
= protected_branch.name = protected_branch.name
- if @project.root_ref?(protected_branch.name) - if @project.root_ref?(protected_branch.name)
...@@ -16,14 +15,14 @@ ...@@ -16,14 +15,14 @@
(branch was removed from repository) (branch was removed from repository)
%td %td
= hidden_field_tag "allowed_to_merge_#{protected_branch.id}", protected_branch.merge_access_level.access_level = hidden_field_tag "allowed_to_merge_#{protected_branch.id}", protected_branch.merge_access_level.access_level
= dropdown_tag(protected_branch.merge_access_level.humanize, = dropdown_tag( (protected_branch.merge_access_level.humanize || 'Select') ,
options: { title: "Allowed to merge", toggle_class: 'allowed-to-merge', dropdown_class: 'dropdown-menu-selectable merge', options: { toggle_class: 'js-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container',
data: { field_name: "allowed_to_merge_#{protected_branch.id}", url: url, id: protected_branch.id, type: "merge_access_level" }}) data: { field_name: "allowed_to_merge_#{protected_branch.id}" }})
%td %td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_level.access_level = hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_level.access_level
= dropdown_tag(protected_branch.push_access_level.humanize, = dropdown_tag( (protected_branch.push_access_level.humanize || 'Select') ,
options: { title: "Allowed to push", toggle_class: 'allowed-to-push', dropdown_class: 'dropdown-menu-selectable push', options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container',
data: { field_name: "allowed_to_push_#{protected_branch.id}", url: url, id: protected_branch.id, type: "push_access_level" }}) data: { field_name: "allowed_to_push_#{protected_branch.id}" }})
- if can_admin_project - if can_admin_project
%td %td
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right" = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning'
...@@ -14,41 +14,7 @@ ...@@ -14,41 +14,7 @@
%li prevent <strong>anyone</strong> from deleting the branch %li prevent <strong>anyone</strong> from deleting the branch
%p.append-bottom-0 Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}. %p.append-bottom-0 Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}.
.col-lg-9 .col-lg-9
%h5.prepend-top-0
Protect a branch
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f| = render 'create_protected_branch'
= form_errors(@protected_branch)
.form-group
= f.label :name, "Branch", class: "label-light"
= render partial: "dropdown", locals: { f: f }
%p.help-block
= link_to "Wildcards", help_page_path('user/project/protected_branches', anchor: "wildcard-protected-branches")
such as
%code *-stable
or
%code production/*
are supported.
.form-group
= hidden_field_tag 'protected_branch[merge_access_level_attributes][access_level]'
= label_tag "Allowed to merge: ", nil, class: "label-light append-bottom-0"
= dropdown_tag("<Make a selection>",
options: { title: "Allowed to merge", toggle_class: 'allowed-to-merge',
dropdown_class: 'dropdown-menu-selectable',
data: { field_name: "protected_branch[merge_access_level_attributes][access_level]" }})
.form-group
= hidden_field_tag 'protected_branch[push_access_level_attributes][access_level]'
= label_tag "Allowed to push: ", nil, class: "label-light append-bottom-0"
= dropdown_tag("<Make a selection>",
options: { title: "Allowed to push", toggle_class: 'allowed-to-push',
dropdown_class: 'dropdown-menu-selectable',
data: { field_name: "protected_branch[push_access_level_attributes][access_level]" }})
= f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
%hr
= render "branches_list" = render "branches_list"
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
= f.label :token, "Secret Token", class: 'label-light' = f.label :token, "Secret Token", class: 'label-light'
= f.text_field :token, class: "form-control", placeholder: '' = f.text_field :token, class: "form-control", placeholder: ''
%p.help-block %p.help-block
Use this token to validate received payloads Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.
.form-group .form-group
= f.label :url, "Trigger", class: 'label-light' = f.label :url, "Trigger", class: 'label-light'
%ul.list-unstyled %ul.list-unstyled
......
...@@ -10,6 +10,10 @@ class PostReceive ...@@ -10,6 +10,10 @@ class PostReceive
log("Check gitlab.yml config for correct repositories.storages values. No repository storage path matches \"#{repo_path}\"") log("Check gitlab.yml config for correct repositories.storages values. No repository storage path matches \"#{repo_path}\"")
end end
changes = Base64.decode64(changes) unless changes.include?(' ')
# Use Sidekiq.logger so arguments can be correlated with execution
# time and thread ID's.
Sidekiq.logger.info "changes: #{changes.inspect}" if ENV['SIDEKIQ_LOG_ARGUMENTS']
post_received = Gitlab::GitPostReceive.new(repo_path, identifier, changes) post_received = Gitlab::GitPostReceive.new(repo_path, identifier, changes)
if post_received.project.nil? if post_received.project.nil?
......
...@@ -100,6 +100,9 @@ Devise.setup do |config| ...@@ -100,6 +100,9 @@ Devise.setup do |config|
# secure: true in order to force SSL only cookies. # secure: true in order to force SSL only cookies.
# config.cookie_options = {} # config.cookie_options = {}
# Send a notification email when the user's password is changed
config.send_password_change_notification = true
# ==> Configuration for :validatable # ==> Configuration for :validatable
# Range for password length. Default is 6..128. # Range for password length. Default is 6..128.
config.password_length = 8..128 config.password_length = 8..128
......
# GitLab Container Registry Administration # GitLab Container Registry Administration
> **Note:** > [Introduced][ce-4040] in GitLab 8.8.
This feature was [introduced][ce-4040] in GitLab 8.8.
With the Docker Container Registry integrated into GitLab, every project can With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images. have its own space to store its Docker images.
......
...@@ -44,8 +44,7 @@ as appropriate. ...@@ -44,8 +44,7 @@ as appropriate.
## Custom error messages ## Custom error messages
>**Note:** > [Introduced][5073] in GitLab 8.10.
This feature was [introduced][5073] in GitLab 8.10.
If the commit is declined or an error occurs during the Git hook check, If the commit is declined or an error occurs during the Git hook check,
the STDERR or STDOUT message of the hook will be present in GitLab's UI. the STDERR or STDOUT message of the hook will be present in GitLab's UI.
......
# Housekeeping # Housekeeping
_**Note:** This feature was [introduced][ce-2371] in GitLab 8.4_ > [Introduced][ce-2371] in GitLab 8.4.
--- ---
......
# Project import/export # Project import/export
>**Note:** >**Note:**
- This feature was [introduced][ce-3050] in GitLab 8.9 >
- Importing will not be possible if the import instance version is lower > - [Introduced][ce-3050] in GitLab 8.9.
than that of the exporter. > - Importing will not be possible if the import instance version is lower
- For existing installations, the project import option has to be enabled in > than that of the exporter.
application settings (`/admin/application_settings`) under 'Import sources'. > - For existing installations, the project import option has to be enabled in
- The exports are stored in a temporary [shared directory][tmp] and are deleted > application settings (`/admin/application_settings`) under 'Import sources'.
every 24 hours by a specific worker. > - The exports are stored in a temporary [shared directory][tmp] and are deleted
> every 24 hours by a specific worker.
The GitLab Import/Export version can be checked by using: The GitLab Import/Export version can be checked by using:
......
# Repository checks # Repository checks
>**Note:** > [Introduced][ce-3232] in GitLab 8.7. It is OFF by default because it still
This feature was [introduced][ce-3232] in GitLab 8.7. It is OFF by causes too many false alarms.
default because it still causes too many false alarms.
Git has a built-in mechanism, [git fsck][git-fsck], to verify the Git has a built-in mechanism, [git fsck][git-fsck], to verify the
integrity of all data committed to a repository. GitLab administrators integrity of all data committed to a repository. GitLab administrators
......
...@@ -81,7 +81,7 @@ Read more about [GitLab as an OAuth2 client](oauth2.md). ...@@ -81,7 +81,7 @@ Read more about [GitLab as an OAuth2 client](oauth2.md).
### Personal Access Tokens ### Personal Access Tokens
> **Note:** This feature was [introduced][ce-3749] in GitLab 8.8 > [Introduced][ce-3749] in GitLab 8.8.
You can create as many personal access tokens as you like from your GitLab You can create as many personal access tokens as you like from your GitLab
profile (`/profile/personal_access_tokens`); perhaps one for each application profile (`/profile/personal_access_tokens`); perhaps one for each application
......
# Award Emoji # Award Emoji
>**Note:** This feature was introduced in GitLab 8.9 > [Introduced][ce-4575] in GitLab 8.9.
An awarded emoji tells a thousand words, and can be awarded on issues, merge An awarded emoji tells a thousand words, and can be awarded on issues, merge
requests and notes/comments. Issues, merge requests and notes are further called requests and notes/comments. Issues, merge requests and notes are further called
...@@ -365,3 +365,5 @@ Example Response: ...@@ -365,3 +365,5 @@ Example Response:
"awardable_type": "Note" "awardable_type": "Note"
} }
``` ```
[ce-4575]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4575
...@@ -159,3 +159,51 @@ Example response: ...@@ -159,3 +159,51 @@ Example response:
"id" : 13 "id" : 13
} }
``` ```
## Enable a deploy key
Enables a deploy key for a project so this can be used. Returns the enabled key, with a status code 201 when successful.
```bash
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/deploy_keys/13/enable
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of the project |
| `key_id` | integer | yes | The ID of the deploy key |
Example response:
```json
{
"key" : "ssh-rsa AAAA...",
"id" : 12,
"title" : "My deploy key",
"created_at" : "2015-08-29T12:44:31.550Z"
}
```
## Disable a deploy key
Disable a deploy key for a project. Returns the disabled key.
```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/deploy_keys/13/disable
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of the project |
| `key_id` | integer | yes | The ID of the deploy key |
Example response:
```json
{
"key" : "ssh-rsa AAAA...",
"id" : 12,
"title" : "My deploy key",
"created_at" : "2015-08-29T12:44:31.550Z"
}
```
# Todos # Todos
**Note:** This feature was [introduced][ce-3188] in GitLab 8.10 > [Introduced][ce-3188] in GitLab 8.10.
## Get a list of todos ## Get a list of todos
......
# Triggering Builds through the API # Triggering Builds through the API
_**Note:** This feature was [introduced][ci-229] in GitLab CE 7.14_ > [Introduced][ci-229] in GitLab CE 7.14.
Triggers can be used to force a rebuild of a specific branch, tag or commit, Triggers can be used to force a rebuild of a specific branch, tag or commit,
with an API call. with an API call.
......
# GitLab Container Registry # GitLab Container Registry
> **Note:** > [Introduced][ce-4040] in GitLab 8.8. Docker Registry manifest
This feature was [introduced][ce-4040] in GitLab 8.8. Docker Registry manifest `v1` support was added in GitLab 8.9 to support Docker versions earlier than 1.10.
v1 support was added in GitLab 8.9 to support Docker versions earlier than 1.10.
> **Note:** > **Note:**
This document is about the user guide. To learn how to enable GitLab Container This document is about the user guide. To learn how to enable GitLab Container
......
...@@ -155,15 +155,15 @@ Inside the document: ...@@ -155,15 +155,15 @@ Inside the document:
- Every piece of documentation that comes with a new feature should declare the - Every piece of documentation that comes with a new feature should declare the
GitLab version that feature got introduced. Right below the heading add a GitLab version that feature got introduced. Right below the heading add a
note: `>**Note:** This feature was introduced in GitLab 8.3` note: `> Introduced in GitLab 8.3.`.
- If possible every feature should have a link to the MR that introduced it. - If possible every feature should have a link to the MR that introduced it.
The above note would be then transformed to: The above note would be then transformed to:
`>**Note:** This feature was [introduced][ce-1242] in GitLab 8.3`, where `> [Introduced][ce-1242] in GitLab 8.3.`, where
the [link identifier](#links) is named after the repository (CE) and the MR the [link identifier](#links) is named after the repository (CE) and the MR
number number.
- If the feature is only in GitLab EE, don't forget to mention it, like: - If the feature is only in GitLab EE, don't forget to mention it, like:
`>**Note:** This feature was introduced in GitLab EE 8.3`. Otherwise, leave `> Introduced in GitLab EE 8.3.`. Otherwise, leave
this mention out this mention out.
## References ## References
......
# Newlines styleguide
This style guide recommends best practices for newlines in Ruby code.
## Rule: separate code with newlines only when it makes sense from logic perspectice
```ruby
# bad
def method
issue = Issue.new
issue.save
render json: issue
end
```
```ruby
# good
def method
issue = Issue.new
issue.save
render json: issue
end
```
## Rule: separate code and block with newlines
### Newline before block
```ruby
# bad
def method
issue = Issue.new
if issue.save
render json: issue
end
end
```
```ruby
# good
def method
issue = Issue.new
if issue.save
render json: issue
end
end
```
## Newline after block
```ruby
# bad
def method
if issue.save
issue.send_email
end
render json: issue
end
```
```ruby
# good
def method
if issue.save
issue.send_email
end
render json: issue
end
```
### Exception: no need for newline when code block starts or ends right inside another code block
```ruby
# bad
def method
if issue
if issue.valid?
issue.save
end
end
end
```
```ruby
# good
def method
if issue
if issue.valid?
issue.save
end
end
end
```
# Health Check # Health Check
>**Note:** This feature was [introduced][ce-3888] in GitLab 8.8. > [Introduced][ce-3888] in GitLab 8.8.
GitLab provides a health check endpoint for uptime monitoring on the `health_check` web GitLab provides a health check endpoint for uptime monitoring on the `health_check` web
endpoint. The health check reports on the overall system status based on the status of endpoint. The health check reports on the overall system status based on the status of
......
...@@ -46,10 +46,11 @@ When you are ready press the **Create label** button to create the new label. ...@@ -46,10 +46,11 @@ When you are ready press the **Create label** button to create the new label.
## Prioritize labels ## Prioritize labels
>**Notes:** >**Notes:**
- This feature was introduced in GitLab 8.9. >
- Priority sorting is based on the highest priority label only. This might > - Introduced in GitLab 8.9.
change in the future, follow the discussion in > - Priority sorting is based on the highest priority label only. This might
https://gitlab.com/gitlab-org/gitlab-ce/issues/18554. > change in the future, follow the discussion in
> https://gitlab.com/gitlab-org/gitlab-ce/issues/18554.
Prioritized labels are like any other label, but sorted by priority. This allows Prioritized labels are like any other label, but sorted by priority. This allows
you to sort issues and merge requests by priority. you to sort issues and merge requests by priority.
...@@ -87,8 +88,7 @@ important. ...@@ -87,8 +88,7 @@ important.
## Create a new label right from the issue tracker ## Create a new label right from the issue tracker
>**Note:** > Introduced in GitLab 8.6.
This feature was introduced in GitLab 8.6.
There are times when you are already in the issue tracker searching for a There are times when you are already in the issue tracker searching for a
label, only to realize it doesn't exist. Instead of going to the **Labels** label, only to realize it doesn't exist. Instead of going to the **Labels**
......
...@@ -47,8 +47,7 @@ creation. ...@@ -47,8 +47,7 @@ creation.
## Wildcard protected branches ## Wildcard protected branches
>**Note:** > [Introduced][ce-4665] in GitLab 8.10.
This feature was [introduced][ce-4665] in GitLab 8.10.
You can specify a wildcard protected branch, which will protect all branches You can specify a wildcard protected branch, which will protect all branches
matching the wildcard. For example: matching the wildcard. For example:
......
# Project import/export # Project import/export
>**Notes:** >**Notes:**
- This feature was [introduced][ce-3050] in GitLab 8.9 >
- Importing will not be possible if the import instance version is lower > - [Introduced][ce-3050] in GitLab 8.9.
than that of the exporter. > - Importing will not be possible if the import instance version is lower
- For existing installations, the project import option has to be enabled in > than that of the exporter.
application settings (`/admin/application_settings`) under 'Import sources'. > - For existing installations, the project import option has to be enabled in
Ask your administrator if you don't see the **GitLab export** button when > application settings (`/admin/application_settings`) under 'Import sources'.
creating a new project. > Ask your administrator if you don't see the **GitLab export** button when
- You can find some useful raketasks if you are an administrator in the > creating a new project.
[import_export](../../../administration/raketasks/project_import_export.md) > - You can find some useful raketasks if you are an administrator in the
raketask. > [import_export](../../../administration/raketasks/project_import_export.md)
- The exports are stored in a temporary [shared directory][tmp] and are deleted > raketask.
every 24 hours by a specific worker. > - The exports are stored in a temporary [shared directory][tmp] and are deleted
> every 24 hours by a specific worker.
Existing projects running on any GitLab instance or GitLab.com can be exported Existing projects running on any GitLab instance or GitLab.com can be exported
with all their related data and be moved into a new GitLab instance. with all their related data and be moved into a new GitLab instance.
......
...@@ -26,6 +26,10 @@ GitLab webhooks keep in mind the following things: ...@@ -26,6 +26,10 @@ GitLab webhooks keep in mind the following things:
you are writing a low-level hook this is important to remember. you are writing a low-level hook this is important to remember.
- GitLab ignores the HTTP status code returned by your endpoint. - GitLab ignores the HTTP status code returned by your endpoint.
## Secret Token
If you specify a secret token, it will be sent with the hook request in the `X-Gitlab-Token` HTTP header. Your webhook endpoint can check that to verify that the request is legitimate.
## SSL Verification ## SSL Verification
By default, the SSL certificate of the webhook endpoint is verified based on By default, the SSL certificate of the webhook endpoint is verified based on
......
# Award emoji # Award emoji
>**Note:** >**Note:**
This feature was [introduced][1825] in GitLab 8.2. [Introduced][1825] in GitLab 8.2.
When you're collaborating online, you get fewer opportunities for high-fives When you're collaborating online, you get fewer opportunities for high-fives
and thumbs-ups. Emoji can be awarded to issues and merge requests, making and thumbs-ups. Emoji can be awarded to issues and merge requests, making
...@@ -16,7 +16,7 @@ award emoji. ...@@ -16,7 +16,7 @@ award emoji.
## Sort issues and merge requests on vote count ## Sort issues and merge requests on vote count
>**Note:** >**Note:**
This feature was [introduced][2871] in GitLab 8.5. [Introduced][2871] in GitLab 8.5.
You can quickly sort issues and merge requests by the number of votes they You can quickly sort issues and merge requests by the number of votes they
have received. The sort options can be found in the dropdown menu as "Most have received. The sort options can be found in the dropdown menu as "Most
...@@ -45,7 +45,7 @@ downvotes. ...@@ -45,7 +45,7 @@ downvotes.
## Award emoji for comments ## Award emoji for comments
>**Note:** >**Note:**
This feature was [introduced][4291] in GitLab 8.9. [Introduced][4291] in GitLab 8.9.
Award emoji can also be applied to individual comments when you want to Award emoji can also be applied to individual comments when you want to
celebrate an accomplishment or agree with an opinion. celebrate an accomplishment or agree with an opinion.
......
# Cherry-pick changes # Cherry-pick changes
>**Note:** > [Introduced][ce-3514] in GitLab 8.7.
This feature was [introduced][ce-3514] in GitLab 8.7.
--- ---
......
# File finder # File finder
_**Note:** This feature was [introduced][gh-9889] in GitLab 8.4._ > [Introduced][gh-9889] in GitLab 8.4.
--- ---
......
# Reverting changes # Reverting changes
_**Note:** This feature was [introduced][ce-1990] in GitLab 8.5._ > [Introduced][ce-1990] in GitLab 8.5.
--- ---
......
# GitLab Todos # GitLab Todos
>**Note:** This feature was [introduced][ce-2817] in GitLab 8.5. > [Introduced][ce-2817] in GitLab 8.5.
When you log into GitLab, you normally want to see where you should spend your When you log into GitLab, you normally want to see where you should spend your
time and take some action, or what you need to keep an eye on. All without the time and take some action, or what you need to keep an eye on. All without the
......
...@@ -70,8 +70,7 @@ There are multiple ways to create a branch from GitLab's web interface. ...@@ -70,8 +70,7 @@ There are multiple ways to create a branch from GitLab's web interface.
### Create a new branch from an issue ### Create a new branch from an issue
>**Note:** > [Introduced][ce-2808] in GitLab 8.6.
This feature was [introduced][ce-2808] in GitLab 8.6.
In case your development workflow dictates to have an issue for every merge In case your development workflow dictates to have an issue for every merge
request, you can quickly create a branch right on the issue page which will be request, you can quickly create a branch right on the issue page which will be
......
...@@ -10,6 +10,9 @@ module API ...@@ -10,6 +10,9 @@ module API
present keys, with: Entities::SSHKey present keys, with: Entities::SSHKey
end end
params do
requires :id, type: String, desc: 'The ID of the project'
end
resource :projects do resource :projects do
before { authorize_admin_project } before { authorize_admin_project }
...@@ -17,52 +20,43 @@ module API ...@@ -17,52 +20,43 @@ module API
# Use "projects/:id/deploy_keys/..." instead. # Use "projects/:id/deploy_keys/..." instead.
# #
%w(keys deploy_keys).each do |path| %w(keys deploy_keys).each do |path|
# Get a specific project's deploy keys desc "Get a specific project's deploy keys" do
# success Entities::SSHKey
# Example Request: end
# GET /projects/:id/deploy_keys
get ":id/#{path}" do get ":id/#{path}" do
present user_project.deploy_keys, with: Entities::SSHKey present user_project.deploy_keys, with: Entities::SSHKey
end end
# Get single deploy key owned by currently authenticated user desc 'Get single deploy key' do
# success Entities::SSHKey
# Example Request: end
# GET /projects/:id/deploy_keys/:key_id params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
get ":id/#{path}/:key_id" do get ":id/#{path}/:key_id" do
key = user_project.deploy_keys.find params[:key_id] key = user_project.deploy_keys.find params[:key_id]
present key, with: Entities::SSHKey present key, with: Entities::SSHKey
end end
# Add new deploy key to currently authenticated user # TODO: for 9.0 we should check if params are there with the params block
# If deploy key already exists - it will be joined to project # grape provides, at this point we'd change behaviour so we can't
# but only if original one was accessible by same user # Behaviour now if you don't provide all required params: it renders a
# # validation error or two.
# Parameters: desc 'Add new deploy key to currently authenticated user' do
# key (required) - New deploy Key success Entities::SSHKey
# title (required) - New deploy Key's title end
# Example Request:
# POST /projects/:id/deploy_keys
post ":id/#{path}" do post ":id/#{path}" do
attrs = attributes_for_keys [:title, :key] attrs = attributes_for_keys [:title, :key]
attrs[:key].strip! if attrs[:key]
if attrs[:key].present? key = user_project.deploy_keys.find_by(key: attrs[:key])
attrs[:key].strip! present key, with: Entities::SSHKey if key
# check if key already exist in project
key = user_project.deploy_keys.find_by(key: attrs[:key])
if key
present key, with: Entities::SSHKey
next
end
# Check for available deploy keys in other projects # Check for available deploy keys in other projects
key = current_user.accessible_deploy_keys.find_by(key: attrs[:key]) key = current_user.accessible_deploy_keys.find_by(key: attrs[:key])
if key if key
user_project.deploy_keys << key user_project.deploy_keys << key
present key, with: Entities::SSHKey present key, with: Entities::SSHKey
next
end
end end
key = DeployKey.new attrs key = DeployKey.new attrs
...@@ -74,12 +68,46 @@ module API ...@@ -74,12 +68,46 @@ module API
end end
end end
# Delete existing deploy key of currently authenticated user desc 'Enable a deploy key for a project' do
# detail 'This feature was added in GitLab 8.11'
# Example Request: success Entities::SSHKey
# DELETE /projects/:id/deploy_keys/:key_id end
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
post ":id/#{path}/:key_id/enable" do
key = ::Projects::EnableDeployKeyService.new(user_project,
current_user, declared(params)).execute
if key
present key, with: Entities::SSHKey
else
not_found!('Deploy Key')
end
end
desc 'Disable a deploy key for a project' do
detail 'This feature was added in GitLab 8.11'
success Entities::SSHKey
end
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
delete ":id/#{path}/:key_id/disable" do
key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
key.destroy
present key.deploy_key, with: Entities::SSHKey
end
desc 'Delete existing deploy key of currently authenticated user' do
success Key
end
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
delete ":id/#{path}/:key_id" do delete ":id/#{path}/:key_id" do
key = user_project.deploy_keys.find params[:key_id] key = user_project.deploy_keys.find(params[:key_id])
key.destroy key.destroy
end end
end end
......
module Banzai module Banzai
module Filter module Filter
# Find every image that isn't already wrapped in an `a` tag, and that has # Find every image that isn't already wrapped in an `a` tag, and that has
# a `src` attribute ending with a video extension, add a new video node and # a `src` attribute ending with a video extension, add a new video node and
# a "Download" link in the case the video cannot be played. # a "Download" link in the case the video cannot be played.
class VideoLinkFilter < HTML::Pipeline::Filter class VideoLinkFilter < HTML::Pipeline::Filter
def call def call
doc.xpath(query).each do |el| doc.xpath(query).each do |el|
el.replace(video_node(doc, el)) el.replace(video_node(doc, el))
...@@ -54,6 +52,5 @@ module Banzai ...@@ -54,6 +52,5 @@ module Banzai
container container
end end
end end
end end
end end
# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database.
module Ci
module StaticModel
extend ActiveSupport::Concern
module ClassMethods
# Used by ActiveRecord's polymorphic association to set object_id
def primary_key
'id'
end
# Used by ActiveRecord's polymorphic association to set object_type
def base_class
self
end
end
# Used by AR for fetching attributes
#
# Pass it along if we respond to it.
def [](key)
send(key) if respond_to?(key)
end
def to_param
id
end
def new_record?
false
end
def persisted?
false
end
def destroyed?
false
end
def ==(other)
if other.is_a? ::Ci::StaticModel
id == other.id
else
super
end
end
end
end
...@@ -14,7 +14,7 @@ module Gitlab ...@@ -14,7 +14,7 @@ module Gitlab
@user_access = UserAccess.new(user, project: project) @user_access = UserAccess.new(user, project: project)
end end
def check(cmd, changes = nil) def check(cmd, changes)
return build_status_object(false, "Git access over #{protocol.upcase} is not allowed") unless protocol_allowed? return build_status_object(false, "Git access over #{protocol.upcase} is not allowed") unless protocol_allowed?
unless actor unless actor
......
...@@ -39,7 +39,6 @@ module Gitlab ...@@ -39,7 +39,6 @@ module Gitlab
end end
def deserialize_changes(changes) def deserialize_changes(changes)
changes = Base64.decode64(changes) unless changes.include?(' ')
changes = utf8_encode_changes(changes) changes = utf8_encode_changes(changes)
changes.lines changes.lines
end end
......
module Gitlab module Gitlab
module ImportExport module ImportExport
class AvatarRestorer class AvatarRestorer
def initialize(project:, shared:) def initialize(project:, shared:)
@project = project @project = project
@shared = shared @shared = shared
......
...@@ -18,11 +18,14 @@ module Gitlab ...@@ -18,11 +18,14 @@ module Gitlab
@map ||= @map ||=
begin begin
@exported_members.inject(missing_keys_tracking_hash) do |hash, member| @exported_members.inject(missing_keys_tracking_hash) do |hash, member|
existing_user = User.where(find_project_user_query(member)).first if member['user']
old_user_id = member['user']['id'] old_user_id = member['user']['id']
if existing_user && add_user_as_team_member(existing_user, member) existing_user = User.where(find_project_user_query(member)).first
hash[old_user_id] = existing_user.id hash[old_user_id] = existing_user.id if existing_user && add_team_member(member, existing_user)
else
add_team_member(member)
end end
hash hash
end end
end end
...@@ -45,7 +48,7 @@ module Gitlab ...@@ -45,7 +48,7 @@ module Gitlab
ProjectMember.create!(user: @user, access_level: ProjectMember::MASTER, source_id: @project.id, importing: true) ProjectMember.create!(user: @user, access_level: ProjectMember::MASTER, source_id: @project.id, importing: true)
end end
def add_user_as_team_member(existing_user, member) def add_team_member(member, existing_user = nil)
member['user'] = existing_user member['user'] = existing_user
ProjectMember.create(member_hash(member)).persisted? ProjectMember.create(member_hash(member)).persisted?
......
...@@ -25,7 +25,7 @@ module Gitlab ...@@ -25,7 +25,7 @@ module Gitlab
end end
end end
def initialize(user, adapter=nil) def initialize(user, adapter = nil)
@adapter = adapter @adapter = adapter
@user = user @user = user
@provider = user.ldap_identity.provider @provider = user.ldap_identity.provider
......
...@@ -13,7 +13,7 @@ module Gitlab ...@@ -13,7 +13,7 @@ module Gitlab
Gitlab::LDAP::Config.new(provider) Gitlab::LDAP::Config.new(provider)
end end
def initialize(provider, ldap=nil) def initialize(provider, ldap = nil)
@provider = provider @provider = provider
@ldap = ldap || Net::LDAP.new(config.adapter_options) @ldap = ldap || Net::LDAP.new(config.adapter_options)
end end
......
...@@ -5,7 +5,7 @@ module Gitlab ...@@ -5,7 +5,7 @@ module Gitlab
module Popen module Popen
extend self extend self
def popen(cmd, path=nil) def popen(cmd, path = nil)
unless cmd.is_a?(Array) unless cmd.is_a?(Array)
raise "System commands must be given as an array of strings" raise "System commands must be given as an array of strings"
end end
......
...@@ -37,7 +37,7 @@ module Gitlab ...@@ -37,7 +37,7 @@ module Gitlab
redis_config_hash redis_config_hash
end end
def initialize(rails_env=nil) def initialize(rails_env = nil)
rails_env ||= Rails.env rails_env ||= Rails.env
config_file = File.expand_path('../../../config/resque.yml', __FILE__) config_file = File.expand_path('../../../config/resque.yml', __FILE__)
......
...@@ -30,6 +30,8 @@ module Gitlab ...@@ -30,6 +30,8 @@ module Gitlab
return false unless user return false unless user
if project.protected_branch?(ref) if project.protected_branch?(ref)
return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user)
access_levels = project.protected_branches.matching(ref).map(&:push_access_level) access_levels = project.protected_branches.matching(ref).map(&:push_access_level)
access_levels.any? { |access_level| access_level.check_access(user) } access_levels.any? { |access_level| access_level.check_access(user) }
else else
......
...@@ -49,12 +49,7 @@ server { ...@@ -49,12 +49,7 @@ server {
proxy_http_version 1.1; proxy_http_version 1.1;
## By overwriting Host and clearing X-Forwarded-Host we ensure that proxy_set_header Host $http_host;
## internal HTTP redirects generated by GitLab always send users to
## YOUR_SERVER_FQDN.
proxy_set_header Host YOUR_SERVER_FQDN;
proxy_set_header X-Forwarded-Host "";
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
......
...@@ -93,12 +93,7 @@ server { ...@@ -93,12 +93,7 @@ server {
proxy_http_version 1.1; proxy_http_version 1.1;
## By overwriting Host and clearing X-Forwarded-Host we ensure that proxy_set_header Host $http_host;
## internal HTTP redirects generated by GitLab always send users to
## YOUR_SERVER_FQDN.
proxy_set_header Host YOUR_SERVER_FQDN;
proxy_set_header X-Forwarded-Host "";
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on; proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
......
...@@ -45,7 +45,6 @@ feature 'Admin disables Git access protocol', feature: true do ...@@ -45,7 +45,6 @@ feature 'Admin disables Git access protocol', feature: true do
expect(page).to have_content("git clone #{project.ssh_url_to_repo}") expect(page).to have_content("git clone #{project.ssh_url_to_repo}")
expect(page).to have_selector('#clone-dropdown') expect(page).to have_selector('#clone-dropdown')
end end
end end
def visit_project def visit_project
......
require 'spec_helper'
describe 'Profile > Password', feature: true do
let(:user) { create(:user, password_automatically_set: true) }
before do
login_as(user)
visit edit_profile_password_path
end
def fill_passwords(password, confirmation)
fill_in 'New password', with: password
fill_in 'Password confirmation', with: confirmation
click_button 'Save password'
end
context 'User with password automatically set' do
describe 'User puts different passwords in the field and in the confirmation' do
it 'shows an error message' do
fill_passwords('mypassword', 'mypassword2')
page.within('.alert-danger') do
expect(page).to have_content("Password confirmation doesn't match Password")
end
end
it 'does not contains the current password field after an error' do
fill_passwords('mypassword', 'mypassword2')
expect(page).to have_no_field('user[current_password]')
end
end
describe 'User puts the same passwords in the field and in the confirmation' do
it 'shows a success message' do
fill_passwords('mypassword', 'mypassword')
page.within('.flash-notice') do
expect(page).to have_content('Password was successfully updated. Please login with it')
end
end
end
end
end
...@@ -11,7 +11,7 @@ feature 'Projected Branches', feature: true, js: true do ...@@ -11,7 +11,7 @@ feature 'Projected Branches', feature: true, js: true do
def set_protected_branch_name(branch_name) def set_protected_branch_name(branch_name)
find(".js-protected-branch-select").click find(".js-protected-branch-select").click
find(".dropdown-input-field").set(branch_name) find(".dropdown-input-field").set(branch_name)
click_on "Create Protected Branch: #{branch_name}" click_on("Create wildcard #{branch_name}")
end end
describe "explicit protected branches" do describe "explicit protected branches" do
...@@ -90,7 +90,7 @@ feature 'Projected Branches', feature: true, js: true do ...@@ -90,7 +90,7 @@ feature 'Projected Branches', feature: true, js: true do
visit namespace_project_protected_branches_path(project.namespace, project) visit namespace_project_protected_branches_path(project.namespace, project)
set_protected_branch_name('master') set_protected_branch_name('master')
within('.new_protected_branch') do within('.new_protected_branch') do
find(".allowed-to-push").click find(".js-allowed-to-push").click
within(".dropdown.open .dropdown-menu") { click_on access_type_name } within(".dropdown.open .dropdown-menu") { click_on access_type_name }
end end
click_on "Protect" click_on "Protect"
...@@ -107,8 +107,8 @@ feature 'Projected Branches', feature: true, js: true do ...@@ -107,8 +107,8 @@ feature 'Projected Branches', feature: true, js: true do
expect(ProtectedBranch.count).to eq(1) expect(ProtectedBranch.count).to eq(1)
within(".protected-branches-list") do within(".protected-branches-list") do
find(".allowed-to-push").click find(".js-allowed-to-push").click
within('.dropdown-menu.push') { click_on access_type_name } within('.js-allowed-to-push-container') { click_on access_type_name }
end end
wait_for_ajax wait_for_ajax
...@@ -121,7 +121,7 @@ feature 'Projected Branches', feature: true, js: true do ...@@ -121,7 +121,7 @@ feature 'Projected Branches', feature: true, js: true do
visit namespace_project_protected_branches_path(project.namespace, project) visit namespace_project_protected_branches_path(project.namespace, project)
set_protected_branch_name('master') set_protected_branch_name('master')
within('.new_protected_branch') do within('.new_protected_branch') do
find(".allowed-to-merge").click find(".js-allowed-to-merge").click
within(".dropdown.open .dropdown-menu") { click_on access_type_name } within(".dropdown.open .dropdown-menu") { click_on access_type_name }
end end
click_on "Protect" click_on "Protect"
...@@ -138,8 +138,8 @@ feature 'Projected Branches', feature: true, js: true do ...@@ -138,8 +138,8 @@ feature 'Projected Branches', feature: true, js: true do
expect(ProtectedBranch.count).to eq(1) expect(ProtectedBranch.count).to eq(1)
within(".protected-branches-list") do within(".protected-branches-list") do
find(".allowed-to-merge").click find(".js-allowed-to-merge").click
within('.dropdown-menu.merge') { click_on access_type_name } within('.js-allowed-to-merge-container') { click_on access_type_name }
end end
wait_for_ajax wait_for_ajax
......
...@@ -47,5 +47,4 @@ describe Banzai::Filter::VideoLinkFilter, lib: true do ...@@ -47,5 +47,4 @@ describe Banzai::Filter::VideoLinkFilter, lib: true do
expect(element['src']).to eq '/path/my_image.jpg' expect(element['src']).to eq '/path/my_image.jpg'
end end
end end
end end
...@@ -533,10 +533,6 @@ module Ci ...@@ -533,10 +533,6 @@ module Ci
} }
end end
context 'when also global variables are defined' do
end
context 'when syntax is correct' do context 'when syntax is correct' do
let(:variables) do let(:variables) do
{ VAR1: 'value1', VAR2: 'value2' } { VAR1: 'value1', VAR2: 'value2' }
......
...@@ -25,7 +25,6 @@ describe Gitlab::Git::Hook, lib: true do ...@@ -25,7 +25,6 @@ describe Gitlab::Git::Hook, lib: true do
end end
['pre-receive', 'post-receive', 'update'].each do |hook_name| ['pre-receive', 'post-receive', 'update'].each do |hook_name|
context "when triggering a #{hook_name} hook" do context "when triggering a #{hook_name} hook" do
context "when the hook is successful" do context "when the hook is successful" do
it "returns success with no errors" do it "returns success with no errors" do
......
...@@ -19,11 +19,11 @@ describe Gitlab::GitAccess, lib: true do ...@@ -19,11 +19,11 @@ describe Gitlab::GitAccess, lib: true do
end end
it 'blocks ssh git push' do it 'blocks ssh git push' do
expect(@acc.check('git-receive-pack').allowed?).to be_falsey expect(@acc.check('git-receive-pack', '_any').allowed?).to be_falsey
end end
it 'blocks ssh git pull' do it 'blocks ssh git pull' do
expect(@acc.check('git-upload-pack').allowed?).to be_falsey expect(@acc.check('git-upload-pack', '_any').allowed?).to be_falsey
end end
end end
...@@ -34,17 +34,17 @@ describe Gitlab::GitAccess, lib: true do ...@@ -34,17 +34,17 @@ describe Gitlab::GitAccess, lib: true do
end end
it 'blocks http push' do it 'blocks http push' do
expect(@acc.check('git-receive-pack').allowed?).to be_falsey expect(@acc.check('git-receive-pack', '_any').allowed?).to be_falsey
end end
it 'blocks http git pull' do it 'blocks http git pull' do
expect(@acc.check('git-upload-pack').allowed?).to be_falsey expect(@acc.check('git-upload-pack', '_any').allowed?).to be_falsey
end end
end end
end end
describe 'download_access_check' do describe 'download_access_check' do
subject { access.check('git-upload-pack') } subject { access.check('git-upload-pack', '_any') }
describe 'master permissions' do describe 'master permissions' do
before { project.team << [user, :master] } before { project.team << [user, :master] }
...@@ -288,7 +288,7 @@ describe Gitlab::GitAccess, lib: true do ...@@ -288,7 +288,7 @@ describe Gitlab::GitAccess, lib: true do
let(:actor) { key } let(:actor) { key }
context 'push code' do context 'push code' do
subject { access.check('git-receive-pack') } subject { access.check('git-receive-pack', '_any') }
context 'when project is authorized' do context 'when project is authorized' do
before { key.projects << project } before { key.projects << project }
......
...@@ -26,6 +26,20 @@ describe Gitlab::ImportExport::MembersMapper, services: true do ...@@ -26,6 +26,20 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
"email" => user2.email, "email" => user2.email,
"username" => user2.username "username" => user2.username
} }
},
{
"id" => 3,
"access_level" => 40,
"source_id" => 14,
"source_type" => "Project",
"user_id" => nil,
"notification_level" => 3,
"created_at" => "2016-03-11T10:21:44.822Z",
"updated_at" => "2016-03-11T10:21:44.822Z",
"created_by_id" => 1,
"invite_email" => 'invite@test.com',
"invite_token" => 'token',
"invite_accepted_at" => nil
}] }]
end end
...@@ -47,5 +61,11 @@ describe Gitlab::ImportExport::MembersMapper, services: true do ...@@ -47,5 +61,11 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(members_mapper.missing_author_ids.first).to eq(-1) expect(members_mapper.missing_author_ids.first).to eq(-1)
end end
it 'has invited members with no user' do
members_mapper.map
expect(ProjectMember.find_by_invite_email('invite@test.com')).not_to be_nil
end
end end
end end
...@@ -2,7 +2,6 @@ require 'spec_helper' ...@@ -2,7 +2,6 @@ require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
describe 'restore project tree' do describe 'restore project tree' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:namespace) { create(:namespace, owner: user) } let(:namespace) { create(:namespace, owner: user) }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') }
......
...@@ -9,35 +9,80 @@ describe Gitlab::UserAccess, lib: true do ...@@ -9,35 +9,80 @@ describe Gitlab::UserAccess, lib: true do
describe 'push to none protected branch' do describe 'push to none protected branch' do
it 'returns true if user is a master' do it 'returns true if user is a master' do
project.team << [user, :master] project.team << [user, :master]
expect(access.can_push_to_branch?('random_branch')).to be_truthy expect(access.can_push_to_branch?('random_branch')).to be_truthy
end end
it 'returns true if user is a developer' do it 'returns true if user is a developer' do
project.team << [user, :developer] project.team << [user, :developer]
expect(access.can_push_to_branch?('random_branch')).to be_truthy expect(access.can_push_to_branch?('random_branch')).to be_truthy
end end
it 'returns false if user is a reporter' do it 'returns false if user is a reporter' do
project.team << [user, :reporter] project.team << [user, :reporter]
expect(access.can_push_to_branch?('random_branch')).to be_falsey expect(access.can_push_to_branch?('random_branch')).to be_falsey
end end
end end
describe 'push to empty project' do
let(:empty_project) { create(:project_empty_repo) }
let(:project_access) { Gitlab::UserAccess.new(user, project: empty_project) }
it 'returns true if user is master' do
empty_project.team << [user, :master]
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
it 'returns false if user is developer and project is fully protected' do
empty_project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
expect(project_access.can_push_to_branch?('master')).to be_falsey
end
it 'returns false if user is developer and it is not allowed to push new commits but can merge into branch' do
empty_project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
expect(project_access.can_push_to_branch?('master')).to be_falsey
end
it 'returns true if user is developer and project is unprotected' do
empty_project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
it 'returns true if user is developer and project grants developers permission' do
empty_project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
end
describe 'push to protected branch' do describe 'push to protected branch' do
let(:branch) { create :protected_branch, project: project } let(:branch) { create :protected_branch, project: project }
it 'returns true if user is a master' do it 'returns true if user is a master' do
project.team << [user, :master] project.team << [user, :master]
expect(access.can_push_to_branch?(branch.name)).to be_truthy expect(access.can_push_to_branch?(branch.name)).to be_truthy
end end
it 'returns false if user is a developer' do it 'returns false if user is a developer' do
project.team << [user, :developer] project.team << [user, :developer]
expect(access.can_push_to_branch?(branch.name)).to be_falsey expect(access.can_push_to_branch?(branch.name)).to be_falsey
end end
it 'returns false if user is a reporter' do it 'returns false if user is a reporter' do
project.team << [user, :reporter] project.team << [user, :reporter]
expect(access.can_push_to_branch?(branch.name)).to be_falsey expect(access.can_push_to_branch?(branch.name)).to be_falsey
end end
end end
...@@ -49,16 +94,19 @@ describe Gitlab::UserAccess, lib: true do ...@@ -49,16 +94,19 @@ describe Gitlab::UserAccess, lib: true do
it 'returns true if user is a master' do it 'returns true if user is a master' do
project.team << [user, :master] project.team << [user, :master]
expect(access.can_push_to_branch?(@branch.name)).to be_truthy expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end end
it 'returns true if user is a developer' do it 'returns true if user is a developer' do
project.team << [user, :developer] project.team << [user, :developer]
expect(access.can_push_to_branch?(@branch.name)).to be_truthy expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end end
it 'returns false if user is a reporter' do it 'returns false if user is a reporter' do
project.team << [user, :reporter] project.team << [user, :reporter]
expect(access.can_push_to_branch?(@branch.name)).to be_falsey expect(access.can_push_to_branch?(@branch.name)).to be_falsey
end end
end end
...@@ -70,19 +118,21 @@ describe Gitlab::UserAccess, lib: true do ...@@ -70,19 +118,21 @@ describe Gitlab::UserAccess, lib: true do
it 'returns true if user is a master' do it 'returns true if user is a master' do
project.team << [user, :master] project.team << [user, :master]
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end end
it 'returns true if user is a developer' do it 'returns true if user is a developer' do
project.team << [user, :developer] project.team << [user, :developer]
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end end
it 'returns false if user is a reporter' do it 'returns false if user is a reporter' do
project.team << [user, :reporter] project.team << [user, :reporter]
expect(access.can_merge_to_branch?(@branch.name)).to be_falsey expect(access.can_merge_to_branch?(@branch.name)).to be_falsey
end end
end end
end end
end end
require 'spec_helper'
describe FasterCacheKeys do
describe '#cache_key' do
it 'returns a String' do
# We're using a fixed string here so it's easier to set an expectation for
# the resulting cache key.
time = '2016-08-08 16:39:00+02'
issue = build(:issue, updated_at: time)
issue.extend(described_class)
expect(issue).to receive(:id).and_return(1)
expect(issue.cache_key).to eq("issues/1-#{time}")
end
end
end
...@@ -69,6 +69,7 @@ describe Project, models: true do ...@@ -69,6 +69,7 @@ describe Project, models: true do
it { is_expected.to include_module(Gitlab::ConfigHelper) } it { is_expected.to include_module(Gitlab::ConfigHelper) }
it { is_expected.to include_module(Gitlab::ShellAdapter) } it { is_expected.to include_module(Gitlab::ShellAdapter) }
it { is_expected.to include_module(Gitlab::VisibilityLevel) } it { is_expected.to include_module(Gitlab::VisibilityLevel) }
it { is_expected.to include_module(Gitlab::CurrentSettings) }
it { is_expected.to include_module(Referable) } it { is_expected.to include_module(Referable) }
it { is_expected.to include_module(Sortable) } it { is_expected.to include_module(Sortable) }
end end
...@@ -1070,28 +1071,97 @@ describe Project, models: true do ...@@ -1070,28 +1071,97 @@ describe Project, models: true do
end end
describe '#protected_branch?' do describe '#protected_branch?' do
context 'existing project' do
let(:project) { create(:project) }
it 'returns true when the branch matches a protected branch via direct match' do
project.protected_branches.create!(name: 'foo')
expect(project.protected_branch?('foo')).to eq(true)
end
it 'returns true when the branch matches a protected branch via wildcard match' do
project.protected_branches.create!(name: 'production/*')
expect(project.protected_branch?('production/some-branch')).to eq(true)
end
it 'returns false when the branch does not match a protected branch via direct match' do
expect(project.protected_branch?('foo')).to eq(false)
end
it 'returns false when the branch does not match a protected branch via wildcard match' do
project.protected_branches.create!(name: 'production/*')
expect(project.protected_branch?('staging/some-branch')).to eq(false)
end
end
context "new project" do
let(:project) { create(:empty_project) }
it 'returns false when default_protected_branch is unprotected' do
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
expect(project.protected_branch?('master')).to be false
end
it 'returns false when default_protected_branch lets developers push' do
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
expect(project.protected_branch?('master')).to be false
end
it 'returns true when default_branch_protection does not let developers push but let developer merge branches' do
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
expect(project.protected_branch?('master')).to be true
end
it 'returns true when default_branch_protection is in full protection' do
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
expect(project.protected_branch?('master')).to be true
end
end
end
describe '#user_can_push_to_empty_repo?' do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:user) { create(:user) }
it 'returns true when the branch matches a protected branch via direct match' do it 'returns false when default_branch_protection is in full protection and user is developer' do
project.protected_branches.create!(name: 'foo') project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
expect(project.protected_branch?('foo')).to eq(true) expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
end end
it 'returns true when the branch matches a protected branch via wildcard match' do it 'returns false when default_branch_protection only lets devs merge and user is dev' do
project.protected_branches.create!(name: 'production/*') project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
expect(project.protected_branch?('production/some-branch')).to eq(true) expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
end end
it 'returns false when the branch does not match a protected branch via direct match' do it 'returns true when default_branch_protection lets devs push and user is developer' do
expect(project.protected_branch?('foo')).to eq(false) project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
it 'returns true when default_branch_protection is unprotected and user is developer' do
project.team << [user, :developer]
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end end
it 'returns false when the branch does not match a protected branch via wildcard match' do it 'returns true when user is master' do
project.protected_branches.create!(name: 'production/*') project.team << [user, :master]
expect(project.protected_branch?('staging/some-branch')).to eq(false) expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end end
end end
......
require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:project) { create(:project, creator_id: user.id) }
let!(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
let(:admin) { create(:admin) }
describe 'GET /deploy_keys' do
before { admin }
context 'when unauthenticated' do
it 'should return authentication error' do
get api('/deploy_keys')
expect(response.status).to eq(401)
end
end
context 'when authenticated as non-admin user' do
it 'should return a 403 error' do
get api('/deploy_keys', user)
expect(response.status).to eq(403)
end
end
context 'when authenticated as admin' do
it 'should return all deploy keys' do
get api('/deploy_keys', admin)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['id']).to eq(deploy_keys_project.deploy_key.id)
end
end
end
end
require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id) }
let(:deploy_key) { create(:deploy_key, public: true) }
let!(:deploy_keys_project) do
create(:deploy_keys_project, project: project, deploy_key: deploy_key)
end
describe 'GET /deploy_keys' do
context 'when unauthenticated' do
it 'should return authentication error' do
get api('/deploy_keys')
expect(response.status).to eq(401)
end
end
context 'when authenticated as non-admin user' do
it 'should return a 403 error' do
get api('/deploy_keys', user)
expect(response.status).to eq(403)
end
end
context 'when authenticated as admin' do
it 'should return all deploy keys' do
get api('/deploy_keys', admin)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['id']).to eq(deploy_keys_project.deploy_key.id)
end
end
end
describe 'GET /projects/:id/deploy_keys' do
before { deploy_key }
it 'should return array of ssh keys' do
get api("/projects/#{project.id}/deploy_keys", admin)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['title']).to eq(deploy_key.title)
end
end
describe 'GET /projects/:id/deploy_keys/:key_id' do
it 'should return a single key' do
get api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(deploy_key.title)
end
it 'should return 404 Not Found with invalid ID' do
get api("/projects/#{project.id}/deploy_keys/404", admin)
expect(response).to have_http_status(404)
end
end
describe 'POST /projects/:id/deploy_keys' do
it 'should not create an invalid ssh key' do
post api("/projects/#{project.id}/deploy_keys", admin), { title: 'invalid key' }
expect(response).to have_http_status(400)
expect(json_response['message']['key']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)',
'is invalid'
])
end
it 'should not create a key without title' do
post api("/projects/#{project.id}/deploy_keys", admin), key: 'some key'
expect(response).to have_http_status(400)
expect(json_response['message']['title']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)'
])
end
it 'should create new ssh key' do
key_attrs = attributes_for :another_key
expect do
post api("/projects/#{project.id}/deploy_keys", admin), key_attrs
end.to change{ project.deploy_keys.count }.by(1)
end
end
describe 'DELETE /projects/:id/deploy_keys/:key_id' do
before { deploy_key }
it 'should delete existing key' do
expect do
delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
end.to change{ project.deploy_keys.count }.by(-1)
end
it 'should return 404 Not Found with invalid ID' do
delete api("/projects/#{project.id}/deploy_keys/404", admin)
expect(response).to have_http_status(404)
end
end
describe 'POST /projects/:id/deploy_keys/:key_id/enable' do
let(:project2) { create(:empty_project) }
context 'when the user can admin the project' do
it 'enables the key' do
expect do
post api("/projects/#{project2.id}/deploy_keys/#{deploy_key.id}/enable", admin)
end.to change { project2.deploy_keys.count }.from(0).to(1)
expect(response).to have_http_status(201)
expect(json_response['id']).to eq(deploy_key.id)
end
end
context 'when authenticated as non-admin user' do
it 'should return a 404 error' do
post api("/projects/#{project2.id}/deploy_keys/#{deploy_key.id}/enable", user)
expect(response).to have_http_status(404)
end
end
end
describe 'DELETE /projects/:id/deploy_keys/:key_id/disable' do
context 'when the user can admin the project' do
it 'disables the key' do
expect do
delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}/disable", admin)
end.to change { project.deploy_keys.count }.from(1).to(0)
expect(response).to have_http_status(200)
expect(json_response['id']).to eq(deploy_key.id)
end
end
context 'when authenticated as non-admin user' do
it 'should return a 404 error' do
delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}/disable", user)
expect(response).to have_http_status(404)
end
end
end
end
...@@ -641,79 +641,7 @@ describe API::API, api: true do ...@@ -641,79 +641,7 @@ describe API::API, api: true do
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
describe :deploy_keys do
let(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
let(:deploy_key) { deploy_keys_project.deploy_key }
describe 'GET /projects/:id/deploy_keys' do
before { deploy_key }
it 'should return array of ssh keys' do
get api("/projects/#{project.id}/deploy_keys", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['title']).to eq(deploy_key.title)
end
end
describe 'GET /projects/:id/deploy_keys/:key_id' do
it 'should return a single key' do
get api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", user)
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(deploy_key.title)
end
it 'should return 404 Not Found with invalid ID' do
get api("/projects/#{project.id}/deploy_keys/404", user)
expect(response).to have_http_status(404)
end
end
describe 'POST /projects/:id/deploy_keys' do
it 'should not create an invalid ssh key' do
post api("/projects/#{project.id}/deploy_keys", user), { title: 'invalid key' }
expect(response).to have_http_status(400)
expect(json_response['message']['key']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)',
'is invalid'
])
end
it 'should not create a key without title' do
post api("/projects/#{project.id}/deploy_keys", user), key: 'some key'
expect(response).to have_http_status(400)
expect(json_response['message']['title']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)'
])
end
it 'should create new ssh key' do
key_attrs = attributes_for :key
expect do
post api("/projects/#{project.id}/deploy_keys", user), key_attrs
end.to change{ project.deploy_keys.count }.by(1)
end
end
describe 'DELETE /projects/:id/deploy_keys/:key_id' do
before { deploy_key }
it 'should delete existing key' do
expect do
delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", user)
end.to change{ project.deploy_keys.count }.by(-1)
end
it 'should return 404 Not Found with invalid ID' do
delete api("/projects/#{project.id}/deploy_keys/404", user)
expect(response).to have_http_status(404)
end
end
end
describe :fork_admin do describe :fork_admin do
let(:project_fork_target) { create(:project) } let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, :public) } let(:project_fork_source) { create(:project, :public) }
......
...@@ -75,9 +75,9 @@ describe 'Git HTTP requests', lib: true do ...@@ -75,9 +75,9 @@ describe 'Git HTTP requests', lib: true do
context "with correct credentials" do context "with correct credentials" do
let(:env) { { user: user.username, password: user.password } } let(:env) { { user: user.username, password: user.password } }
it "uploads get status 200 (because Git hooks do the real check)" do it "uploads get status 403" do
upload(path, env) do |response| upload(path, env) do |response|
expect(response).to have_http_status(200) expect(response).to have_http_status(403)
end end
end end
...@@ -86,7 +86,7 @@ describe 'Git HTTP requests', lib: true do ...@@ -86,7 +86,7 @@ describe 'Git HTTP requests', lib: true do
allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false) allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false)
upload(path, env) do |response| upload(path, env) do |response|
expect(response).to have_http_status(404) expect(response).to have_http_status(403)
end end
end end
end end
...@@ -236,9 +236,9 @@ describe 'Git HTTP requests', lib: true do ...@@ -236,9 +236,9 @@ describe 'Git HTTP requests', lib: true do
end end
end end
it "uploads get status 200 (because Git hooks do the real check)" do it "uploads get status 404" do
upload(path, user: user.username, password: user.password) do |response| upload(path, user: user.username, password: user.password) do |response|
expect(response).to have_http_status(200) expect(response).to have_http_status(404)
end end
end end
end end
...@@ -349,19 +349,19 @@ describe 'Git HTTP requests', lib: true do ...@@ -349,19 +349,19 @@ describe 'Git HTTP requests', lib: true do
end end
end end
def clone_get(project, options={}) def clone_get(project, options = {})
get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token)) get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
def clone_post(project, options={}) def clone_post(project, options = {})
post "/#{project}/git-upload-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token)) post "/#{project}/git-upload-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
def push_get(project, options={}) def push_get(project, options = {})
get "/#{project}/info/refs", { service: 'git-receive-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token)) get "/#{project}/info/refs", { service: 'git-receive-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
def push_post(project, options={}) def push_post(project, options = {})
post "/#{project}/git-receive-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token)) post "/#{project}/git-receive-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token))
end end
......
require 'spec_helper' require 'spec_helper'
describe MergeRequests::MergeRequestDiffCacheService do describe MergeRequests::MergeRequestDiffCacheService do
let(:subject) { MergeRequests::MergeRequestDiffCacheService.new } let(:subject) { MergeRequests::MergeRequestDiffCacheService.new }
describe '#execute' do describe '#execute' do
......
require 'spec_helper'
describe Projects::EnableDeployKeyService, services: true do
let(:deploy_key) { create(:deploy_key, public: true) }
let(:project) { create(:empty_project) }
let(:user) { project.creator}
let!(:params) { { key_id: deploy_key.id } }
it 'enables the key' do
expect do
service.execute
end.to change { project.deploy_keys.count }.from(0).to(1)
end
context 'trying to add an unaccessable key' do
let(:another_key) { create(:another_key) }
let!(:params) { { key_id: another_key.id } }
it 'returns nil if the key cannot be added' do
expect(service.execute).to be nil
end
end
def service
Projects::EnableDeployKeyService.new(project, user, params)
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
module Select2Helper module Select2Helper
def select2(value, options={}) def select2(value, options = {})
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash) raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
selector = options.fetch(:from) selector = options.fetch(:from)
......
.vscode .vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment