Commit 869182ca authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent a82d0c74
......@@ -19,9 +19,13 @@ class Projects::ProtectedBranchesController < Projects::ProtectedRefsController
[:merge_access_levels, :push_access_levels]
end
def protected_ref_params
params.require(:protected_branch).permit(:name,
def protected_ref_params(*attrs)
attrs = ([:name,
merge_access_levels_attributes: access_level_attributes,
push_access_levels_attributes: access_level_attributes)
push_access_levels_attributes: access_level_attributes] + attrs).uniq
params.require(:protected_branch).permit(attrs)
end
end
Projects::ProtectedBranchesController.prepend_if_ee('EE::Projects::ProtectedBranchesController')
......@@ -376,6 +376,7 @@ class ProjectsController < Projects::ApplicationController
:tag_list,
:visibility_level,
:template_name,
:template_project_id,
:merge_method,
:initialize_with_readme,
......
# frozen_string_literal: true
module Checksummable
extend ActiveSupport::Concern
class_methods do
def hexdigest(path)
Digest::SHA256.file(path).hexdigest
end
end
end
......@@ -2,6 +2,7 @@
class LfsObject < ApplicationRecord
include AfterCommitQueue
include Checksummable
include EachBatch
include ObjectStorage::BackgroundMove
......@@ -46,7 +47,7 @@ class LfsObject < ApplicationRecord
# rubocop: enable DestroyAll
def self.calculate_oid(path)
Digest::SHA256.file(path).hexdigest
self.hexdigest(path)
end
end
......
......@@ -40,6 +40,11 @@ class ProtectedBranch < ApplicationRecord
def self.protected_refs(project)
project.protected_branches.select(:name)
end
def self.branch_requires_code_owner_approval?(project, branch_name)
# NOOP
#
end
end
ProtectedBranch.prepend_if_ee('EE::ProtectedBranch')
# frozen_string_literal: true
class Upload < ApplicationRecord
include Checksummable
# Upper limit for foreground checksum processing
CHECKSUM_THRESHOLD = 100.megabytes
......@@ -21,10 +22,6 @@ class Upload < ApplicationRecord
# hooks are not executed and the file will not be deleted
after_destroy :delete_file!, if: -> { uploader_class <= FileUploader }
def self.hexdigest(path)
Digest::SHA256.file(path).hexdigest
end
class << self
##
# FastDestroyAll concerns
......@@ -55,7 +52,7 @@ class Upload < ApplicationRecord
self.checksum = nil
return unless needs_checksum?
self.checksum = Digest::SHA256.file(absolute_path).hexdigest
self.checksum = self.class.hexdigest(absolute_path)
end
# Initialize the associated Uploader class with current model
......
......@@ -4,8 +4,11 @@ module Projects
class CreateFromTemplateService < BaseService
include Gitlab::Utils::StrongMemoize
attr_reader :template_name
def initialize(user, params)
@current_user, @params = user, params.to_h.dup
@template_name = @params.delete(:template_name).presence
end
def execute
......@@ -21,12 +24,6 @@ module Projects
file&.close
end
def template_name
strong_memoize(:template_name) do
params.delete(:template_name).presence
end
end
private
def validate_template!
......
......@@ -13,7 +13,7 @@ module Projects
end
def execute
if @params[:template_name].present?
if create_from_template?
return ::Projects::CreateFromTemplateService.new(current_user, params).execute
end
......@@ -184,6 +184,10 @@ module Projects
private
def create_from_template?
@params[:template_name].present? || @params[:template_project_id].present?
end
def import_schedule
if @project.errors.empty?
@project.import_state.schedule if @project.import? && !@project.bare_repository_import?
......
.protected-branches-list.js-protected-branches-list.qa-protected-branches-list
- if @protected_branches.empty?
.card-header.bg-white
Protected branch (#{@protected_branches_count})
= s_("ProtectedBranch|Protected branch (%{protected_branches_count})") % { protected_branches_count: @protected_branches_count }
%p.settings-message.text-center
There are currently no protected branches, protect a branch with the form above.
= s_("ProtectedBranch|There are currently no protected branches, protect a branch with the form above.")
- else
%table.table.table-bordered
%colgroup
......@@ -15,10 +15,17 @@
%col
%thead
%tr
%th Protected branch (#{@protected_branches_count})
%th Last commit
%th Allowed to merge
%th Allowed to push
%th
= s_("ProtectedBranch|Protected branch (%{protected_branches_count})") % { protected_branches_count: @protected_branches_count }
%th
= s_("ProtectedBranch|Last commit")
%th
= s_("ProtectedBranch|Allowed to merge")
%th
= s_("ProtectedBranch|Allowed to push")
= render_if_exists 'projects/protected_branches/ee/code_owner_approval_table_head'
- if can_admin_project
%th
%tbody
......
......@@ -2,7 +2,7 @@
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
.card
.card-header
Protect a branch
= s_("ProtectedBranch|Protect a branch")
.card-body
= form_errors(@protected_branch)
.form-group.row
......@@ -11,22 +11,19 @@
.col-md-10
= render partial: "projects/protected_branches/shared/dropdown", locals: { f: f }
.form-text.text-muted
= link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches')
such as
%code *-stable
or
%code production/*
are supported
- wildcards_url = help_page_url('user/project/protected_branches', anchor: 'wildcard-protected-branches')
- wildcards_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: wildcards_url }
= (s_("ProtectedBranch|%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported") % { wildcards_link_start: wildcards_link_start, wildcards_link_end: '</a>', code_tag_start: '<code>', code_tag_end: '</code>' }).html_safe
.form-group.row
%label.col-md-2.text-right{ for: 'merge_access_levels_attributes' }
Allowed to merge:
= s_("ProtectedBranch|Allowed to merge:")
.col-md-10
= yield :merge_access_levels
.form-group.row
%label.col-md-2.text-right{ for: 'push_access_levels_attributes' }
Allowed to push:
= s_("ProtectedBranch|Allowed to push:")
.col-md-10
= yield :push_access_levels
= render_if_exists 'projects/protected_branches/ee/code_owner_approval_form'
.card-footer
= f.submit 'Protect', class: 'btn-success btn', disabled: true, data: { qa_selector: 'protect_button' }
= f.submit s_('ProtectedBranch|Protect'), class: 'btn-success btn', disabled: true, data: { qa_selector: 'protect_button' }
......@@ -19,6 +19,8 @@
= yield
= render_if_exists 'projects/protected_branches/ee/code_owner_approval_table', protected_branch: protected_branch
- if can_admin_project
%td
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning"
---
title: Refactor checksum code in uploads
merge_request: 18065
author: briankabiro
type: other
---
title: Add backend support for selecting custom templates by ID
merge_request: 18178
author:
type: fixed
# frozen_string_literal: true
class MigrateCodeOwnerApprovalStatusToProtectedBranchesInBatches < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
BATCH_SIZE = 200
class Project < ActiveRecord::Base
include EachBatch
self.table_name = 'projects'
self.inheritance_column = :_type_disabled
has_many :protected_branches
end
class ProtectedBranch < ActiveRecord::Base
include EachBatch
self.table_name = 'protected_branches'
self.inheritance_column = :_type_disabled
belongs_to :project
end
def up
add_concurrent_index :projects, :id, name: "temp_active_projects_with_prot_branches", where: 'archived = false and pending_delete = false and merge_requests_require_code_owner_approval = true'
ProtectedBranch
.joins(:project)
.where(projects: { archived: false, pending_delete: false, merge_requests_require_code_owner_approval: true })
.each_batch(of: BATCH_SIZE) do |batch|
batch.update_all(code_owner_approval_required: true)
end
remove_concurrent_index_by_name(:projects, "temp_active_projects_with_prot_branches")
end
def down
# noop
#
end
end
......@@ -281,7 +281,7 @@ sync to run once every 2 hours at the top of the hour.
> Introduced in GitLab Enterprise Edition Starter 8.9.
Using the `external_groups` setting will allow you to mark all users belonging
to these groups as [external users](../../user/permissions.md#external-users-permissions).
to these groups as [external users](../../user/permissions.md#external-users-core-only).
Group membership is checked periodically through the `LdapGroupSync` background
task.
......
......@@ -296,3 +296,21 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" 'https://git
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the branch |
## Require code owner approvals for a single branch
Update the "code owner approval required" option for the given protected branch protected branch.
```
PATCH /projects/:id/protected_branches/:name
```
```bash
curl --request PATCH --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_branches/feature-branch'
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the branch |
| `code_owner_approval_required` | boolean | no | **(PREMIUM)** Prevent pushes to this branch if it matches an item in the [`CODEOWNERS` file](../user/project/code_owners.md). (defaults: false)|
......@@ -241,58 +241,83 @@ nested groups if you have membership in one of its parents.
To learn more, read through the documentation on
[subgroups memberships](group/subgroups/index.md#membership).
## Free Guest users **(ULTIMATE)**
When a user is given `Guest` permissions on a project and/or group, and holds no
higher permission level on any other project or group on the instance, the user
is considered a guest user by GitLab and will not consume a license seat.
There is no other specific "guest" designation for newly created users.
If the user is assigned a higher role on any projects or groups, the user will
take a license seat. If a user creates a project, the user becomes a `Maintainer`
on the project, resulting in the use of a license seat.
To prevent a guest user from creating projects, you can edit the user profile to mark the user as
[External](#external-users-permissions).
## External users permissions
## External users **(CORE ONLY)**
In cases where it is desired that a user has access only to some internal or
private projects, there is the option of creating **External Users**. This
feature may be useful when for example a contractor is working on a given
project and should only have access to that project.
External users can only access projects to which they are explicitly granted
access, thus hiding all other internal or private ones from them. Access can be
granted by adding the user as member to the project or group.
External users:
- Cannot create groups or projects.
- Can only access projects to which they are explicitly granted access,
thus hiding all other internal or private ones from them (like being
logged out).
Access can be granted by adding the user as member to the project or group.
They will, like usual users, receive a role in the project or group with all
the abilities that are mentioned in the table above. They cannot however create
groups or projects, and they have the same access as logged out users in all
other cases.
the abilities that are mentioned in the [permissions table above](#project-members-permissions).
For example, if an external user is added as Guest, and your project is
private, they will not have access to the code; you would need to grant the external
user access at the Reporter level or above if you want them to have access to the code. You should
always take into account the
[project's visibility and permissions settings](project/settings/index.md#sharing-and-permissions)
as well as the permission level of the user.
An administrator can flag a user as external [through the API](../api/users.md)
or by checking the checkbox on the admin panel. As an administrator, navigate
to **Admin > Users** to create a new user or edit an existing one. There, you
will find the option to flag the user as external.
NOTE: **Note:**
External users still count towards a license seat.
An administrator can flag a user as external by either of the following methods:
By default new users are not set as external users. This behavior can be changed
by an administrator under **Admin > Application Settings**.
- Either [through the API](../api/users.md#user-modification).
- Or by navigating to the **Admin area > Overview > Users** to create a new user
or edit an existing one. There, you will find the option to flag the user as
external.
### Default internal users
### Setting new users to external
The "Internal users" field allows specifying an e-mail address regex pattern to identify default internal users.
By default, new users are not set as external users. This behavior can be changed
by an administrator under the **Admin Area > Settings > General > Account and limit** page.
New users whose email address matches the regex pattern will be set to internal by default rather than an external collaborator.
If you change the default behavior of creating new users as external, you will
have the option to narrow it down by defining a set of internal users.
The **Internal users** field allows specifying an email address regex pattern to
identify default internal users. New users whose email address matches the regex
pattern will be set to internal by default rather than an external collaborator.
The regex pattern format is Ruby, but it needs to be convertible to JavaScript, and the ignore case flag will be set, e.g. "/regex pattern/i".
The regex pattern format is Ruby, but it needs to be convertible to JavaScript,
and the ignore case flag will be set (`/regex pattern/i`). Here are some examples:
Here are some examples:
- Use `\.internal@domain\.com$` to mark email addresses ending with
`.internal@domain.com` as internal.
- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses
NOT including `.ext@domain.com` as internal.
- Use `\.internal@domain\.com$` to mark email addresses ending with ".internal@domain.com" internal.
- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses NOT including .ext@domain.com internal.
CAUTION: **Warning:**
Be aware that this regex could lead to a
[regular expression denial of service (ReDoS) attack](https://en.wikipedia.org/wiki/ReDoS).
## Free Guest users **(ULTIMATE)**
Please be aware that this regex could lead to a DOS attack, [see](https://en.wikipedia.org/wiki/ReDoS?) ReDos on Wikipedia.
When a user is given Guest permissions on a project, group, or both, and holds no
higher permission level on any other project or group on the GitLab instance,
the user is considered a guest user by GitLab and will not consume a license seat.
There is no other specific "guest" designation for newly created users.
If the user is assigned a higher role on any projects or groups, the user will
take a license seat. If a user creates a project, the user becomes a Maintainer
on the project, resulting in the use of a license seat. Also, note that if your
project is internal or private, Guest users will have all the abilities that are
mentioned in the [permissions table above](#project-members-permissions) (they
will not be able to browse the project's repository for example).
TIP: **Tip:**
To prevent a guest user from creating projects, as an admin, you can edit the
user's profile to mark the user as [external](#external-users-core-only).
Beware though that even if a user is external, if they already have Reporter or
higher permissions in any project or group, they will **not** be counted as a
free guest user.
## Auditor users **(PREMIUM ONLY)**
......
......@@ -21,7 +21,7 @@ Here's how the integration responds when you take the following actions in GitLa
- GitLab hyperlinks to the Jira issue.
- The Jira issue adds an issue link to the commit/MR in GitLab.
- The Jira issue adds a comment reflecting the comment made in GitLab, the comment author, and a link to the commit/MR in GitLab.
- **Mention that a commit or MR 'closes', 'resolves', or 'fixes' a Jira issue ID**. When the commit is made on master or the change is merged to master:
- **Mention that a commit or MR 'closes', 'resolves', or 'fixes' a Jira issue ID**. When the commit is made on the project's default branch (usually master) or the change is merged to the default branch:
- GitLab's merge request page displays a note that it "Closed" the Jira issue, with a link to the issue. (Note: Before the merge, an MR will display that it "Closes" the Jira issue.)
- The Jira issue shows the activity and the Jira issue is closed, or otherwise transitioned.
......
......@@ -47,7 +47,7 @@ It is important to note that we have a few types of users:
Administrator will have to be a member of it in order to have access to it
via another project's job.
- **External users**: CI jobs created by [external users](../permissions.md#external-users-permissions) will have
- **External users**: CI jobs created by [external users](../permissions.md#external-users-core-only) will have
access only to projects to which user has at least reporter access. This
rules out accessing all internal projects by default.
......@@ -58,7 +58,7 @@ Let's consider the following scenario:
hosted in private repositories and you have multiple CI jobs that make use
of these repositories.
1. You invite a new [external user](../permissions.md#external-users-permissions). CI jobs created by that user do not
1. You invite a new [external user](../permissions.md#external-users-core-only). CI jobs created by that user do not
have access to internal repositories, because the user also doesn't have the
access from within GitLab. You as an employee have to grant explicit access
for this user. This allows us to prevent from accidental data leakage.
......
......@@ -86,6 +86,20 @@ Click **Protect** and the branch will appear in the "Protected branch" list.
![Roles and users list](img/protected_branches_select_roles_and_users_list.png)
## Code Owners approvals **(PREMIUM)**
It is possible to require at least one approval for each entry in the
[`CODEOWNERS` file](code_owners.md) that matches a file changed in
the merge request. To enable this feature:
1. Toggle the **Require approval from code owners** slider.
1. Click **Protect**.
When this feature is enabled, all merge requests need approval
from one code owner per matched rule before they can be merged. Additionally,
pushes to the protected branch are denied if a rule is matched.
## Wildcard protected branches
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/4665) in GitLab 8.10.
......
......@@ -42,7 +42,7 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
desc 'Protect a single branch or wildcard' do
desc 'Protect a single branch' do
success Entities::ProtectedBranch
end
params do
......@@ -93,3 +93,5 @@ module API
end
end
end
API::ProtectedBranches.prepend_if_ee('EE::API::ProtectedBranches')
......@@ -4,6 +4,7 @@ module Gitlab
module Ci
class Trace
include ::Gitlab::ExclusiveLeaseHelpers
include Checksummable
LOCK_TTL = 10.minutes
LOCK_RETRIES = 2
......@@ -193,7 +194,7 @@ module Gitlab
project: job.project,
file_type: :trace,
file: stream,
file_sha256: Digest::SHA256.file(path).hexdigest)
file_sha256: self.class.hexdigest(path))
end
end
......
......@@ -358,6 +358,9 @@ msgstr[1] ""
msgid "%{tabname} changed"
msgstr ""
msgid "%{template_project_id} is unknown or invalid"
msgstr ""
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
msgstr[0] ""
......@@ -12812,6 +12815,48 @@ msgstr ""
msgid "Protected branches"
msgstr ""
msgid "ProtectedBranch|%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported"
msgstr ""
msgid "ProtectedBranch|Allowed to merge"
msgstr ""
msgid "ProtectedBranch|Allowed to merge:"
msgstr ""
msgid "ProtectedBranch|Allowed to push"
msgstr ""
msgid "ProtectedBranch|Allowed to push:"
msgstr ""
msgid "ProtectedBranch|Code owner approval"
msgstr ""
msgid "ProtectedBranch|Last commit"
msgstr ""
msgid "ProtectedBranch|Protect"
msgstr ""
msgid "ProtectedBranch|Protect a branch"
msgstr ""
msgid "ProtectedBranch|Protected branch (%{protected_branches_count})"
msgstr ""
msgid "ProtectedBranch|Pushes that change filenames matched by the CODEOWNERS file will be rejected"
msgstr ""
msgid "ProtectedBranch|Require approval from code owners:"
msgstr ""
msgid "ProtectedBranch|There are currently no protected branches, protect a branch with the form above."
msgstr ""
msgid "ProtectedBranch|Toggle code owner approval"
msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
......@@ -13468,9 +13513,6 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
msgid "Require approval from code owners"
msgstr ""
msgid "Require user password to approve"
msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20190827102026_migrate_code_owner_approval_status_to_protected_branches_in_batches.rb')
describe MigrateCodeOwnerApprovalStatusToProtectedBranchesInBatches, :migration do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:protected_branches) { table(:protected_branches) }
let(:namespace) do
namespaces.create!(
path: 'gitlab-instance-administrators',
name: 'GitLab Instance Administrators'
)
end
let(:project) do
projects.create!(
namespace_id: namespace.id,
name: 'GitLab Instance Administration'
)
end
let!(:protected_branch_1) do
protected_branches.create!(
name: "branch name",
project_id: project.id
)
end
describe '#up' do
context "when there's no projects needing approval" do
it "doesn't change any protected branch records" do
expect { migrate! }
.not_to change { ProtectedBranch.where(code_owner_approval_required: true).count }
end
end
context "when there's a project needing approval" do
let!(:project_needing_approval) do
projects.create!(
namespace_id: namespace.id,
name: 'GitLab Instance Administration',
merge_requests_require_code_owner_approval: true
)
end
let!(:protected_branch_2) do
protected_branches.create!(
name: "branch name",
project_id: project_needing_approval.id
)
end
it "changes N protected branch records" do
expect { migrate! }
.to change { ProtectedBranch.where(code_owner_approval_required: true).count }
.by(1)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Checksummable do
describe ".hexdigest" do
let(:fake_class) do
Class.new do
include Checksummable
end
end
it 'returns the SHA256 sum of the file' do
expected = Digest::SHA256.file(__FILE__).hexdigest
expect(fake_class.hexdigest(__FILE__)).to eq(expected)
end
end
end
......@@ -156,4 +156,15 @@ describe LfsObject do
end
end
end
describe ".calculate_oid" do
let(:lfs_object) { create(:lfs_object, :with_file) }
it 'returns SHA256 sum of the file' do
path = lfs_object.file.path
expected = Digest::SHA256.file(path).hexdigest
expect(described_class.calculate_oid(path)).to eq expected
end
end
end
......@@ -423,7 +423,7 @@ shared_examples_for 'trace with disabled live trace feature' do
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
expect(File.exist?(src_path)).to be_falsy
expect(src_checksum)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).hexdigest)
.to eq(described_class.hexdigest(build.job_artifacts_trace.file.path))
expect(build.job_artifacts_trace.file_sha256).to eq(src_checksum)
end
end
......@@ -449,7 +449,7 @@ shared_examples_for 'trace with disabled live trace feature' do
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
expect(build.old_trace).to be_nil
expect(src_checksum)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).hexdigest)
.to eq(described_class.hexdigest(build.job_artifacts_trace.file.path))
expect(build.job_artifacts_trace.file_sha256).to eq(src_checksum)
end
end
......@@ -787,7 +787,7 @@ shared_examples_for 'trace with enabled live trace feature' do
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
expect(Ci::BuildTraceChunk.where(build: build)).not_to be_exist
expect(src_checksum)
.to eq(Digest::SHA256.file(build.job_artifacts_trace.file.path).hexdigest)
.to eq(described_class.hexdigest(build.job_artifacts_trace.file.path))
expect(build.job_artifacts_trace.file_sha256).to eq(src_checksum)
end
end
......
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