Commit b66eb9d2 authored by Yorick Peterse's avatar Yorick Peterse

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

parents ad5bfdf3 8419f355
......@@ -20,20 +20,27 @@ v 8.7.0 (unreleased)
- Ensure empty recipients are rejected in BuildsEmailService
- API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Better errors handling when creating milestones inside groups
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- Gracefully handle notes on deleted commits in merge requests (Stan Hu)
- Fix creation of merge requests for orphaned branches (Stan Hu)
- Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
- Improved UX of the navigation sidebar
- Fix admin/projects when using visibility levels on search (PotHix)
- Build status notifications
- Update GitLab Pages to 0.2.2: fixes content-type of predefined 404 page
- API: Expose user location (Robert Schilling)
v 8.6.5 (unreleased)
- Only update repository language if it is not set to improve performance
- Check permissions when user attempts to import members from another project
- ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
- Update number of Todos in the sidebar when it's marked as "Done". !3600
v 8.6.5
- Fix importing from GitHub Enterprise. !3529
- Perform the language detection after updating merge requests in `GitPushService`, leading to faster visual feedback for the end-user. !3533
- Check permissions when user attempts to import members from another project. !3535
- Only update repository language if it is not set to improve performance. !3556
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu). !3583
- Unblock user when active_directory is disabled and it can be found !3550
- Fix a 2FA authentication spoofing vulnerability.
v 8.6.4
- Don't attempt to fetch any tags from a forked repo (Stan Hu)
......@@ -153,6 +160,9 @@ v 8.6.0
- Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.10
- Fix a 2FA authentication spoofing vulnerability.
v 8.5.9
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
......@@ -315,6 +325,9 @@ v 8.5.0
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- Add Todos
v 8.4.8
- Fix a 2FA authentication spoofing vulnerability.
v 8.4.7
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
......@@ -440,6 +453,9 @@ v 8.4.0
- Add IP check against DNSBLs at account sign-up
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
v 8.3.7
- Fix a 2FA authentication spoofing vulnerability.
v 8.3.6
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
......
......@@ -4,6 +4,7 @@ expanded = 'page-sidebar-expanded'
toggleSidebar = ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
setTimeout ( ->
......
......@@ -33,15 +33,10 @@
background: $color;
}
.complex-sidebar .nav-primary {
border-right: 1px solid lighten($color, 3%);
}
.sidebar-wrapper {
background: $color-darker;
.sidebar-user {
border-top: 1px solid lighten($color, 3%);
background: $color-darker;
color: $color-light;
......@@ -67,6 +62,7 @@
.count {
color: $color-light;
background: $color-dark;
}
}
......
......@@ -123,11 +123,11 @@ header {
}
@mixin collapsed-header {
margin-left: 40px;
margin-left: $sidebar_collapsed_width;
}
.header-collapsed {
margin-left: 40px;
margin-left: $sidebar_collapsed_width;
@media (min-width: $screen-md-min) {
@include collapsed-header;
......
......@@ -144,7 +144,7 @@
}
a {
padding: 7px 12px;
padding: 7px 15px;
font-size: $gl-font-size;
line-height: 24px;
color: $gray;
......@@ -169,12 +169,10 @@
}
.count {
&:before {
content: '(';
}
&:after {
content: ')';
}
float: right;
background: #eee;
padding: 0 8px;
@include border-radius(6px);
}
&.back-link i {
......@@ -193,27 +191,6 @@
}
}
.expand-nav a {
color: $gl-icon-color;
width: 60px;
position: fixed;
top: 0;
left: 0;
font-size: 20px;
background: #fff;
height: 59px;
text-align: center;
line-height: 59px;
border-bottom: 1px solid #eee;
transition-duration: .3s;
outline: none;
z-index: 100;
&:hover {
text-decoration: none;
}
}
.collapse-nav a {
width: $sidebar_width;
position: fixed;
......@@ -233,12 +210,55 @@
}
.page-sidebar-collapsed {
padding-left: $sidebar_collapsed_width;
.sidebar-wrapper {
display: none;
width: $sidebar_collapsed_width;
.header-logo {
width: $sidebar_collapsed_width;
a {
padding-left: ($sidebar_collapsed_width - 36) / 2;
.gitlab-text-container {
display: none;
}
}
}
.nav-sidebar {
width: $sidebar_collapsed_width;
li {
width: auto;
a {
span {
display: none;
}
}
}
}
.collapse-nav a {
width: $sidebar_collapsed_width;
}
.sidebar-user {
padding-left: ($sidebar_collapsed_width - 36) / 2;
width: $sidebar_collapsed_width;
.username {
display: none;
}
}
}
}
.page-sidebar-expanded {
padding-left: $sidebar_collapsed_width;
@media (min-width: $screen-md-min) {
padding-left: $sidebar_width;
}
......@@ -289,48 +309,3 @@
padding-right: $sidebar_collapsed_width;
}
}
.complex-sidebar {
display: inline-block;
.nav-primary {
width: 61px;
float: left;
height: 100vh;
.nav-sidebar {
width: 60px;
li a {
width: 60px;
span {
display: none;
}
}
}
}
.nav-secondary {
$nav-secondary-width: 168px;
float: left;
width: $nav-secondary-width;
.nav-sidebar {
width: $nav-secondary-width;
li {
width: $nav-secondary-width;
a {
width: $nav-secondary-width;
i {
display: none;
}
}
}
}
}
}
......@@ -20,6 +20,8 @@
margin: 0;
padding: 0;
margin-top: 10px;
word-break: normal;
white-space: pre-wrap;
}
.commit-info-row {
......
......@@ -63,10 +63,10 @@
.line_holder td {
line-height: $code_line_height;
font-size: $code_font_size;
}
td {
white-space: nowrap;
span {
white-space: pre;
}
}
}
......@@ -109,6 +109,10 @@
display: table-cell;
}
}
.text-file.diff-wrap-lines table .line_holder td span {
white-space: pre-wrap;
}
}
.image {
background: #ddd;
......
......@@ -18,14 +18,14 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def create
project_ids = params[:milestone][:project_ids]
project_ids = params[:milestone][:project_ids].reject(&:blank?)
title = milestone_params[:title]
@projects.where(id: project_ids).each do |project|
Milestones::CreateService.new(project, current_user, milestone_params).execute
if create_milestones(project_ids)
redirect_to milestone_path(title)
else
render_new_with_error(project_ids.empty?)
end
redirect_to milestone_path(title)
end
def show
......@@ -41,6 +41,27 @@ class Groups::MilestonesController < Groups::ApplicationController
private
def create_milestones(project_ids)
return false unless project_ids.present?
ActiveRecord::Base.transaction do
@projects.where(id: project_ids).each do |project|
Milestones::CreateService.new(project, current_user, milestone_params).execute
end
end
true
rescue ActiveRecord::ActiveRecordError => e
flash.now[:alert] = "An error occurred while creating the milestone: #{e.message}"
false
end
def render_new_with_error(empty_project_ids)
@milestone = Milestone.new(milestone_params)
@milestone.errors.add(:project_id, "Please select at least one project.") if empty_project_ids
render :new
end
def authorize_admin_milestones!
return render_404 unless can?(current_user, :admin_milestones, group)
end
......
......@@ -5,7 +5,8 @@ class SessionsController < Devise::SessionsController
skip_before_action :check_2fa_requirement, only: [:destroy]
prepend_before_action :check_initial_setup, only: [:new]
prepend_before_action :authenticate_with_two_factor, only: [:create]
prepend_before_action :authenticate_with_two_factor,
if: :two_factor_enabled?, only: [:create]
prepend_before_action :store_redirect_path, only: [:new]
before_action :gitlab_geo_login, only: [:new]
before_action :auto_sign_in_with_provider, only: [:new]
......@@ -56,10 +57,10 @@ class SessionsController < Devise::SessionsController
end
def find_user
if user_params[:login]
User.by_login(user_params[:login])
elsif user_params[:otp_attempt] && session[:otp_user_id]
if session[:otp_user_id]
User.find(session[:otp_user_id])
elsif user_params[:login]
User.by_login(user_params[:login])
end
end
......@@ -85,11 +86,13 @@ class SessionsController < Devise::SessionsController
store_location_for(:redirect, redirect_to)
end
def two_factor_enabled?
find_user.try(:two_factor_enabled?)
end
def authenticate_with_two_factor
user = self.resource = find_user
return unless user && user.two_factor_enabled?
if user_params[:otp_attempt].present? && session[:otp_user_id]
if valid_otp_attempt?(user)
# Remove any lingering user data from login
......
......@@ -369,6 +369,8 @@ class Repository
# Runs code after a repository has been created.
def after_create
expire_exists_cache
expire_root_ref_cache
expire_emptiness_caches
end
# Runs code just before a repository is deleted.
......
......@@ -3,7 +3,7 @@ module Milestones
def execute
milestone = project.milestones.new(params)
if milestone.save
if milestone.save!
event_service.open_milestone(milestone, current_user)
end
......
......@@ -10,6 +10,14 @@
= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input' } do |f|
.row
- if @milestone.errors.any?
#error_explanation
.alert.alert-danger
%ul
- @milestone.errors.full_messages.each do |msg|
%li
= msg
.col-md-6
.form-group
= f.label :title, "Title", class: "control-label"
......
- if nav_menu_collapsed?
= link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
- else
= link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
= render "layouts/broadcast"
.expand-nav
= link_to icon('bars'), '#', class: 'toggle-nav-collapse', title: "Open sidebar"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
%a#logo
......@@ -10,19 +8,15 @@
.gitlab-text-container
%h3 GitLab
- primary_sidebar = current_user ? 'dashboard' : 'explore'
- if defined?(sidebar) && sidebar && sidebar != primary_sidebar
.complex-sidebar
.nav-primary
= render "layouts/nav/#{primary_sidebar}"
.nav-secondary
= render "layouts/nav/#{sidebar}"
- if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}"
- elsif current_user
= render 'layouts/nav/dashboard'
- else
= render "layouts/nav/#{primary_sidebar}"
= render 'layouts/nav/explore'
.collapse-nav
= link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Hide sidebar"
= render partial: 'layouts/collapse_button'
- if current_user
= link_to current_user, class: 'sidebar-user', title: "Profile" do
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
......
......@@ -106,7 +106,7 @@
Spam Logs
%span.count= number_with_delimiter(SpamLog.count(:all))
= nav_link(controller: :application_settings) do
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do
= icon('cogs fw')
%span
......
......@@ -9,18 +9,18 @@
= icon('bell fw')
%span
Todos
%span.count= number_with_delimiter(todos_pending_count)
%span.count.todos-pending-count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity' do
= icon('dashboard fw')
%span
Activity
= nav_link(path: ['dashboard/groups#index', 'explore/groups#index']) do
= nav_link(controller: :groups) do
= link_to dashboard_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
= nav_link(path: 'dashboard#milestones') do
= nav_link(controller: :milestones) do
= link_to dashboard_milestones_path, title: 'Milestones' do
= icon('clock-o fw')
%span
......@@ -48,6 +48,7 @@
%span
Help
%li.separate-item
= nav_link(controller: :profile) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
= icon('user fw')
......
%ul.nav.nav-sidebar
= nav_link do
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
%li.separate-item
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do
= icon('group fw')
......@@ -39,7 +47,7 @@
%span
Contribution Analytics
- if can?(current_user, :admin_group, @group)
= nav_link do
= nav_link(html_options: { class: "separate-item" }) do
= link_to edit_group_path(@group), title: 'Settings' do
= icon ('cogs fw')
%span
......
%ul.nav.nav-sidebar
= nav_link do
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
%li.separate-item
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do
= icon('user fw')
......
%ul.nav.nav-sidebar
- if @project.group
= nav_link do
= link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to group
- else
= nav_link do
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
%li.separate-item
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= icon('bookmark fw')
......@@ -98,7 +113,7 @@
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class}"}) do
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), title: 'Settings' do
= icon('cogs fw')
%span
......
%aside.right-sidebar{ class: sidebar_gutter_collapsed_class }
.issuable-sidebar
- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.block.issuable-sidebar-header
%span.issuable-count.hide-collapsed.pull-left
= issuable.iid
......@@ -29,7 +30,7 @@
.title.hide-collapsed
Assignee
= icon('spinner spin', class: 'block-loading')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.bold.hide-collapsed
- if issuable.assignee
......@@ -41,9 +42,10 @@
= issuable.assignee.to_reference
- else
%span.assign-yourself
No assignee -
%a.js-assign-yourself{ href: '#' }
assign yourself
No assignee
- if can_edit_issuable
%a.js-assign-yourself{ href: '#' }
\- assign yourself
.selectbox.hide-collapsed
= f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id'
......@@ -60,7 +62,7 @@
.title.hide-collapsed
Milestone
= icon('spinner spin', class: 'block-loading')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.bold.hide-collapsed
- if issuable.milestone
......@@ -82,7 +84,7 @@
.title.hide-collapsed
Labels
= icon('spinner spin', class: 'block-loading')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels.any?) }
- if issuable.labels.any?
......
......@@ -80,7 +80,7 @@ production: &base
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com.
# issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)'
# issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)'
## Default project features settings
default_projects_features:
......
......@@ -235,7 +235,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
......
......@@ -290,9 +290,13 @@ sudo usermod -aG redis git
# Copy the example Rack attack config
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
# Configure Git global settings for git user, used when editing via web editor
# Configure Git global settings for git user
# 'autocrlf' is needed for the web editor
sudo -u git -H git config --global core.autocrlf input
# Disable 'git gc --auto' because GitLab already runs 'git gc' when needed
sudo -u git -H git config --global gc.auto 0
# Configure Redis connection settings
sudo -u git -H cp config/resque.yml.example config/resque.yml
......
......@@ -86,6 +86,14 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
### 7. Update configuration files
#### Git configuration
Disable `git gc --auto` because GitLab runs `git gc` for us already.
```sh
sudo -u git -H git config --global gc.auto 0
```
#### Nginx configuration
Ensure you're still up-to-date with the latest NGINX configuration changes:
......
......@@ -7,6 +7,10 @@ Feature: Groups
When I visit group "NonExistentGroup" page
Then page status code should be 404
Scenario: I should have back to group button
When I visit group "Owned" page
Then I should see back to dashboard button
@javascript
Scenario: I should see group "Owned" dashboard list
When I visit group "Owned" page
......
......@@ -30,6 +30,15 @@ Feature: Project
When I enable project issues
Then I should see the issues settings
Scenario: I should have back to group button
And project "Shop" belongs to group
And I visit project "Shop" page
Then I should see back to group button
Scenario: I should have back to group button
And I visit project "Shop" page
Then I should see back to dashboard button
Scenario: I should have readme on page
And I visit project "Shop" page
Then I should see project "Shop" README
......
......@@ -26,6 +26,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
end
step 'I should see todos assigned to me' do
page.within('.nav-sidebar') { expect(page).to have_content 'Todos 4' }
expect(page).to have_content 'To do 4'
expect(page).to have_content 'Done 0'
......@@ -41,6 +42,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
click_link 'Done'
end
page.within('.nav-sidebar') { expect(page).to have_content 'Todos 3' }
expect(page).to have_content 'To do 3'
expect(page).to have_content 'Done 1'
should_not_see_todo "John Doe assigned you merge request !#{merge_request.iid}"
......
......@@ -5,9 +5,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
include SharedUser
step 'I click on group milestones' do
page.within '.nav-secondary' do
click_link("Milestones")
end
click_link 'Milestones'
end
step 'I should see group milestones index page has no milestones' do
......
......@@ -4,6 +4,10 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
include SharedGroup
include SharedUser
step 'I should see back to dashboard button' do
expect(page).to have_content 'Go to dashboard'
end
step 'I should see group "Owned"' do
expect(page).to have_content '@owned'
end
......
......@@ -90,9 +90,7 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
# Sub Tabs: Issues
step 'I click the "Milestones" tab' do
page.within '.nav-secondary' do
click_link('Milestones')
end
click_link('Milestones')
end
step 'I click the "Labels" tab' do
......
......@@ -36,7 +36,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
end
step 'I goto the Merge Requests page' do
page.within '.nav-secondary' do
page.within '.page-sidebar-expanded' do
click_link "Merge Requests"
end
end
......
......@@ -139,9 +139,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should not see "Snippets" button' do
page.within '.nav-secondary' do
expect(page).not_to have_link 'Snippets'
end
expect(page).not_to have_link 'Snippets'
end
step 'project "Shop" belongs to group' do
......@@ -150,6 +148,14 @@ class Spinach::Features::Project < Spinach::FeatureSteps
@project.save!
end
step 'I should see back to dashboard button' do
expect(page).to have_content 'Go to dashboard'
end
step 'I should see back to group button' do
expect(page).to have_content 'Go to group'
end
step 'I click notifications drop down button' do
click_link 'notifications-button'
end
......
......@@ -41,7 +41,7 @@ module SharedProjectTab
end
step 'the active main tab should be Settings' do
page.within '.nav-secondary' do
page.within '.nav-sidebar' do
expect(page).to have_content('Go to project')
end
end
......
......@@ -23,5 +23,11 @@ describe Groups::MilestonesController do
expect(response).to redirect_to(group_milestone_path(group, title.to_slug.to_s, title: title))
expect(Milestone.where(title: title).count).to eq(2)
end
it "redirects to new when there are no project ids" do
post :create, group_id: group.id, milestone: { title: title, project_ids: [""] }
expect(response).to render_template :new
expect(assigns(:milestone).errors).not_to be_nil
end
end
end
require 'spec_helper'
describe SessionsController do
describe '#create' do
before do
@request.env['devise.mapping'] = Devise.mappings[:user]
end
context 'when using standard authentications' do
context 'invalid password' do
it 'does not authenticate user' do
post(:create, user: { login: 'invalid', password: 'invalid' })
expect(response)
.to set_flash.now[:alert].to /Invalid login or password/
end
end
context 'when using valid password' do
let(:user) { create(:user) }
it 'authenticates user correctly' do
post(:create, user: { login: user.username, password: user.password })
expect(response).to set_flash.to /Signed in successfully/
expect(subject.current_user). to eq user
end
end
end
context 'when using two-factor authentication' do
let(:user) { create(:user, :two_factor) }
def authenticate_2fa(user_params)
post(:create, { user: user_params }, { otp_user_id: user.id })
end
##
# See #14900 issue
#
context 'when authenticating with login and OTP of another user' do
context 'when another user has 2FA enabled' do
let(:another_user) { create(:user, :two_factor) }
context 'when OTP is valid for another user' do
it 'does not authenticate' do
authenticate_2fa(login: another_user.username,
otp_attempt: another_user.current_otp)
expect(subject.current_user).to_not eq another_user
end
end
context 'when OTP is invalid for another user' do
it 'does not authenticate' do
authenticate_2fa(login: another_user.username,
otp_attempt: 'invalid')
expect(subject.current_user).to_not eq another_user
end
end
context 'when authenticating with OTP' do
context 'when OTP is valid' do
it 'authenticates correctly' do
authenticate_2fa(otp_attempt: user.current_otp)
expect(subject.current_user).to eq user
end
end
context 'when OTP is invalid' do
before { authenticate_2fa(otp_attempt: 'invalid') }
it 'does not authenticate' do
expect(subject.current_user).to_not eq user
end
it 'warns about invalid OTP code' do
expect(response).to set_flash.now[:alert]
.to /Invalid two-factor code/
end
end
end
context 'when another user does not have 2FA enabled' do
let(:another_user) { create(:user) }
it 'does not leak that 2FA is disabled for another user' do
authenticate_2fa(login: another_user.username,
otp_attempt: 'invalid')
expect(response).to set_flash.now[:alert]
.to /Invalid two-factor code/
end
end
end
end
end
end
end
......@@ -22,11 +22,21 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (Closes: #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (closes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (closes: #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Closed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
......@@ -37,106 +47,211 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "closed: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Closing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Closing: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "closing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "closing: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Close #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Close: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "close #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "close: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (Fixes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (Fixes: #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (fixes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (Fixes: #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Fixed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Fixed: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "fixed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "fixed: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Fixing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Fixing: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "fixing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "fixing: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Fix #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Fix: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "fix #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "fix: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (Resolves #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (Resolves: #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (resolves #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Awesome commit (resolves: #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Resolved #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Resolved: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "resolved #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "resolved: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Resolving #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Resolving: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "resolving #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "resolving: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Resolve #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "Resolve: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "resolve #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
message = "resolve: #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
......
......@@ -716,6 +716,19 @@ describe Repository, models: true do
repository.after_create
end
it 'flushes the root ref cache' do
expect(repository).to receive(:expire_root_ref_cache)
repository.after_create
end
it 'flushes the emptiness caches' do
expect(repository).to receive(:expire_emptiness_caches)
repository.after_create
end
end
describe "#main_language" do
......
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