Commit 33cf6171 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into feature/gb/pipeline-only-except-with-modified-paths

* master:
  Require spec helpers loaded by other spec helpers first
  Resolve "2FA mobile options should be rephrased"
  Add css class to Admin > Project > show page
  Trim whitespace when inviting a new user by email
  Bump Gitaly to v0.124.0
  Banzai project ref- share context more aggresively
  Add reliable fetcher for Sidekiq
  Allows to filter issues by `Any milestone` in the API
parents a545703e 1e9003f4
......@@ -295,6 +295,7 @@ gem 'peek-mysql2', '~> 1.1.0', group: :mysql
gem 'peek-pg', '~> 1.3.0', group: :postgres
gem 'peek-rblineprof', '~> 0.2.0'
gem 'peek-redis', '~> 1.2.0'
gem 'gitlab-sidekiq-fetcher', require: 'sidekiq-reliable-fetch'
# Metrics
group :metrics do
......
......@@ -301,6 +301,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
gitlab-sidekiq-fetcher (0.3.0)
sidekiq (~> 5)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
......@@ -1031,6 +1033,7 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-markup (~> 1.6.4)
gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
......
......@@ -304,6 +304,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
gitlab-sidekiq-fetcher (0.3.0)
sidekiq (~> 5)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
......@@ -1040,6 +1042,7 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-markup (~> 1.6.4)
gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
......
......@@ -592,7 +592,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) {
var trimmed = query.term.trim();
emailUser = {
name: "Invite \"" + query.term + "\" by email",
name: "Invite \"" + trimmed + "\" by email",
username: trimmed,
id: trimmed,
invite: true
......
......@@ -428,6 +428,10 @@ class IssuableFinder
params[:milestone_title] == Milestone::Upcoming.name
end
def filter_by_any_milestone?
params[:milestone_title] == Milestone::Any.title
end
def filter_by_started_milestone?
params[:milestone_title] == Milestone::Started.name
end
......@@ -437,6 +441,8 @@ class IssuableFinder
if milestones?
if filter_by_no_milestone?
items = items.left_joins_milestones.where(milestone_id: [-1, nil])
elsif filter_by_any_milestone?
items = items.any_milestone
elsif filter_by_upcoming_milestone?
upcoming_ids = Milestone.upcoming_ids_by_projects(projects(items))
items = items.left_joins_milestones.where(milestone_id: upcoming_ids)
......
......@@ -129,7 +129,7 @@ class LabelsFinder < UnionFinder
end
def project?
params[:project_id].present?
params[:project].present? || params[:project_id].present?
end
def projects?
......@@ -152,7 +152,7 @@ class LabelsFinder < UnionFinder
return @project if defined?(@project)
if project?
@project = Project.find(params[:project_id])
@project = params[:project] || Project.find(params[:project_id])
@project = nil unless authorized_to_read_labels?(@project)
else
@project = nil
......
......@@ -76,6 +76,7 @@ module Issuable
scope :recent, -> { reorder(id: :desc) }
scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_milestones, ->(ids) { where(milestone_id: ids) }
scope :any_milestone, -> { where('milestone_id IS NOT NULL') }
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
scope :opened, -> { with_state(:opened) }
scope :only_opened, -> { with_state(:opened) }
......
- add_to_breadcrumbs "Projects", admin_projects_path
- breadcrumb_title @project.full_name
- page_title @project.full_name, "Projects"
- @content_class = "admin-projects"
%h3.page-title
Project: #{@project.full_name}
= link_to edit_project_path(@project), class: "btn btn-nr float-right" do
......
%p.slead
Should you ever lose your phone, each of these recovery codes can be used one
Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one
time each to regain access to your account. Please save them in a safe place, or you
%b will
lose access to your account.
......
......@@ -6,13 +6,13 @@
.row.prepend-top-default
.col-lg-4
%h4.prepend-top-0
Register Two-Factor Authentication App
Register Two-Factor Authenticator
%p
Use an app on your mobile device to enable two-factor authentication (2FA).
Use an one time password authenticator on your mobile device or computer to enable two-factor authentication (2FA).
.col-lg-8
- if current_user.two_factor_otp_enabled?
%p
You've already enabled two-factor authentication using mobile authenticator applications. In order to register a different device, you must first disable two-factor authentication.
You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication.
%p
If you lose your recovery codes you can generate new ones, invalidating all previous codes.
%div
......
---
title: Rephrase 2FA and TOTP documentation and view
merge_request: 21998
author: Marc Schwede
type: other
---
title: Allows to filter issues by Any milestone in the API
merge_request: 22080
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Trim whitespace when inviting a new user by email
merge_request: 22119
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: Use Reliable Sidekiq fetch
merge_request: 21715
author:
type: fixed
---
title: Banzai label ref finder - minimize SQL calls by sharing context more aggresively
merge_request: 22070
author:
type: performance
......@@ -40,6 +40,10 @@ Sidekiq.configure_server do |config|
ActiveRecord::Base.clear_all_connections!
end
if Feature.enabled?(:gitlab_sidekiq_reliable_fetcher)
Sidekiq::ReliableFetcher.setup_reliable_fetch!(config)
end
# Sidekiq-cron: load recurring jobs from gitlab.yml
# UGLY Hack to get nested hash from settingslogic
cron_jobs = JSON.parse(Gitlab.config.cron_jobs.to_json)
......@@ -57,10 +61,10 @@ Sidekiq.configure_server do |config|
Gitlab::SidekiqVersioning.install!
config = Gitlab::Database.config ||
db_config = Gitlab::Database.config ||
Rails.application.config.database_configuration[Rails.env]
config['pool'] = Sidekiq.options[:concurrency]
ActiveRecord::Base.establish_connection(config)
db_config['pool'] = Sidekiq.options[:concurrency]
ActiveRecord::Base.establish_connection(db_config)
Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}")
# Avoid autoload issue such as 'Mail::Parsers::AddressStruct'
......
......@@ -37,7 +37,7 @@ GET /issues?my_reaction_emoji=star
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone. `Any+Milestone` lists all issues that have an assigned milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
......
......@@ -2,18 +2,18 @@
Two-factor Authentication (2FA) provides an additional level of security to your
GitLab account. Once enabled, in addition to supplying your username and
password to login, you'll be prompted for a code generated by an application on
your phone.
password to login, you'll be prompted for a code generated by your one time password
authenticator. For example, a password manager on one of your devices.
By enabling 2FA, the only way someone other than you can log into your account
is to know your username and password *and* have access to your phone.
is to know your username and password *and* have access to your one time password secret.
## Overview
> **Note:**
When you enable 2FA, don't forget to back up your recovery codes.
In addition to a phone application, GitLab supports U2F (universal 2nd factor) devices as
In addition to one time authenticators (TOTP), GitLab supports U2F (universal 2nd factor) devices as
the second factor of authentication. Once enabled, in addition to supplying your username and
password to login, you'll be prompted to activate your U2F device (usually by pressing
a button on it), and it will perform secure authentication on your behalf.
......@@ -24,10 +24,10 @@ from other browsers.
## Enabling 2FA
There are two ways to enable two-factor authentication: via a mobile application
There are two ways to enable two-factor authentication: via a one time password authenticator
or a U2F device.
### Enable 2FA via mobile application
### Enable 2FA via one time password authenticator
**In GitLab:**
......@@ -82,7 +82,7 @@ Click on **Register U2F Device** to complete the process.
> **Note:**
Recovery codes are not generated for U2F devices.
Should you ever lose access to your phone, you can use one of the ten provided
Should you ever lose access to your one time password authenticator, you can use one of the ten provided
backup codes to login to your account. We suggest copying or printing them for
storage in a safe place. **Each code can be used only once** to log in to your
account.
......@@ -98,7 +98,7 @@ be presented with a second prompt, depending on which type of 2FA you've enabled
### Log in via mobile application
Enter the pin from your phone's application or a recovery code to log in.
Enter the pin from your one time password authenticator's application or a recovery code to log in.
![Two-Factor Authentication on sign in via OTP](img/2fa_auth.png)
......
......@@ -13,6 +13,7 @@ module Banzai
# Returns a Project, or nil if the reference can't be found
def parent_from_ref(ref)
return context[:project] || context[:group] unless ref
return context[:project] if context[:project]&.full_path == ref
Project.find_by_full_path(ref)
end
......
......@@ -48,7 +48,7 @@ module Banzai
include_ancestor_groups: true,
only_group_labels: true }
else
{ project_id: parent.id,
{ project: parent,
include_ancestor_groups: true }
end
......
......@@ -637,6 +637,18 @@ describe Projects::IssuesController do
project_id: project,
id: id
end
it 'avoids (most) N+1s loading labels' do
label = create(:label, project: project).to_reference
labels = create_list(:label, 10, project: project).map(&:to_reference)
issue = create(:issue, project: project, description: 'Test issue')
control_count = ActiveRecord::QueryRecorder.new { issue.update(description: [issue.description, label].join(' ')) }.count
# Follow-up to get rid of this `2 * label.count` requirement: https://gitlab.com/gitlab-org/gitlab-ce/issues/52230
expect { issue.update(description: [issue.description, labels].join(' ')) }
.not_to exceed_query_limit(control_count + 2 * labels.count)
end
end
describe 'GET #realtime_changes' do
......
......@@ -42,7 +42,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
it 'allows registering a new device with a name' do
visit profile_account_path
manage_two_factor_authentication
expect(page).to have_content("You've already enabled two-factor authentication using mobile")
expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
u2f_device = register_u2f_device
......@@ -70,7 +70,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
it 'allows deleting a device' do
visit profile_account_path
manage_two_factor_authentication
expect(page).to have_content("You've already enabled two-factor authentication using mobile")
expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
first_u2f_device = register_u2f_device
second_u2f_device = register_u2f_device(name: 'My other device')
......
......@@ -125,6 +125,14 @@ describe IssuesFinder do
end
end
context 'filtering by any milestone' do
let(:params) { { milestone_title: Milestone::Any.title } }
it 'returns issues with any assigned milestone' do
expect(issues).to contain_exactly(issue1)
end
end
context 'filtering by upcoming milestone' do
let(:params) { { milestone_title: Milestone::Upcoming.name } }
......
require 'spec_helper'
describe Banzai::CrossProjectReference do
include described_class
let(:including_class) { Class.new.include(described_class).new }
before do
allow(including_class).to receive(:context).and_return({})
allow(including_class).to receive(:parent_from_ref).and_call_original
end
describe '#parent_from_ref' do
context 'when no project was referenced' do
it 'returns the project from context' do
project = double
allow(self).to receive(:context).and_return({ project: project })
allow(including_class).to receive(:context).and_return({ project: project })
expect(parent_from_ref(nil)).to eq project
expect(including_class.parent_from_ref(nil)).to eq project
end
end
......@@ -18,15 +23,15 @@ describe Banzai::CrossProjectReference do
it 'returns the group from context' do
group = double
allow(self).to receive(:context).and_return({ group: group })
allow(including_class).to receive(:context).and_return({ group: group })
expect(parent_from_ref(nil)).to eq group
expect(including_class.parent_from_ref(nil)).to eq group
end
end
context 'when referenced project does not exist' do
it 'returns nil' do
expect(parent_from_ref('invalid/reference')).to be_nil
expect(including_class.parent_from_ref('invalid/reference')).to be_nil
end
end
......@@ -37,7 +42,7 @@ describe Banzai::CrossProjectReference do
expect(Project).to receive(:find_by_full_path)
.with('cross/reference').and_return(project2)
expect(parent_from_ref('cross/reference')).to eq project2
expect(including_class.parent_from_ref('cross/reference')).to eq project2
end
end
end
......
......@@ -60,6 +60,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
exp = act = "See #{commit1.id.reverse}...#{commit2.id}"
allow(project.repository).to receive(:commit).with(commit1.id.reverse)
allow(project.repository).to receive(:commit).with(commit2.id)
expect(reference_filter(act).to_html).to eq exp
end
......
......@@ -56,6 +56,7 @@ describe API::Issues do
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
let(:no_milestone_title) { URI.escape(Milestone::None.title) }
let(:any_milestone_title) { URI.escape(Milestone::Any.title) }
before(:all) do
project.add_reporter(user)
......@@ -811,6 +812,15 @@ describe API::Issues do
expect(json_response.first['id']).to eq(confidential_issue.id)
end
it 'returns an array of issues with any milestone' do
get api("#{base_url}/issues?milestone=#{any_milestone_title}", user)
response_ids = json_response.map { |issue| issue['id'] }
expect_paginated_array_response(size: 2)
expect(response_ids).to contain_exactly(closed_issue.id, issue.id)
end
it 'sorts by created_at descending by default' do
get api("#{base_url}/issues", user)
......
......@@ -34,6 +34,11 @@ Rainbow.enabled = false
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
# Requires helpers, and shared contexts/examples first since they're used in other support files
# Load these first since they may be required by other helpers
require Rails.root.join("spec/support/helpers/git_helpers.rb")
# Then the rest
Dir[Rails.root.join("spec/support/helpers/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f }
......
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