Commit 759bab05 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 61f0c589
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
.signup-heading h2 { .signup-heading h2 {
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
padding: 0 10px; padding: 0 $gl-padding;
@include media-breakpoint-down(md) { @include media-breakpoint-down(md) {
font-size: $gl-font-size-large; font-size: $gl-font-size-large;
......
...@@ -17,7 +17,7 @@ class ApplicationController < ActionController::Base ...@@ -17,7 +17,7 @@ class ApplicationController < ActionController::Base
include Gitlab::Tracking::ControllerConcern include Gitlab::Tracking::ControllerConcern
include Gitlab::Experimentation::ControllerConcern include Gitlab::Experimentation::ControllerConcern
before_action :authenticate_user! before_action :authenticate_user!, except: [:route_not_found]
before_action :enforce_terms!, if: :should_enforce_terms? before_action :enforce_terms!, if: :should_enforce_terms?
before_action :validate_user_service_ticket! before_action :validate_user_service_ticket!
before_action :check_password_expiration before_action :check_password_expiration
...@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base ...@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base
before_action :active_user_check, unless: :devise_controller? before_action :active_user_check, unless: :devise_controller?
before_action :set_usage_stats_consent_flag before_action :set_usage_stats_consent_flag
before_action :check_impersonation_availability before_action :check_impersonation_availability
before_action :require_role before_action :required_signup_info
around_action :set_locale around_action :set_locale
around_action :set_session_storage around_action :set_session_storage
...@@ -95,11 +95,13 @@ class ApplicationController < ActionController::Base ...@@ -95,11 +95,13 @@ class ApplicationController < ActionController::Base
end end
def route_not_found def route_not_found
# We need to call #authenticate_user! here because sometimes this is called from another action if current_user
# and not from our wildcard fallback route not_found
authenticate_user! else
store_location_for(:user, request.fullpath) unless request.xhr?
not_found redirect_to new_user_session_path, alert: I18n.t('devise.failure.unauthenticated')
end
end end
def render(*args) def render(*args)
...@@ -536,10 +538,13 @@ class ApplicationController < ActionController::Base ...@@ -536,10 +538,13 @@ class ApplicationController < ActionController::Base
@current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user) @current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user)
end end
# A user requires a role when they are part of the experimental signup flow (executed by the Growth team). Users # A user requires a role and have the setup_for_company attribute set when they are part of the experimental signup
# are redirected to the welcome page when their role is required and the experiment is enabled for the current user. # flow (executed by the Growth team). Users are redirected to the welcome page when their role is required and the
def require_role # experiment is enabled for the current user.
return unless current_user && current_user.role_required? && experiment_enabled?(:signup_flow) def required_signup_info
return unless current_user
return unless current_user.role_required?
return unless experiment_enabled?(:signup_flow)
store_location_for :user, request.fullpath store_location_for :user, request.fullpath
......
...@@ -8,7 +8,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -8,7 +8,7 @@ class RegistrationsController < Devise::RegistrationsController
layout :choose_layout layout :choose_layout
skip_before_action :require_role, only: [:welcome, :update_role] skip_before_action :required_signup_info, only: [:welcome, :update_registration]
prepend_before_action :check_captcha, only: :create prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, only: [:destroy] before_action :whitelist_query_limiting, only: [:destroy]
before_action :ensure_terms_accepted, before_action :ensure_terms_accepted,
...@@ -53,22 +53,22 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -53,22 +53,22 @@ class RegistrationsController < Devise::RegistrationsController
def welcome def welcome
return redirect_to new_user_registration_path unless current_user return redirect_to new_user_registration_path unless current_user
return redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) if current_user.role.present? return redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) if current_user.role.present? && !current_user.setup_for_company.nil?
current_user.name = nil current_user.name = nil if current_user.name == current_user.username
render layout: 'devise_experimental_separate_sign_up_flow' render layout: 'devise_experimental_separate_sign_up_flow'
end end
def update_role def update_registration
user_params = params.require(:user).permit(:name, :role) user_params = params.require(:user).permit(:name, :role, :setup_for_company)
result = ::Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute result = ::Users::SignupService.new(current_user, user_params).execute
if result[:status] == :success if result[:status] == :success
track_experiment_event(:signup_flow, 'end') # We want this event to be tracked when the user is _in_ the experimental group track_experiment_event(:signup_flow, 'end') # We want this event to be tracked when the user is _in_ the experimental group
set_flash_message! :notice, :signed_up set_flash_message! :notice, :signed_up
redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) redirect_to stored_location_or_dashboard_or_almost_there_path(current_user)
else else
redirect_to users_sign_up_welcome_path, alert: result[:message] render :welcome, layout: 'devise_experimental_separate_sign_up_flow'
end end
end end
......
...@@ -385,6 +385,9 @@ class IssuableFinder ...@@ -385,6 +385,9 @@ class IssuableFinder
end end
def count_key(value) def count_key(value)
# value may be an array if the finder used in `count_by_state` added an
# additional `group by`. Anyway we are sure that state will be always the
# last item because it's added as the last one to the query.
value = Array(value).last value = Array(value).last
klass.available_states.key(value) klass.available_states.key(value)
end end
......
...@@ -18,6 +18,11 @@ class LfsObject < ApplicationRecord ...@@ -18,6 +18,11 @@ class LfsObject < ApplicationRecord
after_save :update_file_store, if: :saved_change_to_file? after_save :update_file_store, if: :saved_change_to_file?
def self.not_linked_to_project(project)
where('NOT EXISTS (?)',
project.lfs_objects_projects.select(1).where('lfs_objects_projects.lfs_object_id = lfs_objects.id'))
end
def update_file_store def update_file_store
# The file.object_store is set during `uploader.store!` # The file.object_store is set during `uploader.store!`
# which happens after object is inserted/updated # which happens after object is inserted/updated
......
...@@ -240,6 +240,7 @@ class User < ApplicationRecord ...@@ -240,6 +240,7 @@ class User < ApplicationRecord
delegate :time_display_relative, :time_display_relative=, to: :user_preference delegate :time_display_relative, :time_display_relative=, to: :user_preference
delegate :time_format_in_24h, :time_format_in_24h=, to: :user_preference delegate :time_format_in_24h, :time_format_in_24h=, to: :user_preference
delegate :show_whitespace_in_diffs, :show_whitespace_in_diffs=, to: :user_preference delegate :show_whitespace_in_diffs, :show_whitespace_in_diffs=, to: :user_preference
delegate :setup_for_company, :setup_for_company=, to: :user_preference
accepts_nested_attributes_for :user_preference, update_only: true accepts_nested_attributes_for :user_preference, update_only: true
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
module Projects module Projects
module LfsPointers module LfsPointers
class LfsLinkService < BaseService class LfsLinkService < BaseService
TooManyOidsError = Class.new(StandardError)
MAX_OIDS = 100_000
BATCH_SIZE = 1000 BATCH_SIZE = 1000
# Accept an array of oids to link # Accept an array of oids to link
...@@ -12,6 +15,10 @@ module Projects ...@@ -12,6 +15,10 @@ module Projects
def execute(oids) def execute(oids)
return [] unless project&.lfs_enabled? return [] unless project&.lfs_enabled?
if oids.size > MAX_OIDS
raise TooManyOidsError, 'Too many LFS object ids to link, please push them manually'
end
# Search and link existing LFS Object # Search and link existing LFS Object
link_existing_lfs_objects(oids) link_existing_lfs_objects(oids)
end end
...@@ -20,22 +27,27 @@ module Projects ...@@ -20,22 +27,27 @@ module Projects
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def link_existing_lfs_objects(oids) def link_existing_lfs_objects(oids)
all_existing_objects = [] linked_existing_objects = []
iterations = 0 iterations = 0
LfsObject.where(oid: oids).each_batch(of: BATCH_SIZE) do |existent_lfs_objects| oids.each_slice(BATCH_SIZE) do |oids_batch|
# Load all existing LFS Objects immediately so we don't issue an extra
# query for the `.any?`
existent_lfs_objects = LfsObject.where(oid: oids_batch).load
next unless existent_lfs_objects.any? next unless existent_lfs_objects.any?
rows = existent_lfs_objects
.not_linked_to_project(project)
.map { |existing_lfs_object| { project_id: project.id, lfs_object_id: existing_lfs_object.id } }
Gitlab::Database.bulk_insert(:lfs_objects_projects, rows)
iterations += 1 iterations += 1
not_linked_lfs_objects = existent_lfs_objects.where.not(id: project.all_lfs_objects)
project.all_lfs_objects << not_linked_lfs_objects
all_existing_objects += existent_lfs_objects.pluck(:oid) linked_existing_objects += existent_lfs_objects.map(&:oid)
end end
log_lfs_link_results(all_existing_objects.count, iterations) log_lfs_link_results(linked_existing_objects.count, iterations)
all_existing_objects linked_existing_objects
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
# frozen_string_literal: true
module Users
class SignupService < BaseService
def initialize(current_user, params = {})
@user = current_user
@params = params.dup
end
def execute
assign_attributes
inject_validators
if @user.save
success
else
error(@user.errors.full_messages.join('. '))
end
end
private
def assign_attributes
@user.assign_attributes(params) unless params.empty?
end
def inject_validators
class << @user
validates :role, presence: true
validates :setup_for_company, inclusion: { in: [true, false], message: :blank }
end
end
end
end
- content_for(:page_title, _('Welcome to GitLab %{username}!') % { username: current_user.username }) - content_for(:page_title, _('Welcome to GitLab @%{username}!') % { username: current_user.username })
- max_name_length = 128 - max_name_length = 128
.text-center.mb-3 .text-center.mb-3
= _('In order to tailor your experience with GitLab<br>we would like to know a bit more about you.').html_safe = _('In order to tailor your experience with GitLab we<br>would like to know a bit more about you.').html_safe
.signup-box.p-3.mb-2 .signup-box.p-3.mb-2
.signup-body .signup-body
= form_for(current_user, url: users_sign_up_update_role_path, html: { class: 'new_new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f| = form_for(current_user, url: users_sign_up_update_registration_path, html: { class: 'new_new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f|
.devise-errors.mt-0 .devise-errors.mt-0
= render 'devise/shared/error_messages', resource: current_user = render 'devise/shared/error_messages', resource: current_user
.name.form-group .name.form-group
...@@ -13,5 +13,14 @@ ...@@ -13,5 +13,14 @@
.form-group .form-group
= f.label :role, _('Role'), class: 'label-bold' = f.label :role, _('Role'), class: 'label-bold'
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'form-control' = f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'form-control'
.form-group
= f.label :setup_for_company, _('Are you setting up GitLab for a company?'), class: 'label-bold'
.d-flex.justify-content-center
.w-25
= f.radio_button :setup_for_company, true
= f.label :setup_for_company, _('Yes'), value: 'true'
.w-25
= f.radio_button :setup_for_company, false
= f.label :setup_for_company, _('No'), value: 'false'
.submit-container.mt-3 .submit-container.mt-3
= f.submit _('Get started!'), class: 'btn-register btn btn-block mb-0 p-2' = f.submit _('Get started!'), class: 'btn-register btn btn-block mb-0 p-2'
---
title: Fix JSON responses returning 302 instead of 401
merge_request: 19412
author:
type: fixed
---
title: Improve performance of linking LFS objects during import
merge_request: 19709
author:
type: performance
---
title: Ask if the user is setting up GitLab for a company during signup
merge_request: 17999
author:
type: changed
...@@ -57,7 +57,7 @@ Rails.application.routes.draw do ...@@ -57,7 +57,7 @@ Rails.application.routes.draw do
# Sign up # Sign up
get 'users/sign_up/welcome' => 'registrations#welcome' get 'users/sign_up/welcome' => 'registrations#welcome'
patch 'users/sign_up/update_role' => 'registrations#update_role' patch 'users/sign_up/update_registration' => 'registrations#update_registration'
# Search # Search
get 'search' => 'search#show' get 'search' => 'search#show'
......
...@@ -52,7 +52,7 @@ scope(path: '*namespace_id/:project_id', ...@@ -52,7 +52,7 @@ scope(path: '*namespace_id/:project_id',
# /info/refs?service=git-receive-pack, but nothing else. # /info/refs?service=git-receive-pack, but nothing else.
# #
git_http_handshake = lambda do |request| git_http_handshake = lambda do |request|
::Constraints::ProjectUrlConstrainer.new.matches?(request) && ::Constraints::ProjectUrlConstrainer.new.matches?(request, existence_check: false) &&
(request.query_string.blank? || (request.query_string.blank? ||
request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/)) request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/))
end end
......
...@@ -245,6 +245,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -245,6 +245,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :validate_query, on: :collection post :validate_query, on: :collection
end end
end end
Gitlab.ee do
resources :alerts, constraints: { id: /\d+/ }, only: [:index, :create, :show, :update, :destroy] do
post :notify, on: :collection
end
end
end end
resources :merge_requests, concerns: :awardable, except: [:new, :create, :show], constraints: { id: /\d+/ } do resources :merge_requests, concerns: :awardable, except: [:new, :create, :show], constraints: { id: /\d+/ } do
...@@ -347,6 +353,17 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -347,6 +353,17 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
Gitlab.ee do
resources :path_locks, only: [:index, :destroy] do
collection do
post :toggle
end
end
get '/service_desk' => 'service_desk#show', as: :service_desk
put '/service_desk' => 'service_desk#update', as: :service_desk_refresh
end
resource :variables, only: [:show, :update] resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :edit, :update, :destroy] resources :triggers, only: [:index, :create, :edit, :update, :destroy]
...@@ -380,6 +397,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -380,6 +397,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :failures get :failures
get :status get :status
get :test_report get :test_report
Gitlab.ee do
get :security
get :licenses
end
end end
member do member do
...@@ -514,11 +536,24 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -514,11 +536,24 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :realtime_changes get :realtime_changes
post :create_merge_request post :create_merge_request
get :discussions, format: :json get :discussions, format: :json
Gitlab.ee do
get 'designs(/*vueroute)', to: 'issues#designs', as: :designs, format: false
end
end end
collection do collection do
post :bulk_update post :bulk_update
post :import_csv post :import_csv
Gitlab.ee do
post :export_csv
get :service_desk
end
end
Gitlab.ee do
resources :issue_links, only: [:index, :create, :destroy], as: 'links', path: 'links'
end end
end end
...@@ -594,15 +629,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -594,15 +629,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
Gitlab.ee do Gitlab.ee do
resources :managed_licenses, only: [:index, :show, :new, :create, :edit, :update, :destroy] resources :managed_licenses, only: [:index, :show, :new, :create, :edit, :update, :destroy]
end end
# Legacy routes.
# Introduced in 12.0.
# Should be removed after 12.1
Gitlab::Routing.redirect_legacy_paths(self, :settings, :branches, :tags,
:network, :graphs, :autocomplete_sources,
:project_members, :deploy_keys, :deploy_tokens,
:labels, :milestones, :services, :boards, :releases,
:forks, :group_links, :import, :avatar)
end end
resources(:projects, resources(:projects,
...@@ -627,4 +653,22 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -627,4 +653,22 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
end end
# Legacy routes.
# Introduced in 12.0.
# Should be removed after 12.1
scope(path: '*namespace_id',
as: :namespace,
namespace_id: Gitlab::PathRegex.full_namespace_route_regex) do
scope(path: ':project_id',
constraints: { project_id: Gitlab::PathRegex.project_route_regex },
module: :projects,
as: :project) do
Gitlab::Routing.redirect_legacy_paths(self, :settings, :branches, :tags,
:network, :graphs, :autocomplete_sources,
:project_members, :deploy_keys, :deploy_tokens,
:labels, :milestones, :services, :boards, :releases,
:forks, :group_links, :import, :avatar)
end
end
end end
# frozen_string_literal: true
class AddSetupForCompanyToUserPreferences < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :user_preferences, :setup_for_company, :boolean
end
end
...@@ -3754,6 +3754,7 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do ...@@ -3754,6 +3754,7 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do
t.boolean "time_format_in_24h" t.boolean "time_format_in_24h"
t.string "projects_sort", limit: 64 t.string "projects_sort", limit: 64
t.boolean "show_whitespace_in_diffs", default: true, null: false t.boolean "show_whitespace_in_diffs", default: true, null: false
t.boolean "setup_for_company"
t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true
end end
......
...@@ -2,12 +2,17 @@ ...@@ -2,12 +2,17 @@
module Constraints module Constraints
class ProjectUrlConstrainer class ProjectUrlConstrainer
def matches?(request) def matches?(request, existence_check: true)
namespace_path = request.params[:namespace_id] namespace_path = request.params[:namespace_id]
project_path = request.params[:project_id] || request.params[:id] project_path = request.params[:project_id] || request.params[:id]
full_path = [namespace_path, project_path].join('/') full_path = [namespace_path, project_path].join('/')
ProjectPathValidator.valid_path?(full_path) return false unless ProjectPathValidator.valid_path?(full_path)
return true unless existence_check
# We intentionally allow SELECT(*) here so result of this query can be used
# as cache for further Project.find_by_full_path calls within request
Project.find_by_full_path(full_path, follow_redirects: request.get?).present?
end end
end end
end end
...@@ -10,7 +10,7 @@ module Gitlab ...@@ -10,7 +10,7 @@ module Gitlab
RoutesNotFound = Class.new(StandardError) RoutesNotFound = Class.new(StandardError)
def draw(routes_name) def draw(routes_name)
drawn_any = draw_ee(routes_name) | draw_ce(routes_name) drawn_any = draw_ce(routes_name) | draw_ee(routes_name)
drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}")) drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}"))
end end
......
...@@ -1947,6 +1947,9 @@ msgstr "" ...@@ -1947,6 +1947,9 @@ msgstr ""
msgid "Archiving the project will make it entirely read-only. It is hidden from the dashboard and doesn't show up in searches. <strong>The repository cannot be committed to, and no issues, comments or other entities can be created.</strong>" msgid "Archiving the project will make it entirely read-only. It is hidden from the dashboard and doesn't show up in searches. <strong>The repository cannot be committed to, and no issues, comments or other entities can be created.</strong>"
msgstr "" msgstr ""
msgid "Are you setting up GitLab for a company?"
msgstr ""
msgid "Are you sure that you want to archive this project?" msgid "Are you sure that you want to archive this project?"
msgstr "" msgstr ""
...@@ -9143,7 +9146,7 @@ msgstr "" ...@@ -9143,7 +9146,7 @@ msgstr ""
msgid "In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index." msgid "In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index."
msgstr "" msgstr ""
msgid "In order to tailor your experience with GitLab<br>we would like to know a bit more about you." msgid "In order to tailor your experience with GitLab we<br>would like to know a bit more about you."
msgstr "" msgstr ""
msgid "In the next step, you'll be able to select the projects you want to import." msgid "In the next step, you'll be able to select the projects you want to import."
...@@ -19090,7 +19093,7 @@ msgstr "" ...@@ -19090,7 +19093,7 @@ msgstr ""
msgid "Welcome to GitLab" msgid "Welcome to GitLab"
msgstr "" msgstr ""
msgid "Welcome to GitLab %{username}!" msgid "Welcome to GitLab @%{username}!"
msgstr "" msgstr ""
msgid "Welcome to the Guided GitLab Tour" msgid "Welcome to the Guided GitLab Tour"
......
...@@ -186,7 +186,7 @@ describe ApplicationController do ...@@ -186,7 +186,7 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(404) expect(response).to have_gitlab_http_status(404)
end end
it 'redirects to login page via authenticate_user! if not authenticated' do it 'redirects to login page if not authenticated' do
get :index get :index
expect(response).to redirect_to new_user_session_path expect(response).to redirect_to new_user_session_path
...@@ -827,7 +827,7 @@ describe ApplicationController do ...@@ -827,7 +827,7 @@ describe ApplicationController do
end end
end end
describe '#require_role' do describe '#required_signup_info' do
controller(described_class) do controller(described_class) do
def index; end def index; end
end end
...@@ -849,7 +849,7 @@ describe ApplicationController do ...@@ -849,7 +849,7 @@ describe ApplicationController do
it { is_expected.to redirect_to users_sign_up_welcome_path } it { is_expected.to redirect_to users_sign_up_welcome_path }
end end
context 'experiment enabled and user without a role' do context 'experiment enabled and user without a required role' do
before do before do
sign_in(user) sign_in(user)
get :index get :index
...@@ -858,7 +858,7 @@ describe ApplicationController do ...@@ -858,7 +858,7 @@ describe ApplicationController do
it { is_expected.not_to redirect_to users_sign_up_welcome_path } it { is_expected.not_to redirect_to users_sign_up_welcome_path }
end end
context 'experiment disabled and user with required role' do context 'experiment disabled' do
let(:experiment_enabled) { false } let(:experiment_enabled) { false }
before do before do
......
...@@ -142,7 +142,7 @@ describe Projects::CommitsController do ...@@ -142,7 +142,7 @@ describe Projects::CommitsController do
context 'token authentication' do context 'token authentication' do
context 'public project' do context 'public project' do
it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do it_behaves_like 'authenticates sessionless user', :show, :atom, { public: true, ignore_incrementing: true } do
before do before do
public_project = create(:project, :repository, :public) public_project = create(:project, :repository, :public)
...@@ -152,7 +152,7 @@ describe Projects::CommitsController do ...@@ -152,7 +152,7 @@ describe Projects::CommitsController do
end end
context 'private project' do context 'private project' do
it_behaves_like 'authenticates sessionless user', :show, :atom, public: false do it_behaves_like 'authenticates sessionless user', :show, :atom, { public: false, ignore_incrementing: true } do
before do before do
private_project = create(:project, :repository, :private) private_project = create(:project, :repository, :private)
private_project.add_maintainer(user) private_project.add_maintainer(user)
......
...@@ -146,7 +146,7 @@ describe Projects::ErrorTrackingController do ...@@ -146,7 +146,7 @@ describe Projects::ErrorTrackingController do
it 'redirects to sign-in page' do it 'redirects to sign-in page' do
post :list_projects, params: list_projects_params post :list_projects, params: list_projects_params
expect(response).to have_gitlab_http_status(:unauthorized) expect(response).to have_gitlab_http_status(:redirect)
end end
end end
......
...@@ -1441,7 +1441,7 @@ describe Projects::IssuesController do ...@@ -1441,7 +1441,7 @@ describe Projects::IssuesController do
context 'private project with token authentication' do context 'private project with token authentication' do
let(:private_project) { create(:project, :private) } let(:private_project) { create(:project, :private) }
it_behaves_like 'authenticates sessionless user', :index, :atom do it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do
before do before do
default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
...@@ -1449,7 +1449,7 @@ describe Projects::IssuesController do ...@@ -1449,7 +1449,7 @@ describe Projects::IssuesController do
end end
end end
it_behaves_like 'authenticates sessionless user', :calendar, :ics do it_behaves_like 'authenticates sessionless user', :calendar, :ics, ignore_incrementing: true do
before do before do
default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
......
...@@ -111,8 +111,8 @@ describe Projects::ReleasesController do ...@@ -111,8 +111,8 @@ describe Projects::ReleasesController do
context 'when the project is private and the user is not logged in' do context 'when the project is private and the user is not logged in' do
let(:project) { private_project } let(:project) { private_project }
it 'returns a 401' do it 'returns a redirect' do
expect(response).to have_gitlab_http_status(:unauthorized) expect(response).to have_gitlab_http_status(:redirect)
end end
end end
end end
......
...@@ -41,7 +41,7 @@ describe Projects::TagsController do ...@@ -41,7 +41,7 @@ describe Projects::TagsController do
context 'private project with token authentication' do context 'private project with token authentication' do
let(:private_project) { create(:project, :repository, :private) } let(:private_project) { create(:project, :repository, :private) }
it_behaves_like 'authenticates sessionless user', :index, :atom do it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do
before do before do
default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
......
...@@ -1149,7 +1149,7 @@ describe ProjectsController do ...@@ -1149,7 +1149,7 @@ describe ProjectsController do
context 'private project with token authentication' do context 'private project with token authentication' do
let(:private_project) { create(:project, :private) } let(:private_project) { create(:project, :private) }
it_behaves_like 'authenticates sessionless user', :show, :atom do it_behaves_like 'authenticates sessionless user', :show, :atom, ignore_incrementing: true do
before do before do
default_params.merge!(id: private_project, namespace_id: private_project.namespace) default_params.merge!(id: private_project, namespace_id: private_project.namespace)
......
...@@ -381,7 +381,7 @@ describe RegistrationsController do ...@@ -381,7 +381,7 @@ describe RegistrationsController do
end end
end end
describe '#update_role' do describe '#update_registration' do
before do before do
stub_experiment(signup_flow: true) stub_experiment(signup_flow: true)
stub_experiment_for_user(signup_flow: true) stub_experiment_for_user(signup_flow: true)
...@@ -395,7 +395,7 @@ describe RegistrationsController do ...@@ -395,7 +395,7 @@ describe RegistrationsController do
label: anything, label: anything,
property: 'experimental_group' property: 'experimental_group'
) )
patch :update_role, params: { user: { name: 'New name', role: 'software_developer' } } patch :update_registration, params: { user: { name: 'New name', role: 'software_developer', setup_for_company: 'false' } }
end end
end end
end end
...@@ -819,7 +819,10 @@ describe 'Pipelines', :js do ...@@ -819,7 +819,10 @@ describe 'Pipelines', :js do
context 'when project is private' do context 'when project is private' do
let(:project) { create(:project, :private, :repository) } let(:project) { create(:project, :private, :repository) }
it { expect(page).to have_content 'You need to sign in' } it 'redirects the user to sign_in and displays the flash alert' do
expect(page).to have_content 'You need to sign in'
expect(page.current_path).to eq("/users/sign_in")
end
end end
end end
......
...@@ -15,7 +15,7 @@ describe 'User views tags', :feature do ...@@ -15,7 +15,7 @@ describe 'User views tags', :feature do
it do it do
visit project_tags_path(project, format: :atom) visit project_tags_path(project, format: :atom)
expect(page).to have_gitlab_http_status(401) expect(page.current_path).to eq("/users/sign_in")
end end
end end
......
...@@ -441,11 +441,13 @@ describe 'With experimental flow' do ...@@ -441,11 +441,13 @@ describe 'With experimental flow' do
fill_in 'user_name', with: 'New name' fill_in 'user_name', with: 'New name'
select 'Software Developer', from: 'user_role' select 'Software Developer', from: 'user_role'
choose 'user_setup_for_company_true'
click_button 'Get started!' click_button 'Get started!'
new_user = User.find_by_username(new_user.username) new_user = User.find_by_username(new_user.username)
expect(new_user.name).to eq 'New name' expect(new_user.name).to eq 'New name'
expect(new_user.software_developer_role?).to be_truthy expect(new_user.software_developer_role?).to be_truthy
expect(new_user.setup_for_company).to be_truthy
expect(page).to have_current_path(new_project_path) expect(page).to have_current_path(new_project_path)
end end
end end
......
...@@ -14,15 +14,42 @@ describe Constraints::ProjectUrlConstrainer do ...@@ -14,15 +14,42 @@ describe Constraints::ProjectUrlConstrainer do
end end
context 'invalid request' do context 'invalid request' do
context "non-existing project" do
let(:request) { build_request('foo', 'bar') }
it { expect(subject.matches?(request)).to be_falsey }
context 'existence_check is false' do
it { expect(subject.matches?(request, existence_check: false)).to be_truthy }
end
end
context "project id ending with .git" do context "project id ending with .git" do
let(:request) { build_request(namespace.full_path, project.path + '.git') } let(:request) { build_request(namespace.full_path, project.path + '.git') }
it { expect(subject.matches?(request)).to be_falsey } it { expect(subject.matches?(request)).to be_falsey }
end end
end end
context 'when the request matches a redirect route' do
let(:old_project_path) { 'old_project_path' }
let!(:redirect_route) { project.redirect_routes.create!(path: "#{namespace.full_path}/#{old_project_path}") }
context 'and is a GET request' do
let(:request) { build_request(namespace.full_path, old_project_path) }
it { expect(subject.matches?(request)).to be_truthy }
end
context 'and is NOT a GET request' do
let(:request) { build_request(namespace.full_path, old_project_path, 'POST') }
it { expect(subject.matches?(request)).to be_falsey }
end
end
end end
def build_request(namespace, project) def build_request(namespace, project, method = 'GET')
double(:request, params: { namespace_id: namespace, id: project }) double(:request,
'get?': (method == 'GET'),
params: { namespace_id: namespace, id: project })
end end
end end
...@@ -3,6 +3,18 @@ ...@@ -3,6 +3,18 @@
require 'spec_helper' require 'spec_helper'
describe LfsObject do describe LfsObject do
context 'scopes' do
describe '.not_existing_in_project' do
it 'contains only lfs objects not linked to the project' do
project = create(:project)
create(:lfs_objects_project, project: project)
other_lfs_object = create(:lfs_object)
expect(described_class.not_linked_to_project(project)).to contain_exactly(other_lfs_object)
end
end
end
it 'has a distinct has_many :projects relation through lfs_objects_projects' do it 'has a distinct has_many :projects relation through lfs_objects_projects' do
lfs_object = create(:lfs_object) lfs_object = create(:lfs_object)
project = create(:project) project = create(:project)
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::BlobController do
let(:project) { create(:project, :private, :repository) }
let(:namespace) { project.namespace }
context 'anonymous user views blob in inaccessible project' do
context 'with default HTML format' do
before do
get namespace_project_blob_path(namespace_id: namespace, project_id: project, id: 'master/README.md')
end
context 'when project is private' do
it { expect(response).to have_gitlab_http_status(:redirect) }
end
context 'when project does not exist' do
let(:namespace) { 'non_existent_namespace' }
let(:project) { 'non_existent_project' }
it { expect(response).to have_gitlab_http_status(:redirect) }
end
end
context 'with JSON format' do
before do
get namespace_project_blob_path(namespace_id: namespace, project_id: project, id: 'master/README.md', format: :json)
end
context 'when project is private' do
it { expect(response).to have_gitlab_http_status(:unauthorized) }
end
context 'when project does not exist' do
let(:namespace) { 'non_existent_namespace' }
let(:project) { 'non_existent_project' }
it { expect(response).to have_gitlab_http_status(:unauthorized) }
end
end
end
end
...@@ -776,6 +776,10 @@ describe 'project routing' do ...@@ -776,6 +776,10 @@ describe 'project routing' do
it 'routes when :template_type is `issue`' do it 'routes when :template_type is `issue`' do
expect(get(show_with_template_type('issue'))).to route_to('projects/templates#show', namespace_id: 'gitlab', project_id: 'gitlabhq', template_type: 'issue', key: 'template_name', format: 'json') expect(get(show_with_template_type('issue'))).to route_to('projects/templates#show', namespace_id: 'gitlab', project_id: 'gitlabhq', template_type: 'issue', key: 'template_name', format: 'json')
end end
it 'routes to application#route_not_found when :template_type is unknown' do
expect(get(show_with_template_type('invalid'))).to route_to('application#route_not_found', unmatched_route: 'gitlab/gitlabhq/templates/invalid/template_name')
end
end end
end end
......
...@@ -16,6 +16,13 @@ describe Projects::LfsPointers::LfsLinkService do ...@@ -16,6 +16,13 @@ describe Projects::LfsPointers::LfsLinkService do
end end
describe '#execute' do describe '#execute' do
it 'raises an error when trying to link too many objects at once' do
oids = Array.new(described_class::MAX_OIDS) { |i| "oid-#{i}" }
oids << 'the straw'
expect { subject.execute(oids) }.to raise_error(described_class::TooManyOidsError)
end
it 'links existing lfs objects to the project' do it 'links existing lfs objects to the project' do
expect(project.all_lfs_objects.count).to eq 2 expect(project.all_lfs_objects.count).to eq 2
...@@ -28,7 +35,7 @@ describe Projects::LfsPointers::LfsLinkService do ...@@ -28,7 +35,7 @@ describe Projects::LfsPointers::LfsLinkService do
it 'returns linked oids' do it 'returns linked oids' do
linked = lfs_objects_project.map(&:lfs_object).map(&:oid) << new_lfs_object.oid linked = lfs_objects_project.map(&:lfs_object).map(&:oid) << new_lfs_object.oid
expect(subject.execute(new_oid_list.keys)).to eq linked expect(subject.execute(new_oid_list.keys)).to contain_exactly(*linked)
end end
it 'links in batches' do it 'links in batches' do
...@@ -48,5 +55,26 @@ describe Projects::LfsPointers::LfsLinkService do ...@@ -48,5 +55,26 @@ describe Projects::LfsPointers::LfsLinkService do
expect(project.all_lfs_objects.count).to eq 9 expect(project.all_lfs_objects.count).to eq 9
expect(linked.size).to eq 7 expect(linked.size).to eq 7
end end
it 'only queries for the batch that will be processed', :aggregate_failures do
stub_const("#{described_class}::BATCH_SIZE", 1)
oids = %w(one two)
expect(LfsObject).to receive(:where).with(oid: %w(one)).once.and_call_original
expect(LfsObject).to receive(:where).with(oid: %w(two)).once.and_call_original
subject.execute(oids)
end
it 'only queries 3 times' do
# make sure that we don't count the queries in the setup
new_oid_list
# These are repeated for each batch of oids: maximum (MAX_OIDS / BATCH_SIZE) times
# 1. Load the batch of lfs object ids that we might know already
# 2. Load the objects that have not been linked to the project yet
# 3. Insert the lfs_objects_projects for that batch
expect { subject.execute(new_oid_list.keys) }.not_to exceed_query_limit(3)
end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Users::SignupService do
let(:user) { create(:user, setup_for_company: true) }
describe '#execute' do
context 'when updating name' do
it 'updates the name attribute' do
result = update_user(user, name: 'New Name')
expect(result).to eq(status: :success)
expect(user.reload.name).to eq('New Name')
end
it 'returns an error result when name is missing' do
result = update_user(user, name: '')
expect(user.reload.name).not_to be_blank
expect(result[:status]).to eq(:error)
expect(result[:message]).to include("Name can't be blank")
end
end
context 'when updating role' do
it 'updates the role attribute' do
result = update_user(user, role: 'development_team_lead')
expect(result).to eq(status: :success)
expect(user.reload.role).to eq('development_team_lead')
end
it 'returns an error result when role is missing' do
result = update_user(user, role: '')
expect(user.reload.role).not_to be_blank
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq("Role can't be blank")
end
end
context 'when updating setup_for_company' do
it 'updates the setup_for_company attribute' do
result = update_user(user, setup_for_company: 'false')
expect(result).to eq(status: :success)
expect(user.reload.setup_for_company).to be(false)
end
it 'returns an error result when setup_for_company is missing' do
result = update_user(user, setup_for_company: '')
expect(user.reload.setup_for_company).not_to be_blank
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq("Setup for company can't be blank")
end
end
def update_user(user, opts)
described_class.new(user, opts).execute
end
end
end
...@@ -34,8 +34,15 @@ shared_examples 'authenticates sessionless user' do |path, format, params| ...@@ -34,8 +34,15 @@ shared_examples 'authenticates sessionless user' do |path, format, params|
context 'when the personal access token has no api scope', unless: params[:public] do context 'when the personal access token has no api scope', unless: params[:public] do
it 'does not log the user in' do it 'does not log the user in' do
expect(authentication_metrics) # Several instances of where these specs are shared route the request
.to increment(:user_unauthenticated_counter) # through ApplicationController#route_not_found which does not involve
# the usual auth code from Devise, so does not increment the
# :user_unauthenticated_counter
#
unless params[:ignore_incrementing]
expect(authentication_metrics)
.to increment(:user_unauthenticated_counter)
end
personal_access_token.update(scopes: [:read_user]) personal_access_token.update(scopes: [:read_user])
...@@ -84,8 +91,15 @@ shared_examples 'authenticates sessionless user' do |path, format, params| ...@@ -84,8 +91,15 @@ shared_examples 'authenticates sessionless user' do |path, format, params|
end end
it "doesn't log the user in otherwise", unless: params[:public] do it "doesn't log the user in otherwise", unless: params[:public] do
expect(authentication_metrics) # Several instances of where these specs are shared route the request
.to increment(:user_unauthenticated_counter) # through ApplicationController#route_not_found which does not involve
# the usual auth code from Devise, so does not increment the
# :user_unauthenticated_counter
#
unless params[:ignore_incrementing]
expect(authentication_metrics)
.to increment(:user_unauthenticated_counter)
end
get path, params: default_params.merge(private_token: 'token') get path, params: default_params.merge(private_token: 'token')
......
...@@ -39,7 +39,7 @@ shared_examples 'todos actions' do ...@@ -39,7 +39,7 @@ shared_examples 'todos actions' do
post_create post_create
end.to change { user.todos.count }.by(0) end.to change { user.todos.count }.by(0)
expect(response).to have_gitlab_http_status(parent.is_a?(Group) ? 401 : 302) expect(response).to have_gitlab_http_status(302)
end end
end end
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