Commit fcaa2c25 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'ce-upstream' into 'master'

Merge CE 73ead4 to EE



See merge request !403
parents 81ecb9f4 2aeb5de5
...@@ -5,6 +5,7 @@ v 8.8.0 (unreleased) ...@@ -5,6 +5,7 @@ v 8.8.0 (unreleased)
- Fix error when using link to uploads in global snippets - Fix error when using link to uploads in global snippets
- Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen) - Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen)
- Use a case-insensitive comparison in sanitizing URI schemes - Use a case-insensitive comparison in sanitizing URI schemes
- Toggle sign-up confirmation emails in application settings
- Project#open_branches has been cleaned up and no longer loads entire records into memory. - Project#open_branches has been cleaned up and no longer loads entire records into memory.
- Escape HTML in commit titles in system note messages - Escape HTML in commit titles in system note messages
- Improve multiple branch push performance by memoizing permission checking - Improve multiple branch push performance by memoizing permission checking
...@@ -15,6 +16,7 @@ v 8.8.0 (unreleased) ...@@ -15,6 +16,7 @@ v 8.8.0 (unreleased)
- Make build status canceled if any of the jobs was canceled and none failed - Make build status canceled if any of the jobs was canceled and none failed
- Upgrade Sidekiq to 4.1.2 - Upgrade Sidekiq to 4.1.2
- Added /health_check endpoint for checking service status - Added /health_check endpoint for checking service status
- Make 'upcoming' filter for milestones work better across projects
- Sanitize repo paths in new project error message - Sanitize repo paths in new project error message
- Bump mail_room to 0.7.0 to fix stuck IDLE connections - Bump mail_room to 0.7.0 to fix stuck IDLE connections
- Remove future dates from contribution calendar graph. - Remove future dates from contribution calendar graph.
...@@ -25,9 +27,11 @@ v 8.8.0 (unreleased) ...@@ -25,9 +27,11 @@ v 8.8.0 (unreleased)
- Update SVG sanitizer to conform to SVG 1.1 - Update SVG sanitizer to conform to SVG 1.1
- Speed up push emails with multiple recipients by only generating the email once - Speed up push emails with multiple recipients by only generating the email once
- Updated search UI - Updated search UI
- Added authentication service for Container Registry
- Display informative message when new milestone is created - Display informative message when new milestone is created
- Sanitize milestones and labels titles - Sanitize milestones and labels titles
- Support multi-line tag messages. !3833 (Calin Seciu) - Support multi-line tag messages. !3833 (Calin Seciu)
- Force users to reset their password after an admin changes it
- Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
- Added button to toggle whitespaces changes on diff view - Added button to toggle whitespaces changes on diff view
- Backport GitHub Enterprise import support from EE - Backport GitHub Enterprise import support from EE
...@@ -43,7 +47,7 @@ v 8.8.0 (unreleased) ...@@ -43,7 +47,7 @@ v 8.8.0 (unreleased)
- API support for the 'since' and 'until' operators on commit requests (Paco Guzman) - API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
- Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko) - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
- Expire repository exists? and has_visible_content? caches after a push if necessary - Expire repository exists? and has_visible_content? caches after a push if necessary
- Fix unintentional filtering bug in issues sorted by milestone due (Takuya Noguchi) - Fix unintentional filtering bug in Issue/MR sorted by milestone due (Takuya Noguchi)
- Fix adding a todo for private group members (Ahmad Sherif) - Fix adding a todo for private group members (Ahmad Sherif)
- Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3 - Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3
- Total method execution timings are no longer tracked - Total method execution timings are no longer tracked
...@@ -51,6 +55,12 @@ v 8.8.0 (unreleased) ...@@ -51,6 +55,12 @@ v 8.8.0 (unreleased)
- Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif) - Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif)
- Hide left sidebar on phone screens to give more space for content - Hide left sidebar on phone screens to give more space for content
- Redesign navigation for profile and group pages - Redesign navigation for profile and group pages
- Add counter metrics for rails cache
- Import pull requests from GitHub where the source or target branches were removed
- All Grape API helpers are now instrumented
v 8.7.6
- Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
v 8.7.5 v 8.7.5
- Fix relative links in wiki pages. !4050 - Fix relative links in wiki pages. !4050
......
...@@ -37,6 +37,7 @@ gem 'omniauth-twitter', '~> 1.2.0' ...@@ -37,6 +37,7 @@ gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
gem 'gssapi', group: :kerberos gem 'gssapi', group: :kerberos
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt'
# Spam and anti-bot protection # Spam and anti-bot protection
gem 'recaptcha', require: 'recaptcha/rails' gem 'recaptcha', require: 'recaptcha/rails'
...@@ -234,7 +235,9 @@ gem 'request_store', '~> 1.3.0' ...@@ -234,7 +235,9 @@ gem 'request_store', '~> 1.3.0'
gem 'select2-rails', '~> 3.5.9' gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1' gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1' gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0'
gem "gitlab-license", "~> 0.0.4" gem "gitlab-license", "~> 0.0.4"
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 0.15' gem 'sentry-raven', '~> 0.15'
......
...@@ -70,6 +70,7 @@ GEM ...@@ -70,6 +70,7 @@ GEM
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
babosa (1.0.2) babosa (1.0.2)
base32 (0.3.2)
bcrypt (3.1.10) bcrypt (3.1.10)
benchmark-ips (2.3.0) benchmark-ips (2.3.0)
better_errors (1.0.1) better_errors (1.0.1)
...@@ -920,6 +921,7 @@ DEPENDENCIES ...@@ -920,6 +921,7 @@ DEPENDENCIES
attr_encrypted (~> 1.3.4) attr_encrypted (~> 1.3.4)
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0)
benchmark-ips benchmark-ips
better_errors (~> 1.0.1) better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
...@@ -986,6 +988,7 @@ DEPENDENCIES ...@@ -986,6 +988,7 @@ DEPENDENCIES
jquery-rails (~> 4.1.0) jquery-rails (~> 4.1.0)
jquery-turbolinks (~> 2.1.0) jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
jwt
kaminari (~> 0.16.3) kaminari (~> 0.16.3)
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
licensee (~> 8.0.0) licensee (~> 8.0.0)
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) { @mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
.page-with-sidebar { .page-with-sidebar {
.header-logo { .header-logo {
background: $color-darker;
a { a {
color: $color-light; color: $color-light;
......
...@@ -108,6 +108,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -108,6 +108,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:email_author_in_body, :email_author_in_body,
:repository_checks_enabled, :repository_checks_enabled,
:metrics_packet_size, :metrics_packet_size,
:send_user_confirmation_email,
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [], import_sources: [],
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: []
......
...@@ -119,6 +119,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -119,6 +119,7 @@ class Admin::UsersController < Admin::ApplicationController
user_params_with_pass.merge!( user_params_with_pass.merge!(
password: params[:user][:password], password: params[:user][:password],
password_confirmation: params[:user][:password_confirmation], password_confirmation: params[:user][:password_confirmation],
password_expires_at: Time.now
) )
end end
......
class JwtController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
before_action :authenticate_project_or_user
SERVICES = {
Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService,
}
def auth
service = SERVICES[params[:service]]
return head :not_found unless service
result = service.new(@project, @user, auth_params).execute
render json: result, status: result[:http_status]
end
private
def authenticate_project_or_user
authenticate_with_http_basic do |login, password|
# if it's possible we first try to authenticate project with login and password
@project = authenticate_project(login, password)
return if @project
@user = authenticate_user(login, password)
return if @user
render_403
end
end
def auth_params
params.permit(:service, :scope, :offline_token, :account, :client_id)
end
def authenticate_project(login, password)
if login == 'gitlab_ci_token'
Project.find_by(builds_enabled: true, runners_token: password)
end
end
def authenticate_user(login, password)
# TODO: this is a copy and paste from grack_auth,
# it should be refactored in the future
user = Gitlab::Auth.new.find(login, password)
# If the user authenticated successfully, we reset the auth failure count
# from Rack::Attack for that IP. A client may attempt to authenticate
# with a username and blank password first, and only after it receives
# a 401 error does it present a password. Resetting the count prevents
# false positives from occurring.
#
# Otherwise, we let Rack::Attack know there was a failed authentication
# attempt from this IP. This information is stored in the Rails cache
# (Redis) and will be used by the Rack::Attack middleware to decide
# whether to block requests from this IP.
config = Gitlab.config.rack_attack.git_basic_auth
if config.enabled
if user
# A successful login will reset the auth failure count from this IP
Rack::Attack::Allow2Ban.reset(request.ip, config)
else
banned = Rack::Attack::Allow2Ban.filter(request.ip, config) do
# Unless the IP is whitelisted, return true so that Allow2Ban
# increments the counter (stored in Rails.cache) for the IP
if config.ip_whitelist.include?(request.ip)
false
else
true
end
end
if banned
Rails.logger.info "IP #{request.ip} failed to login " \
"as #{login} but has been temporarily banned from Git auth"
return
end
end
end
user
end
end
...@@ -236,7 +236,8 @@ class ProjectsController < Projects::ApplicationController ...@@ -236,7 +236,8 @@ class ProjectsController < Projects::ApplicationController
def project_params def project_params
params.require(:project).permit( params.require(:project).permit(
:name, :path, :description, :issues_tracker, :tag_list, :runners_token, :name, :path, :description, :issues_tracker, :tag_list, :runners_token,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :container_registry_enabled,
:issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds, :public_builds,
......
...@@ -37,8 +37,8 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -37,8 +37,8 @@ class RegistrationsController < Devise::RegistrationsController
super super
end end
def after_sign_up_path_for(_resource) def after_sign_up_path_for(user)
users_almost_there_path user.confirmed_at.present? ? dashboard_projects_path : users_almost_there_path
end end
def after_inactive_sign_up_path_for(_resource) def after_inactive_sign_up_path_for(_resource)
......
...@@ -253,8 +253,8 @@ class IssuableFinder ...@@ -253,8 +253,8 @@ class IssuableFinder
if filter_by_no_milestone? if filter_by_no_milestone?
items = items.where(milestone_id: [-1, nil]) items = items.where(milestone_id: [-1, nil])
elsif filter_by_upcoming_milestone? elsif filter_by_upcoming_milestone?
upcoming = Milestone.where(project_id: projects).upcoming upcoming_ids = Milestone.upcoming_ids_by_projects(projects)
items = items.joins(:milestone).where(milestones: { title: upcoming.try(:title) }) items = items.joins(:milestone).where(milestone_id: upcoming_ids)
else else
items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] }) items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] })
......
...@@ -3,7 +3,7 @@ module EventsHelper ...@@ -3,7 +3,7 @@ module EventsHelper
author = event.author author = event.author
if author if author
link_to author.name, user_path(author.username), title: h(author.name) link_to author.name, user_path(author.username), title: author.name
else else
event.author_name event.author_name
end end
...@@ -57,11 +57,7 @@ module EventsHelper ...@@ -57,11 +57,7 @@ module EventsHelper
words << event.ref_name words << event.ref_name
words << "at" words << "at"
elsif event.commented? elsif event.commented?
if event.note_commit? words << event.note_target_reference
words << event.note_short_commit_id
else
words << "##{truncate event.note_target_iid}"
end
words << "at" words << "at"
elsif event.milestone? elsif event.milestone?
words << "##{event.target_iid}" if event.target_iid words << "##{event.target_iid}" if event.target_iid
...@@ -84,22 +80,13 @@ module EventsHelper ...@@ -84,22 +80,13 @@ module EventsHelper
elsif event.merge_request? elsif event.merge_request?
namespace_project_merge_request_url(event.project.namespace, namespace_project_merge_request_url(event.project.namespace,
event.project, event.merge_request) event.project, event.merge_request)
elsif event.note? && event.note_commit? elsif event.note? && event.commit_note?
namespace_project_commit_url(event.project.namespace, event.project, namespace_project_commit_url(event.project.namespace, event.project,
event.note_target) event.note_target)
elsif event.note? elsif event.note?
if event.note_target if event.note_target
if event.note_commit?
namespace_project_commit_path(event.project.namespace, event.project,
event.note_commit_id,
anchor: dom_id(event.target))
elsif event.note_project_snippet?
namespace_project_snippet_path(event.project.namespace,
event.project, event.note_target)
else
event_note_target_path(event) event_note_target_path(event)
end end
end
elsif event.push? elsif event.push?
push_event_feed_url(event) push_event_feed_url(event)
end end
...@@ -134,9 +121,16 @@ module EventsHelper ...@@ -134,9 +121,16 @@ module EventsHelper
end end
def event_note_target_path(event) def event_note_target_path(event)
if event.note? && event.note_commit? if event.note? && event.commit_note?
namespace_project_commit_path(event.project.namespace, event.project, namespace_project_commit_path(event.project.namespace,
event.note_target) event.project,
event.note_target,
anchor: dom_id(event.target))
elsif event.project_snippet_note?
namespace_project_snippet_path(event.project.namespace,
event.project,
event.note_target,
anchor: dom_id(event.target))
else else
polymorphic_path([event.project.namespace.becomes(Namespace), polymorphic_path([event.project.namespace.becomes(Namespace),
event.project, event.note_target], event.project, event.note_target],
...@@ -146,30 +140,11 @@ module EventsHelper ...@@ -146,30 +140,11 @@ module EventsHelper
def event_note_title_html(event) def event_note_title_html(event)
if event.note_target if event.note_target
if event.note_commit? link_to(event_note_target_path(event), title: event.target_title, class: 'has-tooltip') do
link_to( "#{event.note_target_type} #{event.note_target_reference}"
namespace_project_commit_path(event.project.namespace, event.project,
event.note_commit_id,
anchor: dom_id(event.target), title: h(event.target_title)),
class: "commit_short_id"
) do
"#{event.note_target_type} #{event.note_short_commit_id}"
end
elsif event.note_project_snippet?
link_to(namespace_project_snippet_path(event.project.namespace,
event.project,
event.note_target), title: h(event.project.name)) do
"#{event.note_target_type} #{truncate event.note_target.to_reference}"
end end
else else
link_to event_note_target_path(event) do content_tag(:strong, '(deleted)')
"#{event.note_target_type} #{truncate event.note_target.to_reference}"
end
end
else
content_tag :strong do
"(deleted)"
end
end end
end end
......
...@@ -75,6 +75,7 @@ class Ability ...@@ -75,6 +75,7 @@ class Ability
:read_merge_request, :read_merge_request,
:read_note, :read_note,
:read_commit_status, :read_commit_status,
:read_container_image,
:download_code :download_code
] ]
...@@ -217,6 +218,7 @@ class Ability ...@@ -217,6 +218,7 @@ class Ability
:admin_label, :admin_label,
:read_commit_status, :read_commit_status,
:read_build, :read_build,
:read_container_image,
] ]
end end
...@@ -230,7 +232,9 @@ class Ability ...@@ -230,7 +232,9 @@ class Ability
:update_build, :update_build,
:create_merge_request, :create_merge_request,
:create_wiki, :create_wiki,
:push_code :push_code,
:create_container_image,
:update_container_image,
] ]
end end
...@@ -259,6 +263,7 @@ class Ability ...@@ -259,6 +263,7 @@ class Ability
:admin_project, :admin_project,
:admin_commit_status, :admin_commit_status,
:admin_build, :admin_build,
:admin_container_image,
:admin_pages, :admin_pages,
] ]
end end
...@@ -305,6 +310,10 @@ class Ability ...@@ -305,6 +310,10 @@ class Ability
rules += named_abilities('build') rules += named_abilities('build')
end end
unless project.container_registry_enabled
rules += named_abilities('container_image')
end
rules rules
end end
......
...@@ -120,7 +120,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -120,7 +120,8 @@ class ApplicationSetting < ActiveRecord::Base
recaptcha_enabled: false, recaptcha_enabled: false,
akismet_enabled: false, akismet_enabled: false,
repository_checks_enabled: true, repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false
) )
end end
......
...@@ -86,7 +86,7 @@ class Event < ActiveRecord::Base ...@@ -86,7 +86,7 @@ class Event < ActiveRecord::Base
end end
def target_title def target_title
target.title if target && target.respond_to?(:title) target.try(:title)
end end
def created? def created?
...@@ -272,28 +272,20 @@ class Event < ActiveRecord::Base ...@@ -272,28 +272,20 @@ class Event < ActiveRecord::Base
branch? && project.default_branch != branch_name branch? && project.default_branch != branch_name
end end
def note_commit_id
target.commit_id
end
def target_iid def target_iid
target.respond_to?(:iid) ? target.iid : target_id target.respond_to?(:iid) ? target.iid : target_id
end end
def note_short_commit_id def commit_note?
Commit.truncate_sha(note_commit_id) target.for_commit?
end
def note_commit?
target.noteable_type == "Commit"
end end
def issue_note? def issue_note?
note? && target && target.noteable_type == "Issue" note? && target && target.for_issue?
end end
def note_project_snippet? def project_snippet_note?
target.noteable_type == "Snippet" target.for_snippet?
end end
def note_target def note_target
...@@ -301,19 +293,22 @@ class Event < ActiveRecord::Base ...@@ -301,19 +293,22 @@ class Event < ActiveRecord::Base
end end
def note_target_id def note_target_id
if note_commit? if commit_note?
target.commit_id target.commit_id
else else
target.noteable_id.to_s target.noteable_id.to_s
end end
end end
def note_target_iid def note_target_reference
if note_target.respond_to?(:iid) return unless note_target
note_target.iid
# Commit#to_reference returns the full SHA, but we want the short one here
if commit_note?
note_target.short_id
else else
note_target_id note_target.to_reference
end.to_s end
end end
def note_target_type def note_target_type
......
...@@ -29,6 +29,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -29,6 +29,10 @@ class MergeRequest < ActiveRecord::Base
# when creating new merge request # when creating new merge request
attr_accessor :can_be_created, :compare_commits, :compare attr_accessor :can_be_created, :compare_commits, :compare
# Temporary fields to store target_sha, and base_sha to
# compare when importing pull requests from GitHub
attr_accessor :base_target_sha, :head_source_sha
state_machine :state, initial: :opened do state_machine :state, initial: :opened do
event :close do event :close do
transition [:reopened, :opened] => :closed transition [:reopened, :opened] => :closed
...@@ -546,10 +550,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -546,10 +550,14 @@ class MergeRequest < ActiveRecord::Base
end end
def target_sha def target_sha
@target_sha ||= target_project.repository.commit(target_branch).try(:sha) return @base_target_sha if defined?(@base_target_sha)
target_project.repository.commit(target_branch).try(:sha)
end end
def source_sha def source_sha
return @head_source_sha if defined?(@head_source_sha)
last_commit.try(:sha) || source_tip.try(:sha) last_commit.try(:sha) || source_tip.try(:sha)
end end
......
...@@ -6,7 +6,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -6,7 +6,7 @@ class MergeRequestDiff < ActiveRecord::Base
belongs_to :merge_request belongs_to :merge_request
delegate :target_branch, :source_branch, to: :merge_request, prefix: nil delegate :head_source_sha, :target_branch, :source_branch, to: :merge_request, prefix: nil
state_machine :state, initial: :empty do state_machine :state, initial: :empty do
state :collected state :collected
...@@ -38,8 +38,8 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -38,8 +38,8 @@ class MergeRequestDiff < ActiveRecord::Base
@diffs_no_whitespace ||= begin @diffs_no_whitespace ||= begin
compare = Gitlab::Git::Compare.new( compare = Gitlab::Git::Compare.new(
self.repository.raw_repository, self.repository.raw_repository,
self.target_branch, self.base,
self.source_sha, self.head,
) )
compare.diffs(options) compare.diffs(options)
end end
...@@ -144,7 +144,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -144,7 +144,7 @@ class MergeRequestDiff < ActiveRecord::Base
self.st_diffs = new_diffs self.st_diffs = new_diffs
self.base_commit_sha = self.repository.merge_base(self.source_sha, self.target_branch) self.base_commit_sha = self.repository.merge_base(self.head, self.base)
self.save self.save
end end
...@@ -160,10 +160,24 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -160,10 +160,24 @@ class MergeRequestDiff < ActiveRecord::Base
end end
def source_sha def source_sha
return head_source_sha if head_source_sha.present?
source_commit = merge_request.source_project.commit(source_branch) source_commit = merge_request.source_project.commit(source_branch)
source_commit.try(:sha) source_commit.try(:sha)
end end
def target_sha
merge_request.target_sha
end
def base
self.target_sha || self.target_branch
end
def head
self.source_sha
end
def compare def compare
@compare ||= @compare ||=
begin begin
...@@ -172,8 +186,8 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -172,8 +186,8 @@ class MergeRequestDiff < ActiveRecord::Base
Gitlab::Git::Compare.new( Gitlab::Git::Compare.new(
self.repository.raw_repository, self.repository.raw_repository,
self.target_branch, self.base,
self.source_sha self.head
) )
end end
end end
......
...@@ -68,8 +68,18 @@ class Milestone < ActiveRecord::Base ...@@ -68,8 +68,18 @@ class Milestone < ActiveRecord::Base
@link_reference_pattern ||= super("milestones", /(?<milestone>\d+)/) @link_reference_pattern ||= super("milestones", /(?<milestone>\d+)/)
end end
def self.upcoming def self.upcoming_ids_by_projects(projects)
self.where('due_date > ?', Time.now).reorder(due_date: :asc).first rel = unscoped.of_projects(projects).active.where('due_date > ?', Time.now)
if Gitlab::Database.postgresql?
rel.order(:project_id, :due_date).select('DISTINCT ON (project_id) id')
else
rel.
group(:project_id).
having('due_date = MIN(due_date)').
pluck(:id, :project_id, :due_date).
map(&:first)
end
end end
def to_reference(from_project = nil) def to_reference(from_project = nil)
......
...@@ -20,6 +20,7 @@ class Note < ActiveRecord::Base ...@@ -20,6 +20,7 @@ class Note < ActiveRecord::Base
delegate :gfm_reference, :local_reference, to: :noteable delegate :gfm_reference, :local_reference, to: :noteable
delegate :name, to: :project, prefix: true delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true delegate :name, :email, to: :author, prefix: true
delegate :title, to: :noteable, allow_nil: true
before_validation :set_award! before_validation :set_award!
......
...@@ -23,6 +23,7 @@ class Project < ActiveRecord::Base ...@@ -23,6 +23,7 @@ class Project < ActiveRecord::Base
default_value_for :builds_enabled, gitlab_config_features.builds default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
# set last_activity_at to the same as created_at # set last_activity_at to the same as created_at
...@@ -43,6 +44,8 @@ class Project < ActiveRecord::Base ...@@ -43,6 +44,8 @@ class Project < ActiveRecord::Base
attr_accessor :new_default_branch attr_accessor :new_default_branch
attr_accessor :old_path_with_namespace attr_accessor :old_path_with_namespace
alias_attribute :title, :name
# Relations # Relations
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id' belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id'
...@@ -359,6 +362,12 @@ class Project < ActiveRecord::Base ...@@ -359,6 +362,12 @@ class Project < ActiveRecord::Base
@repository ||= Repository.new(path_with_namespace, self) @repository ||= Repository.new(path_with_namespace, self)
end end
def container_registry_url
if container_registry_enabled? && Gitlab.config.registry.enabled
"#{Gitlab.config.registry.host_with_port}/#{path_with_namespace}"
end
end
def commit(id = 'HEAD') def commit(id = 'HEAD')
repository.commit(id) repository.commit(id)
end end
......
...@@ -42,7 +42,7 @@ class ProjectWiki ...@@ -42,7 +42,7 @@ class ProjectWiki
end end
def wiki_base_path def wiki_base_path
["/", @project.path_with_namespace, "/wikis"].join('') [Gitlab.config.gitlab.relative_url_root, "/", @project.path_with_namespace, "/wikis"].join('')
end end
# Returns the Gollum::Wiki object. # Returns the Gollum::Wiki object.
......
...@@ -245,8 +245,8 @@ class Repository ...@@ -245,8 +245,8 @@ class Repository
cache.fetch(:branch_names) { branches.map(&:name) } cache.fetch(:branch_names) { branches.map(&:name) }
end end
def branch_exists?(name) def branch_exists?(branch_name)
branch_names.include?(name) branch_names.include?(branch_name)
end end
def tag_names def tag_names
......
...@@ -114,6 +114,7 @@ class User < ActiveRecord::Base ...@@ -114,6 +114,7 @@ class User < ActiveRecord::Base
before_save :ensure_external_user_rights before_save :ensure_external_user_rights
after_save :ensure_namespace_correct after_save :ensure_namespace_correct
after_initialize :set_projects_limit after_initialize :set_projects_limit
before_create :check_confirmation_email
after_create :post_create_hook after_create :post_create_hook
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
...@@ -339,6 +340,10 @@ class User < ActiveRecord::Base ...@@ -339,6 +340,10 @@ class User < ActiveRecord::Base
@reset_token @reset_token
end end
def check_confirmation_email
skip_confirmation! unless current_application_settings.send_user_confirmation_email
end
def recently_sent_password_reset? def recently_sent_password_reset?
reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago
end end
......
module Auth
class ContainerRegistryAuthenticationService < BaseService
AUDIENCE = 'container_registry'
def execute
return error('not found', 404) unless registry.enabled
if params[:offline_token]
return error('forbidden', 403) unless current_user
else
return error('forbidden', 403) unless scope
end
{ token: authorized_token(scope).encoded }
end
private
def authorized_token(*accesses)
token = JSONWebToken::RSAToken.new(registry.key)
token.issuer = registry.issuer
token.audience = params[:service]
token.subject = current_user.try(:username)
token[:access] = accesses.compact
token
end
def scope
return unless params[:scope]
@scope ||= process_scope(params[:scope])
end
def process_scope(scope)
type, name, actions = scope.split(':', 3)
actions = actions.split(',')
return unless type == 'repository'
process_repository_access(type, name, actions)
end
def process_repository_access(type, name, actions)
requested_project = Project.find_with_namespace(name)
return unless requested_project
actions = actions.select do |action|
can_access?(requested_project, action)
end
{ type: type, name: name, actions: actions } if actions.present?
end
def can_access?(requested_project, requested_action)
return false unless requested_project.container_registry_enabled?
case requested_action
when 'pull'
requested_project == project || can?(current_user, :read_container_image, requested_project)
when 'push'
requested_project == project || can?(current_user, :create_container_image, requested_project)
else
false
end
end
def registry
Gitlab.config.registry
end
end
end
...@@ -103,6 +103,12 @@ ...@@ -103,6 +103,12 @@
= f.label :signup_enabled do = f.label :signup_enabled do
= f.check_box :signup_enabled = f.check_box :signup_enabled
Sign-up enabled Sign-up enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :send_user_confirmation_email do
= f.check_box :send_user_confirmation_email
Send confirmation email on sign-up
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= event_action_name(event) = event_action_name(event)
- if event.target - if event.target
%strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target] %strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], title: event.target_title
= event_preposition(event) = event_preposition(event)
......
...@@ -84,6 +84,16 @@ ...@@ -84,6 +84,16 @@
%br %br
%span.descr Share code pastes with others out of git repository %span.descr Share code pastes with others out of git repository
- if Gitlab.config.registry.enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :container_registry_enabled do
= f.check_box :container_registry_enabled
%strong Container Registry
%br
%span.descr Enable Container Registry for this repository
= render 'issues_settings', f: f = render 'issues_settings', f: f
= render 'merge_request_settings', f: f = render 'merge_request_settings', f: f
= render 'builds_settings', f: f = render 'builds_settings', f: f
......
...@@ -98,6 +98,7 @@ production: &base ...@@ -98,6 +98,7 @@ production: &base
wiki: true wiki: true
snippets: false snippets: false
builds: true builds: true
container_registry: true
## Webhook settings ## Webhook settings
# Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10) # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10)
...@@ -224,6 +225,14 @@ production: &base ...@@ -224,6 +225,14 @@ production: &base
geo_bulk_notify_worker: geo_bulk_notify_worker:
cron: "*/10 * * * * *" cron: "*/10 * * * * *"
registry:
# enabled: true
# host: registry.example.com
# port: 5000
# api_url: http://localhost:5000/
# key: config/registry.key
# issuer: omnibus-certificate
# #
# 2. GitLab CI settings # 2. GitLab CI settings
# ========================== # ==========================
......
...@@ -248,6 +248,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g ...@@ -248,6 +248,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil? Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil?
Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil? Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
Settings.gitlab['restricted_signup_domains'] ||= [] Settings.gitlab['restricted_signup_domains'] ||= []
...@@ -287,6 +288,16 @@ Settings.artifacts['enabled'] = true if Settings.artifacts['enabled'].nil? ...@@ -287,6 +288,16 @@ Settings.artifacts['enabled'] = true if Settings.artifacts['enabled'].nil?
Settings.artifacts['path'] = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root) Settings.artifacts['path'] = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root)
Settings.artifacts['max_size'] ||= 100 # in megabytes Settings.artifacts['max_size'] ||= 100 # in megabytes
#
# Registry
#
Settings['registry'] ||= Settingslogic.new({})
Settings.registry['enabled'] ||= false
Settings.registry['host'] ||= "example.com"
Settings.registry['api_url'] ||= "http://localhost:5000/"
Settings.registry['key'] ||= nil
Settings.registry['issuer'] ||= nil
# #
# Pages # Pages
# #
......
...@@ -118,6 +118,8 @@ if Gitlab::Metrics.enabled? ...@@ -118,6 +118,8 @@ if Gitlab::Metrics.enabled?
# Instrument the classes used for checking if somebody has push access. # Instrument the classes used for checking if somebody has push access.
config.instrument_instance_methods(Gitlab::GitAccess) config.instrument_instance_methods(Gitlab::GitAccess)
config.instrument_instance_methods(Gitlab::GitAccessWiki) config.instrument_instance_methods(Gitlab::GitAccessWiki)
config.instrument_instance_methods(API::Helpers)
end end
GC::Profiler.enable GC::Profiler.enable
......
...@@ -69,6 +69,9 @@ Rails.application.routes.draw do ...@@ -69,6 +69,9 @@ Rails.application.routes.draw do
get 'search' => 'search#show' get 'search' => 'search#show'
get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
# JSON Web Token
get 'jwt/auth' => 'jwt#auth'
# API # API
API::API.logger Rails.logger API::API.logger Rails.logger
mount API::API => '/api' mount API::API => '/api'
......
class AddImagesEnabledForProject < ActiveRecord::Migration
def change
add_column :projects, :container_registry_enabled, :boolean
end
end
class AddSendUserConfirmationEmailToApplicationSettings < ActiveRecord::Migration
def up
add_column :application_settings, :send_user_confirmation_email, :boolean, default: false
#Sets confirmation email to true by default on existing installations.
execute "UPDATE application_settings SET send_user_confirmation_email=true"
end
def down
remove_column :application_settings, :send_user_confirmation_email
end
end
...@@ -877,6 +877,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do ...@@ -877,6 +877,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do
t.integer "pushes_since_gc", default: 0 t.integer "pushes_since_gc", default: 0
t.boolean "last_repository_check_failed" t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at" t.datetime "last_repository_check_at"
t.boolean "container_registry_enabled"
end end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
......
...@@ -424,6 +424,7 @@ Parameters: ...@@ -424,6 +424,7 @@ Parameters:
- `builds_enabled` (optional) - `builds_enabled` (optional)
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
...@@ -447,6 +448,7 @@ Parameters: ...@@ -447,6 +448,7 @@ Parameters:
- `builds_enabled` (optional) - `builds_enabled` (optional)
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
...@@ -472,6 +474,7 @@ Parameters: ...@@ -472,6 +474,7 @@ Parameters:
- `builds_enabled` (optional) - `builds_enabled` (optional)
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `public_builds` (optional) - `public_builds` (optional)
......
...@@ -27,6 +27,7 @@ documentation](../workflow/add-user/add-user.md). ...@@ -27,6 +27,7 @@ documentation](../workflow/add-user/add-user.md).
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ | | Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ |
| See a commit status | | ✓ | ✓ | ✓ | ✓ | | See a commit status | | ✓ | ✓ | ✓ | ✓ |
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| Manage merge requests | | | ✓ | ✓ | ✓ | | Manage merge requests | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ | | Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ | | Create new branches | | | ✓ | ✓ | ✓ |
...@@ -37,6 +38,7 @@ documentation](../workflow/add-user/add-user.md). ...@@ -37,6 +38,7 @@ documentation](../workflow/add-user/add-user.md).
| Write a wiki | | | ✓ | ✓ | ✓ | | Write a wiki | | | ✓ | ✓ | ✓ |
| Cancel and retry builds | | | ✓ | ✓ | ✓ | | Cancel and retry builds | | | ✓ | ✓ | ✓ |
| Create or update commit status | | | ✓ | ✓ | ✓ | | Create or update commit status | | | ✓ | ✓ | ✓ |
| Update a container registry | | | ✓ | ✓ | ✓ |
| Create new milestones | | | | ✓ | ✓ | | Create new milestones | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ | | Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ | | Push to protected branches | | | | ✓ | ✓ |
......
...@@ -8,3 +8,4 @@ ...@@ -8,3 +8,4 @@
- [User File Uploads](user_file_uploads.md) - [User File Uploads](user_file_uploads.md)
- [How we manage the CRIME vulnerability](crime_vulnerability.md) - [How we manage the CRIME vulnerability](crime_vulnerability.md)
- [Enforce Two-factor authentication](two_factor_authentication.md) - [Enforce Two-factor authentication](two_factor_authentication.md)
- [Send email confirmation on sign-up](user_email_confirmation.md)
# User email confirmation at sign-up
Gitlab admin can enable email confirmation on sign-up, if you want to confirm all
user emails before they are able to sign-in.
In the Admin area under **Settings** (`/admin/application_settings`), go to section
**Sign-in Restrictions** and look for **Send confirmation email on sign-up** option.
...@@ -71,7 +71,8 @@ module API ...@@ -71,7 +71,8 @@ module API
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :name, :name_with_namespace expose :name, :name_with_namespace
expose :path, :path_with_namespace expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :created_at, :last_activity_at expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :container_registry_enabled
expose :created_at, :last_activity_at
expose :shared_runners_enabled expose :shared_runners_enabled
expose :creator_id expose :creator_id
expose :namespace expose :namespace
......
...@@ -94,6 +94,7 @@ module API ...@@ -94,6 +94,7 @@ module API
# builds_enabled (optional) # builds_enabled (optional)
# wiki_enabled (optional) # wiki_enabled (optional)
# snippets_enabled (optional) # snippets_enabled (optional)
# container_registry_enabled (optional)
# shared_runners_enabled (optional) # shared_runners_enabled (optional)
# namespace_id (optional) - defaults to user namespace # namespace_id (optional) - defaults to user namespace
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
...@@ -112,6 +113,7 @@ module API ...@@ -112,6 +113,7 @@ module API
:builds_enabled, :builds_enabled,
:wiki_enabled, :wiki_enabled,
:snippets_enabled, :snippets_enabled,
:container_registry_enabled,
:shared_runners_enabled, :shared_runners_enabled,
:namespace_id, :namespace_id,
:public, :public,
...@@ -143,6 +145,7 @@ module API ...@@ -143,6 +145,7 @@ module API
# builds_enabled (optional) # builds_enabled (optional)
# wiki_enabled (optional) # wiki_enabled (optional)
# snippets_enabled (optional) # snippets_enabled (optional)
# container_registry_enabled (optional)
# shared_runners_enabled (optional) # shared_runners_enabled (optional)
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) # visibility_level (optional)
...@@ -206,6 +209,7 @@ module API ...@@ -206,6 +209,7 @@ module API
# builds_enabled (optional) # builds_enabled (optional)
# wiki_enabled (optional) # wiki_enabled (optional)
# snippets_enabled (optional) # snippets_enabled (optional)
# container_registry_enabled (optional)
# shared_runners_enabled (optional) # shared_runners_enabled (optional)
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - visibility level of a project # visibility_level (optional) - visibility level of a project
...@@ -222,6 +226,7 @@ module API ...@@ -222,6 +226,7 @@ module API
:builds_enabled, :builds_enabled,
:wiki_enabled, :wiki_enabled,
:snippets_enabled, :snippets_enabled,
:container_registry_enabled,
:shared_runners_enabled, :shared_runners_enabled,
:public, :public,
:visibility_level, :visibility_level,
......
module Gitlab
module GithubImport
class BranchFormatter < BaseFormatter
delegate :repo, :sha, :ref, to: :raw_data
def exists?
project.repository.branch_exists?(ref)
end
def name
@name ||= exists? ? ref : "#{ref}-#{short_id}"
end
def valid?
repo.present?
end
def valid?
repo.present?
end
private
def short_id
sha.to_s[0..7]
end
end
end
end
...@@ -3,12 +3,15 @@ module Gitlab ...@@ -3,12 +3,15 @@ module Gitlab
class Importer class Importer
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
attr_reader :project, :client attr_reader :client, :project, :repo, :repo_url
def initialize(project) def initialize(project)
@project = project @project = project
if import_data_credentials @repo = project.import_source
@client = Client.new(import_data_credentials[:user]) @repo_url = project.import_url
if credentials
@client = Client.new(credentials[:user])
@formatter = Gitlab::ImportFormatter.new @formatter = Gitlab::ImportFormatter.new
else else
raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}"
...@@ -22,12 +25,12 @@ module Gitlab ...@@ -22,12 +25,12 @@ module Gitlab
private private
def import_data_credentials def credentials
@import_data_credentials ||= project.import_data.credentials if project.import_data @credentials ||= project.import_data.credentials if project.import_data
end end
def import_labels def import_labels
client.labels(project.import_source).each do |raw_data| client.labels(repo).each do |raw_data|
Label.create!(LabelFormatter.new(project, raw_data).attributes) Label.create!(LabelFormatter.new(project, raw_data).attributes)
end end
...@@ -37,7 +40,7 @@ module Gitlab ...@@ -37,7 +40,7 @@ module Gitlab
end end
def import_milestones def import_milestones
client.list_milestones(project.import_source, state: :all).each do |raw_data| client.list_milestones(repo, state: :all).each do |raw_data|
Milestone.create!(MilestoneFormatter.new(project, raw_data).attributes) Milestone.create!(MilestoneFormatter.new(project, raw_data).attributes)
end end
...@@ -47,9 +50,7 @@ module Gitlab ...@@ -47,9 +50,7 @@ module Gitlab
end end
def import_issues def import_issues
client.list_issues(project.import_source, state: :all, client.list_issues(repo, state: :all, sort: :created, direction: :asc).each do |raw_data|
sort: :created,
direction: :asc).each do |raw_data|
gh_issue = IssueFormatter.new(project, raw_data) gh_issue = IssueFormatter.new(project, raw_data)
if gh_issue.valid? if gh_issue.valid?
...@@ -68,12 +69,17 @@ module Gitlab ...@@ -68,12 +69,17 @@ module Gitlab
end end
def import_pull_requests def import_pull_requests
client.pull_requests(project.import_source, state: :all, pull_requests = client.pull_requests(repo, state: :all, sort: :created, direction: :asc)
sort: :created, .map { |raw| PullRequestFormatter.new(project, raw) }
direction: :asc).each do |raw_data| .select(&:valid?)
pull_request = PullRequestFormatter.new(project, raw_data)
source_branches_removed = pull_requests.reject(&:source_branch_exists?).map { |pr| [pr.source_branch_name, pr.source_branch_sha] }
target_branches_removed = pull_requests.reject(&:target_branch_exists?).map { |pr| [pr.target_branch_name, pr.target_branch_sha] }
branches_removed = source_branches_removed | target_branches_removed
create_refs(branches_removed)
if pull_request.valid? pull_requests.each do |pull_request|
merge_request = MergeRequest.new(pull_request.attributes) merge_request = MergeRequest.new(pull_request.attributes)
if merge_request.save if merge_request.save
...@@ -82,15 +88,31 @@ module Gitlab ...@@ -82,15 +88,31 @@ module Gitlab
import_comments_on_diff(pull_request.number, merge_request) import_comments_on_diff(pull_request.number, merge_request)
end end
end end
end
delete_refs(branches_removed)
true true
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message raise Projects::ImportService::Error, e.message
end end
def create_refs(branches)
branches.each do |name, sha|
client.create_ref(repo, "refs/heads/#{name}", sha)
end
project.repository.fetch_ref(repo_url, '+refs/heads/*', 'refs/heads/*')
end
def delete_refs(branches)
branches.each do |name, _|
client.delete_ref(repo, "heads/#{name}")
project.repository.rm_branch(project.creator, name)
end
end
def apply_labels(number, issuable) def apply_labels(number, issuable)
issue = client.issue(project.import_source, number) issue = client.issue(repo, number)
if issue.labels.count > 0 if issue.labels.count > 0
label_ids = issue.labels.map do |raw| label_ids = issue.labels.map do |raw|
...@@ -102,12 +124,12 @@ module Gitlab ...@@ -102,12 +124,12 @@ module Gitlab
end end
def import_comments(issue_number, noteable) def import_comments(issue_number, noteable)
comments = client.issue_comments(project.import_source, issue_number) comments = client.issue_comments(repo, issue_number)
create_comments(comments, noteable) create_comments(comments, noteable)
end end
def import_comments_on_diff(pull_request_number, merge_request) def import_comments_on_diff(pull_request_number, merge_request)
comments = client.pull_request_comments(project.import_source, pull_request_number) comments = client.pull_request_comments(repo, pull_request_number)
create_comments(comments, merge_request) create_comments(comments, merge_request)
end end
......
module Gitlab module Gitlab
module GithubImport module GithubImport
class PullRequestFormatter < BaseFormatter class PullRequestFormatter < BaseFormatter
delegate :exists?, :name, :project, :repo, :sha, to: :source_branch, prefix: true
delegate :exists?, :name, :project, :repo, :sha, to: :target_branch, prefix: true
def attributes def attributes
{ {
iid: number, iid: number,
title: raw_data.title, title: raw_data.title,
description: description, description: description,
source_project: source_project, source_project: source_branch_project,
source_branch: source_branch.name, source_branch: source_branch_name,
target_project: target_project, head_source_sha: source_branch_sha,
target_branch: target_branch.name, target_project: target_branch_project,
target_branch: target_branch_name,
base_target_sha: target_branch_sha,
state: state, state: state,
milestone: milestone, milestone: milestone,
author_id: author_id, author_id: author_id,
...@@ -24,7 +29,15 @@ module Gitlab ...@@ -24,7 +29,15 @@ module Gitlab
end end
def valid? def valid?
!cross_project? && source_branch.present? && target_branch.present? source_branch.valid? && target_branch.valid? && !cross_project?
end
def source_branch
@source_branch ||= BranchFormatter.new(project, raw_data.head)
end
def target_branch
@target_branch ||= BranchFormatter.new(project, raw_data.base)
end end
private private
...@@ -52,7 +65,7 @@ module Gitlab ...@@ -52,7 +65,7 @@ module Gitlab
end end
def cross_project? def cross_project?
source_repo.present? && target_repo.present? && source_repo.id != target_repo.id source_branch_repo.id != target_branch_repo.id
end end
def description def description
...@@ -65,30 +78,6 @@ module Gitlab ...@@ -65,30 +78,6 @@ module Gitlab
end end
end end
def source_project
project
end
def source_repo
raw_data.head.repo
end
def source_branch
source_project.repository.find_branch(raw_data.head.ref)
end
def target_project
project
end
def target_repo
raw_data.base.repo
end
def target_branch
target_project.repository.find_branch(raw_data.base.ref)
end
def state def state
@state ||= case true @state ||= case true
when raw_data.state == 'closed' && raw_data.merged_at.present? when raw_data.state == 'closed' && raw_data.merged_at.present?
......
...@@ -6,26 +6,28 @@ module Gitlab ...@@ -6,26 +6,28 @@ module Gitlab
attach_to :active_support attach_to :active_support
def cache_read(event) def cache_read(event)
increment(:cache_read_duration, event.duration) increment(:cache_read, event.duration)
end end
def cache_write(event) def cache_write(event)
increment(:cache_write_duration, event.duration) increment(:cache_write, event.duration)
end end
def cache_delete(event) def cache_delete(event)
increment(:cache_delete_duration, event.duration) increment(:cache_delete, event.duration)
end end
def cache_exist?(event) def cache_exist?(event)
increment(:cache_exists_duration, event.duration) increment(:cache_exists, event.duration)
end end
def increment(key, duration) def increment(key, duration)
return unless current_transaction return unless current_transaction
current_transaction.increment(:cache_duration, duration) current_transaction.increment(:cache_duration, duration)
current_transaction.increment(key, duration) current_transaction.increment(:cache_count, 1)
current_transaction.increment("#{key}_duration".to_sym, duration)
current_transaction.increment("#{key}_count".to_sym, 1)
end end
private private
......
...@@ -62,7 +62,7 @@ module Gitlab ...@@ -62,7 +62,7 @@ module Gitlab
end end
def wiki_page_url def wiki_page_url
"#{Gitlab.config.gitlab.url}#{object.wiki.wiki_base_path}/#{object.slug}" namespace_project_wiki_url(object.wiki.project.namespace, object.wiki.project, object.slug)
end end
end end
end end
module JSONWebToken
class RSAToken < Token
attr_reader :key_file
def initialize(key_file)
super()
@key_file = key_file
end
def encoded
headers = {
kid: kid
}
JWT.encode(payload, key, 'RS256', headers)
end
private
def key_data
@key_data ||= File.read(key_file)
end
def key
@key ||= OpenSSL::PKey::RSA.new(key_data)
end
def public_key
key.public_key
end
def kid
# calculate sha256 from DER encoded ASN1
kid = Digest::SHA256.digest(public_key.to_der)
# we encode only 30 bytes with base32
kid = Base32.encode(kid[0..29])
# insert colon every 4 characters
kid.scan(/.{4}/).join(':')
end
end
end
module JSONWebToken
class Token
attr_accessor :issuer, :subject, :audience, :id
attr_accessor :issued_at, :not_before, :expire_time
def initialize
@id = SecureRandom.uuid
@issued_at = Time.now
# we give a few seconds for time shift
@not_before = issued_at - 5.seconds
# default 60 seconds should be more than enough for this authentication token
@expire_time = issued_at + 1.minute
@custom_payload = {}
end
def [](key)
@custom_payload[key]
end
def []=(key, value)
@custom_payload[key] = value
end
def encoded
raise NotImplementedError
end
def payload
@custom_payload.merge(default_payload)
end
private
def default_payload
{
jti: id,
aud: audience,
sub: subject,
iss: issuer,
iat: issued_at.to_i,
nbf: not_before.to_i,
exp: expire_time.to_i
}.compact
end
end
end
...@@ -65,3 +65,4 @@ Disallow: /*/*/deploy_keys ...@@ -65,3 +65,4 @@ Disallow: /*/*/deploy_keys
Disallow: /*/*/hooks Disallow: /*/*/hooks
Disallow: /*/*/services Disallow: /*/*/services
Disallow: /*/*/protected_branches Disallow: /*/*/protected_branches
Disallow: /*/*/uploads/
...@@ -114,6 +114,82 @@ describe Admin::UsersController do ...@@ -114,6 +114,82 @@ describe Admin::UsersController do
end end
end end
describe 'POST update' do
context 'when the password has changed' do
def update_password(user, password, password_confirmation = nil)
params = {
id: user.to_param,
user: {
password: password,
password_confirmation: password_confirmation || password
}
}
post :update, params
end
context 'when the new password is valid' do
it 'redirects to the user' do
update_password(user, 'AValidPassword1')
expect(response).to redirect_to(admin_user_path(user))
end
it 'updates the password' do
update_password(user, 'AValidPassword1')
expect { user.reload }.to change { user.encrypted_password }
end
it 'sets the new password to expire immediately' do
update_password(user, 'AValidPassword1')
expect { user.reload }.to change { user.password_expires_at }.to(a_value <= Time.now)
end
end
context 'when the new password is invalid' do
it 'shows the edit page again' do
update_password(user, 'invalid')
expect(response).to render_template(:edit)
end
it 'returns the error message' do
update_password(user, 'invalid')
expect(assigns[:user].errors).to contain_exactly(a_string_matching(/too short/))
end
it 'does not update the password' do
update_password(user, 'invalid')
expect { user.reload }.not_to change { user.encrypted_password }
end
end
context 'when the new password does not match the password confirmation' do
it 'shows the edit page again' do
update_password(user, 'AValidPassword1', 'AValidPassword2')
expect(response).to render_template(:edit)
end
it 'returns the error message' do
update_password(user, 'AValidPassword1', 'AValidPassword2')
expect(assigns[:user].errors).to contain_exactly(a_string_matching(/doesn't match/))
end
it 'does not update the password' do
update_password(user, 'AValidPassword1', 'AValidPassword2')
expect { user.reload }.not_to change { user.encrypted_password }
end
end
end
end
describe "POST impersonate" do describe "POST impersonate" do
context "when the user is blocked" do context "when the user is blocked" do
before do before do
......
require 'spec_helper'
describe RegistrationsController do
describe '#create' do
around(:each) do |example|
perform_enqueued_jobs do
example.run
end
end
let(:user_params) { { user: { name: "new_user", username: "new_username", email: "new@user.com", password: "Any_password" } } }
context 'when sending email confirmation' do
before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(false) }
it 'logs user in directly' do
post(:create, user_params)
expect(ActionMailer::Base.deliveries.last).to be_nil
expect(subject.current_user).to_not be_nil
end
end
context 'when not sending email confirmation' do
before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(true) }
it 'does not authenticate user and sends confirmation email' do
post(:create, user_params)
expect(ActionMailer::Base.deliveries.last.to.first).to eq(user_params[:user][:email])
expect(subject.current_user).to be_nil
end
end
end
end
...@@ -210,6 +210,8 @@ describe "Admin::Users", feature: true do ...@@ -210,6 +210,8 @@ describe "Admin::Users", feature: true do
before do before do
fill_in "user_name", with: "Big Bang" fill_in "user_name", with: "Big Bang"
fill_in "user_email", with: "bigbang@mail.com" fill_in "user_email", with: "bigbang@mail.com"
fill_in "user_password", with: "AValidPassword1"
fill_in "user_password_confirmation", with: "AValidPassword1"
check "user_admin" check "user_admin"
click_button "Save changes" click_button "Save changes"
end end
...@@ -223,6 +225,7 @@ describe "Admin::Users", feature: true do ...@@ -223,6 +225,7 @@ describe "Admin::Users", feature: true do
@simple_user.reload @simple_user.reload
expect(@simple_user.name).to eq('Big Bang') expect(@simple_user.name).to eq('Big Bang')
expect(@simple_user.is_admin?).to be_truthy expect(@simple_user.is_admin?).to be_truthy
expect(@simple_user.password_expires_at).to be <= Time.now
end end
end end
end end
......
...@@ -38,6 +38,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -38,6 +38,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(page).to have_content 'lfs' expect(page).to have_content 'lfs'
expect(page).not_to have_content 'fix' expect(page).not_to have_content 'fix'
expect(page).not_to have_content 'markdown' expect(page).not_to have_content 'markdown'
expect(count_merge_requests).to eq(1)
end end
it 'filters on a specific assignee' do it 'filters on a specific assignee' do
...@@ -46,6 +47,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -46,6 +47,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(page).not_to have_content 'lfs' expect(page).not_to have_content 'lfs'
expect(page).to have_content 'fix' expect(page).to have_content 'fix'
expect(page).to have_content 'markdown' expect(page).to have_content 'markdown'
expect(count_merge_requests).to eq(2)
end end
it 'sorts by newest' do it 'sorts by newest' do
...@@ -53,6 +55,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -53,6 +55,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(first_merge_request).to include('lfs') expect(first_merge_request).to include('lfs')
expect(last_merge_request).to include('fix') expect(last_merge_request).to include('fix')
expect(count_merge_requests).to eq(3)
end end
it 'sorts by oldest' do it 'sorts by oldest' do
...@@ -60,30 +63,35 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -60,30 +63,35 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(first_merge_request).to include('fix') expect(first_merge_request).to include('fix')
expect(last_merge_request).to include('lfs') expect(last_merge_request).to include('lfs')
expect(count_merge_requests).to eq(3)
end end
it 'sorts by last updated' do it 'sorts by last updated' do
visit_merge_requests(project, sort: sort_value_recently_updated) visit_merge_requests(project, sort: sort_value_recently_updated)
expect(first_merge_request).to include('lfs') expect(first_merge_request).to include('lfs')
expect(count_merge_requests).to eq(3)
end end
it 'sorts by oldest updated' do it 'sorts by oldest updated' do
visit_merge_requests(project, sort: sort_value_oldest_updated) visit_merge_requests(project, sort: sort_value_oldest_updated)
expect(first_merge_request).to include('markdown') expect(first_merge_request).to include('markdown')
expect(count_merge_requests).to eq(3)
end end
it 'sorts by milestone due soon' do it 'sorts by milestone due soon' do
visit_merge_requests(project, sort: sort_value_milestone_soon) visit_merge_requests(project, sort: sort_value_milestone_soon)
expect(first_merge_request).to include('fix') expect(first_merge_request).to include('fix')
expect(count_merge_requests).to eq(3)
end end
it 'sorts by milestone due later' do it 'sorts by milestone due later' do
visit_merge_requests(project, sort: sort_value_milestone_later) visit_merge_requests(project, sort: sort_value_milestone_later)
expect(first_merge_request).to include('markdown') expect(first_merge_request).to include('markdown')
expect(count_merge_requests).to eq(3)
end end
it 'filters on one label and sorts by due soon' do it 'filters on one label and sorts by due soon' do
...@@ -94,6 +102,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -94,6 +102,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
sort: sort_value_due_date_soon) sort: sort_value_due_date_soon)
expect(first_merge_request).to include('fix') expect(first_merge_request).to include('fix')
expect(count_merge_requests).to eq(1)
end end
context 'while filtering on two labels' do context 'while filtering on two labels' do
...@@ -110,6 +119,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -110,6 +119,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
sort: sort_value_due_date_soon) sort: sort_value_due_date_soon)
expect(first_merge_request).to include('fix') expect(first_merge_request).to include('fix')
expect(count_merge_requests).to eq(1)
end end
context 'filter on assignee and' do context 'filter on assignee and' do
...@@ -119,6 +129,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -119,6 +129,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
sort: sort_value_due_date_soon) sort: sort_value_due_date_soon)
expect(first_merge_request).to include('fix') expect(first_merge_request).to include('fix')
expect(count_merge_requests).to eq(1)
end end
end end
end end
...@@ -134,4 +145,8 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true ...@@ -134,4 +145,8 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
def last_merge_request def last_merge_request
page.all('ul.mr-list > li').last.text page.all('ul.mr-list > li').last.text
end end
def count_merge_requests
page.all('ul.mr-list > li').count
end
end end
...@@ -2,6 +2,10 @@ require 'spec_helper' ...@@ -2,6 +2,10 @@ require 'spec_helper'
feature 'Signup', feature: true do feature 'Signup', feature: true do
describe 'signup with no errors' do describe 'signup with no errors' do
context "when sending confirmation email" do
before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true) }
it 'creates the user account and sends a confirmation email' do it 'creates the user account and sends a confirmation email' do
user = build(:user) user = build(:user)
...@@ -18,6 +22,27 @@ feature 'Signup', feature: true do ...@@ -18,6 +22,27 @@ feature 'Signup', feature: true do
end end
end end
context "when not sending confirmation email" do
before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false) }
it 'creates the user account and goes to dashboard' do
user = build(:user)
visit root_path
fill_in 'new_user_name', with: user.name
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: user.email
fill_in 'new_user_password', with: user.password
click_button "Sign up"
expect(current_path).to eq dashboard_projects_path
expect(page).to have_content("Welcome! You have signed up successfully.")
end
end
end
describe 'signup with errors' do describe 'signup with errors' do
it "displays the errors" do it "displays the errors" do
existing_user = create(:user) existing_user = create(:user)
......
require 'spec_helper' require 'spec_helper'
describe IssuesFinder do describe IssuesFinder do
let(:user) { create :user } let(:user) { create(:user) }
let(:user2) { create :user } let(:user2) { create(:user) }
let(:project1) { create(:project) } let(:project1) { create(:empty_project) }
let(:project2) { create(:project) } let(:project2) { create(:empty_project) }
let(:milestone) { create(:milestone, project: project1) } let(:milestone) { create(:milestone, project: project1) }
let(:label) { create(:label, project: project2) } let(:label) { create(:label, project: project2) }
let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone) } let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone) }
...@@ -16,101 +16,147 @@ describe IssuesFinder do ...@@ -16,101 +16,147 @@ describe IssuesFinder do
project1.team << [user, :master] project1.team << [user, :master]
project2.team << [user, :developer] project2.team << [user, :developer]
project2.team << [user2, :developer] project2.team << [user2, :developer]
end
describe :execute do
before :each do
issue1 issue1
issue2 issue2
issue3 issue3
end end
describe '#execute' do
let(:search_user) { user }
let(:params) { {} }
let(:issues) { IssuesFinder.new(search_user, params.merge(scope: scope, state: 'opened')).execute }
context 'scope: all' do context 'scope: all' do
it 'should filter by all' do let(:scope) { 'all' }
params = { scope: "all", state: 'opened' }
issues = IssuesFinder.new(user, params).execute it 'returns all issues' do
expect(issues.size).to eq(3) expect(issues).to contain_exactly(issue1, issue2, issue3)
end end
it 'should filter by assignee id' do context 'filtering by assignee ID' do
params = { scope: "all", assignee_id: user.id, state: 'opened' } let(:params) { { assignee_id: user.id } }
issues = IssuesFinder.new(user, params).execute
expect(issues.size).to eq(2) it 'returns issues assigned to that user' do
expect(issues).to contain_exactly(issue1, issue2)
end
end end
it 'should filter by author id' do context 'filtering by author ID' do
params = { scope: "all", author_id: user2.id, state: 'opened' } let(:params) { { author_id: user2.id } }
issues = IssuesFinder.new(user, params).execute
expect(issues).to eq([issue3]) it 'returns issues created by that user' do
expect(issues).to contain_exactly(issue3)
end
end end
it 'should filter by milestone id' do context 'filtering by milestone' do
params = { scope: "all", milestone_title: milestone.title, state: 'opened' } let(:params) { { milestone_title: milestone.title } }
issues = IssuesFinder.new(user, params).execute
expect(issues).to eq([issue1]) it 'returns issues assigned to that milestone' do
expect(issues).to contain_exactly(issue1)
end
end end
it 'should filter by no milestone id' do context 'filtering by no milestone' do
params = { scope: "all", milestone_title: Milestone::None.title, state: 'opened' } let(:params) { { milestone_title: Milestone::None.title } }
issues = IssuesFinder.new(user, params).execute
expect(issues).to match_array([issue2, issue3]) it 'returns issues with no milestone' do
expect(issues).to contain_exactly(issue2, issue3)
end
end end
it 'should filter by label name' do context 'filtering by upcoming milestone' do
params = { scope: "all", label_name: label.title, state: 'opened' } let(:params) { { milestone_title: Milestone::Upcoming.name } }
issues = IssuesFinder.new(user, params).execute
expect(issues).to eq([issue2]) let(:project_no_upcoming_milestones) { create(:empty_project, :public) }
let(:project_next_1_1) { create(:empty_project, :public) }
let(:project_next_8_8) { create(:empty_project, :public) }
let(:yesterday) { Date.today - 1.day }
let(:tomorrow) { Date.today + 1.day }
let(:two_days_from_now) { Date.today + 2.days }
let(:ten_days_from_now) { Date.today + 10.days }
let(:milestones) do
[
create(:milestone, :closed, project: project_no_upcoming_milestones),
create(:milestone, project: project_next_1_1, title: '1.1', due_date: two_days_from_now),
create(:milestone, project: project_next_1_1, title: '8.8', due_date: ten_days_from_now),
create(:milestone, project: project_next_8_8, title: '1.1', due_date: yesterday),
create(:milestone, project: project_next_8_8, title: '8.8', due_date: tomorrow)
]
end end
it 'returns unique issues when filtering by multiple labels' do before do
label2 = create(:label, project: project2) milestones.each do |milestone|
create(:issue, project: milestone.project, milestone: milestone, author: user, assignee: user)
end
end
create(:label_link, label: label2, target: issue2) it 'returns issues in the upcoming milestone for each project' do
expect(issues.map { |issue| issue.milestone.title }).to contain_exactly('1.1', '8.8')
expect(issues.map { |issue| issue.milestone.due_date }).to contain_exactly(tomorrow, two_days_from_now)
end
end
context 'filtering by label' do
let(:params) { { label_name: label.title } }
it 'returns issues with that label' do
expect(issues).to contain_exactly(issue2)
end
end
params = { context 'filtering by multiple labels' do
scope: 'all', let(:params) { { label_name: [label.title, label2.title].join(',') } }
label_name: [label.title, label2.title].join(','), let(:label2) { create(:label, project: project2) }
state: 'opened'
}
issues = IssuesFinder.new(user, params).execute before { create(:label_link, label: label2, target: issue2) }
expect(issues).to eq([issue2]) it 'returns the unique issues with any of those labels' do
expect(issues).to contain_exactly(issue2)
end end
end
context 'filtering by no label' do
let(:params) { { label_name: Label::None.title } }
it 'should filter by no label name' do it 'returns issues with no labels' do
params = { scope: "all", label_name: Label::None.title, state: 'opened' } expect(issues).to contain_exactly(issue1, issue3)
issues = IssuesFinder.new(user, params).execute
expect(issues).to match_array([issue1, issue3])
end end
end
context 'when the user is unauthorized' do
let(:search_user) { nil }
it 'should be empty for unauthorized user' do it 'returns no results' do
params = { scope: "all", state: 'opened' } expect(issues).to be_empty
issues = IssuesFinder.new(nil, params).execute
expect(issues.size).to be_zero
end end
end
context 'when the user can see some, but not all, issues' do
let(:search_user) { user2 }
it 'should not include unauthorized issues' do it 'returns only issues they can see' do
params = { scope: "all", state: 'opened' } expect(issues).to contain_exactly(issue2, issue3)
issues = IssuesFinder.new(user2, params).execute end
expect(issues.size).to eq(2)
expect(issues).not_to include(issue1)
expect(issues).to include(issue2)
expect(issues).to include(issue3)
end end
end end
context 'personal scope' do context 'personal scope' do
it 'should filter by assignee' do let(:scope) { 'assigned-to-me' }
params = { scope: "assigned-to-me", state: 'opened' }
issues = IssuesFinder.new(user, params).execute it 'returns issue assigned to the user' do
expect(issues.size).to eq(2) expect(issues).to contain_exactly(issue1, issue2)
end end
it 'should filter by project' do context 'filtering by project' do
params = { scope: "assigned-to-me", state: 'opened', project_id: project1.id } let(:params) { { project_id: project1.id } }
issues = IssuesFinder.new(user, params).execute
expect(issues.size).to eq(1) it 'returns issues assigned to the user in that project' do
expect(issues).to contain_exactly(issue1)
end
end end
end end
end end
......
require 'spec_helper'
describe Gitlab::GithubImport::BranchFormatter, lib: true do
let(:project) { create(:project) }
let(:repo) { double }
let(:raw) do
{
ref: 'feature',
repo: repo,
sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b'
}
end
describe '#exists?' do
it 'returns true when branch exists' do
branch = described_class.new(project, double(raw))
expect(branch.exists?).to eq true
end
it 'returns false when branch does not exist' do
branch = described_class.new(project, double(raw.merge(ref: 'removed-branch')))
expect(branch.exists?).to eq false
end
end
describe '#name' do
it 'returns raw ref when branch exists' do
branch = described_class.new(project, double(raw))
expect(branch.name).to eq 'feature'
end
it 'returns formatted ref when branch does not exist' do
branch = described_class.new(project, double(raw.merge(ref: 'removed-branch')))
expect(branch.name).to eq 'removed-branch-2e5d3239'
end
end
describe '#repo' do
it 'returns raw repo' do
branch = described_class.new(project, double(raw))
expect(branch.repo).to eq repo
end
end
describe '#sha' do
it 'returns raw sha' do
branch = described_class.new(project, double(raw))
expect(branch.sha).to eq '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b'
end
end
describe '#valid?' do
it 'returns true when repository exists' do
branch = described_class.new(project, double(raw))
expect(branch.valid?).to eq true
end
it 'returns false when repository does not exist' do
branch = described_class.new(project, double(raw.merge(repo: nil)))
expect(branch.valid?).to eq false
end
end
end
...@@ -4,9 +4,9 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -4,9 +4,9 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:repository) { double(id: 1, fork: false) } let(:repository) { double(id: 1, fork: false) }
let(:source_repo) { repository } let(:source_repo) { repository }
let(:source_branch) { double(ref: 'feature', repo: source_repo) } let(:source_branch) { double(ref: 'feature', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b') }
let(:target_repo) { repository } let(:target_repo) { repository }
let(:target_branch) { double(ref: 'master', repo: target_repo) } let(:target_branch) { double(ref: 'master', repo: target_repo, sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7') }
let(:octocat) { double(id: 123456, login: 'octocat') } let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
...@@ -41,8 +41,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -41,8 +41,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
description: "*Created by: octocat*\n\nPlease pull these awesome changes", description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project, source_project: project,
source_branch: 'feature', source_branch: 'feature',
head_source_sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b',
target_project: project, target_project: project,
target_branch: 'master', target_branch: 'master',
base_target_sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7',
state: 'opened', state: 'opened',
milestone: nil, milestone: nil,
author_id: project.creator_id, author_id: project.creator_id,
...@@ -66,8 +68,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -66,8 +68,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
description: "*Created by: octocat*\n\nPlease pull these awesome changes", description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project, source_project: project,
source_branch: 'feature', source_branch: 'feature',
head_source_sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b',
target_project: project, target_project: project,
target_branch: 'master', target_branch: 'master',
base_target_sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7',
state: 'closed', state: 'closed',
milestone: nil, milestone: nil,
author_id: project.creator_id, author_id: project.creator_id,
...@@ -91,8 +95,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -91,8 +95,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
description: "*Created by: octocat*\n\nPlease pull these awesome changes", description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project, source_project: project,
source_branch: 'feature', source_branch: 'feature',
head_source_sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b',
target_project: project, target_project: project,
target_branch: 'master', target_branch: 'master',
base_target_sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7',
state: 'merged', state: 'merged',
milestone: nil, milestone: nil,
author_id: project.creator_id, author_id: project.creator_id,
...@@ -137,11 +143,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -137,11 +143,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:milestone) { double(number: 45) } let(:milestone) { double(number: 45) }
let(:raw_data) { double(base_data.merge(milestone: milestone)) } let(:raw_data) { double(base_data.merge(milestone: milestone)) }
it 'returns nil when milestone does not exists' do it 'returns nil when milestone does not exist' do
expect(pull_request.attributes.fetch(:milestone)).to be_nil expect(pull_request.attributes.fetch(:milestone)).to be_nil
end end
it 'returns milestone when is exists' do it 'returns milestone when it exists' do
milestone = create(:milestone, project: project, iid: 45) milestone = create(:milestone, project: project, iid: 45)
expect(pull_request.attributes.fetch(:milestone)).to eq milestone expect(pull_request.attributes.fetch(:milestone)).to eq milestone
...@@ -158,36 +164,16 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -158,36 +164,16 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
describe '#valid?' do describe '#valid?' do
let(:invalid_branch) { double(ref: 'invalid-branch').as_null_object } context 'when source, and target repos are not a fork' do
let(:raw_data) { double(base_data) }
context 'when source, and target repositories are the same' do
context 'and source and target branches exists' do
let(:raw_data) { double(base_data.merge(head: source_branch, base: target_branch)) }
it 'returns true' do it 'returns true' do
expect(pull_request.valid?).to eq true expect(pull_request.valid?).to eq true
end end
end end
context 'and source branch doesn not exists' do
let(:raw_data) { double(base_data.merge(head: invalid_branch, base: target_branch)) }
it 'returns false' do
expect(pull_request.valid?).to eq false
end
end
context 'and target branch doesn not exists' do
let(:raw_data) { double(base_data.merge(head: source_branch, base: invalid_branch)) }
it 'returns false' do
expect(pull_request.valid?).to eq false
end
end
end
context 'when source repo is a fork' do context 'when source repo is a fork' do
let(:source_repo) { double(id: 2, fork: true) } let(:source_repo) { double(id: 2) }
let(:raw_data) { double(base_data) } let(:raw_data) { double(base_data) }
it 'returns false' do it 'returns false' do
...@@ -196,7 +182,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -196,7 +182,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
context 'when target repo is a fork' do context 'when target repo is a fork' do
let(:target_repo) { double(id: 2, fork: true) } let(:target_repo) { double(id: 2) }
let(:raw_data) { double(base_data) } let(:raw_data) { double(base_data) }
it 'returns false' do it 'returns false' do
......
...@@ -9,7 +9,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do ...@@ -9,7 +9,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_read' do describe '#cache_read' do
it 'increments the cache_read duration' do it 'increments the cache_read duration' do
expect(subscriber).to receive(:increment). expect(subscriber).to receive(:increment).
with(:cache_read_duration, event.duration) with(:cache_read, event.duration)
subscriber.cache_read(event) subscriber.cache_read(event)
end end
...@@ -18,7 +18,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do ...@@ -18,7 +18,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_write' do describe '#cache_write' do
it 'increments the cache_write duration' do it 'increments the cache_write duration' do
expect(subscriber).to receive(:increment). expect(subscriber).to receive(:increment).
with(:cache_write_duration, event.duration) with(:cache_write, event.duration)
subscriber.cache_write(event) subscriber.cache_write(event)
end end
...@@ -27,7 +27,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do ...@@ -27,7 +27,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_delete' do describe '#cache_delete' do
it 'increments the cache_delete duration' do it 'increments the cache_delete duration' do
expect(subscriber).to receive(:increment). expect(subscriber).to receive(:increment).
with(:cache_delete_duration, event.duration) with(:cache_delete, event.duration)
subscriber.cache_delete(event) subscriber.cache_delete(event)
end end
...@@ -36,7 +36,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do ...@@ -36,7 +36,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_exist?' do describe '#cache_exist?' do
it 'increments the cache_exists duration' do it 'increments the cache_exists duration' do
expect(subscriber).to receive(:increment). expect(subscriber).to receive(:increment).
with(:cache_exists_duration, event.duration) with(:cache_exists, event.duration)
subscriber.cache_exist?(event) subscriber.cache_exist?(event)
end end
...@@ -61,10 +61,16 @@ describe Gitlab::Metrics::Subscribers::RailsCache do ...@@ -61,10 +61,16 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
expect(transaction).to receive(:increment). expect(transaction).to receive(:increment).
with(:cache_duration, event.duration) with(:cache_duration, event.duration)
expect(transaction).to receive(:increment).
with(:cache_count, 1)
expect(transaction).to receive(:increment). expect(transaction).to receive(:increment).
with(:cache_delete_duration, event.duration) with(:cache_delete_duration, event.duration)
subscriber.increment(:cache_delete_duration, event.duration) expect(transaction).to receive(:increment).
with(:cache_delete_count, 1)
subscriber.increment(:cache_delete, event.duration)
end end
end end
end end
......
describe JSONWebToken::RSAToken do
let(:rsa_key) do
OpenSSL::PKey::RSA.new <<-eos.strip_heredoc
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAMA5sXIBE0HwgIB40iNidN4PGWzOyLQK0bsdOBNgpEXkDlZBvnak
OUgAPF+rME4PB0Yl415DabUI40T5UNmlwxcCAwEAAQJAZtY2pSwIFm3JAXIh0cZZ
iXcAfiJ+YzuqinUOS+eW2sBCAEzjcARlU/o6sFQgtsOi4FOMczAd1Yx8UDMXMmrw
2QIhAPBgVhJiTF09pdmeFWutCvTJDlFFAQNbrbo2X2x/9WF9AiEAzLgqMKeStSRu
H9N16TuDrUoO8R+DPqriCwkKrSHaWyMCIFzMhE4inuKcSywBaLmiG4m3GQzs++Al
A6PRG/PSTpQtAiBxtBg6zdf+JC3GH3zt/dA0/10tL4OF2wORfYQghRzyYQIhAL2l
0ZQW+yLIZAGrdBFWYEAa52GZosncmzBNlsoTgwE4
-----END RSA PRIVATE KEY-----
eos
end
let(:rsa_token) { described_class.new(nil) }
let(:rsa_encoded) { rsa_token.encoded }
before { allow_any_instance_of(described_class).to receive(:key).and_return(rsa_key) }
context 'token' do
context 'for valid key to be validated' do
before { rsa_token['key'] = 'value' }
subject { JWT.decode(rsa_encoded, rsa_key) }
it { expect{subject}.to_not raise_error }
it { expect(subject.first).to include('key' => 'value') }
it do
expect(subject.second).to eq(
"typ" => "JWT",
"alg" => "RS256",
"kid" => "OGXY:4TR7:FAVO:WEM2:XXEW:E4FP:TKL7:7ACK:TZAF:D54P:SUIA:P3B2")
end
end
context 'for invalid key to raise an exception' do
let(:new_key) { OpenSSL::PKey::RSA.generate(512) }
subject { JWT.decode(rsa_encoded, new_key) }
it { expect{subject}.to raise_error(JWT::DecodeError) }
end
end
end
describe JSONWebToken::Token do
let(:token) { described_class.new }
context 'custom parameters' do
let(:value) { 'value' }
before { token[:key] = value }
it { expect(token[:key]).to eq(value) }
it { expect(token.payload).to include(key: value) }
end
context 'embeds default payload' do
subject { token.payload }
let(:default) { token.send(:default_payload) }
it { is_expected.to include(default) }
end
end
...@@ -64,7 +64,13 @@ describe MergeRequest, models: true do ...@@ -64,7 +64,13 @@ describe MergeRequest, models: true do
describe '#target_sha' do describe '#target_sha' do
context 'when the target branch does not exist anymore' do context 'when the target branch does not exist anymore' do
subject { create(:merge_request).tap { |mr| mr.update_attribute(:target_branch, 'deleted') } } let(:project) { create(:project) }
subject { create(:merge_request, source_project: project, target_project: project) }
before do
project.repository.raw_repository.delete_branch(subject.target_branch)
end
it 'returns nil' do it 'returns nil' do
expect(subject.target_sha).to be_nil expect(subject.target_sha).to be_nil
...@@ -319,7 +325,12 @@ describe MergeRequest, models: true do ...@@ -319,7 +325,12 @@ describe MergeRequest, models: true do
let(:fork_project) { create(:project, forked_from_project: project) } let(:fork_project) { create(:project, forked_from_project: project) }
context 'when the target branch does not exist anymore' do context 'when the target branch does not exist anymore' do
subject { create(:merge_request).tap { |mr| mr.update_attribute(:target_branch, 'deleted') } } subject { create(:merge_request, source_project: project, target_project: project) }
before do
project.repository.raw_repository.delete_branch(subject.target_branch)
subject.reload
end
it 'does not crash' do it 'does not crash' do
expect{ subject.diverged_commits_count }.not_to raise_error expect{ subject.diverged_commits_count }.not_to raise_error
......
...@@ -204,4 +204,37 @@ describe Milestone, models: true do ...@@ -204,4 +204,37 @@ describe Milestone, models: true do
to eq([milestone]) to eq([milestone])
end end
end end
describe '.upcoming_ids_by_projects' do
let(:project_1) { create(:empty_project) }
let(:project_2) { create(:empty_project) }
let(:project_3) { create(:empty_project) }
let(:projects) { [project_1, project_2, project_3] }
let!(:past_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now - 1.day) }
let!(:current_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now + 1.day) }
let!(:future_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now + 2.days) }
let!(:past_milestone_project_2) { create(:milestone, project: project_2, due_date: Time.now - 1.day) }
let!(:closed_milestone_project_2) { create(:milestone, :closed, project: project_2, due_date: Time.now + 1.day) }
let!(:current_milestone_project_2) { create(:milestone, project: project_2, due_date: Time.now + 2.days) }
let!(:past_milestone_project_3) { create(:milestone, project: project_3, due_date: Time.now - 1.day) }
# The call to `#try` is because this returns a relation with a Postgres DB,
# and an array of IDs with a MySQL DB.
let(:milestone_ids) { Milestone.upcoming_ids_by_projects(projects).map { |id| id.try(:id) || id } }
it 'returns the next upcoming open milestone ID for each project' do
expect(milestone_ids).to contain_exactly(current_milestone_project_1.id, current_milestone_project_2.id)
end
context 'when the projects have no open upcoming milestones' do
let(:projects) { [project_3] }
it 'returns no results' do
expect(milestone_ids).to be_empty
end
end
end
end end
...@@ -38,7 +38,8 @@ describe ProjectWiki, models: true do ...@@ -38,7 +38,8 @@ describe ProjectWiki, models: true do
describe "#wiki_base_path" do describe "#wiki_base_path" do
it "returns the wiki base path" do it "returns the wiki base path" do
wiki_base_path = "/#{project.path_with_namespace}/wikis" wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.path_with_namespace}/wikis"
expect(subject.wiki_base_path).to eq(wiki_base_path) expect(subject.wiki_base_path).to eq(wiki_base_path)
end end
end end
......
...@@ -154,6 +154,7 @@ describe User, models: true do ...@@ -154,6 +154,7 @@ describe User, models: true do
end end
describe '#confirm' do describe '#confirm' do
before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(true) }
let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: 'test@gitlab.com') } let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: 'test@gitlab.com') }
it 'returns unconfirmed' do it 'returns unconfirmed' do
......
require 'spec_helper'
describe JwtController do
let(:service) { double(execute: {}) }
let(:service_class) { double(new: service) }
let(:service_name) { 'test' }
let(:parameters) { { service: service_name } }
before { stub_const('JwtController::SERVICES', service_name => service_class) }
context 'existing service' do
subject! { get '/jwt/auth', parameters }
it { expect(response.status).to eq(200) }
context 'returning custom http code' do
let(:service) { double(execute: { http_status: 505 }) }
it { expect(response.status).to eq(505) }
end
end
context 'when using authorized request' do
context 'using CI token' do
let(:project) { create(:empty_project, runners_token: 'token', builds_enabled: builds_enabled) }
let(:headers) { { authorization: credentials('gitlab_ci_token', project.runners_token) } }
subject! { get '/jwt/auth', parameters, headers }
context 'project with enabled CI' do
let(:builds_enabled) { true }
it { expect(service_class).to have_received(:new).with(project, nil, parameters) }
end
context 'project with disabled CI' do
let(:builds_enabled) { false }
it { expect(response.status).to eq(403) }
end
end
context 'using User login' do
let(:user) { create(:user) }
let(:headers) { { authorization: credentials('user', 'password') } }
before { expect_any_instance_of(Gitlab::Auth).to receive(:find).with('user', 'password').and_return(user) }
subject! { get '/jwt/auth', parameters, headers }
it { expect(service_class).to have_received(:new).with(nil, user, parameters) }
end
context 'using invalid login' do
let(:headers) { { authorization: credentials('invalid', 'password') } }
subject! { get '/jwt/auth', parameters, headers }
it { expect(response.status).to eq(403) }
end
end
context 'unknown service' do
subject! { get '/jwt/auth', service: 'unknown' }
it { expect(response.status).to eq(404) }
end
def credentials(login, password)
ActionController::HttpAuthentication::Basic.encode_credentials(login, password)
end
end
require 'spec_helper'
describe Auth::ContainerRegistryAuthenticationService, services: true do
let(:current_project) { nil }
let(:current_user) { nil }
let(:current_params) { {} }
let(:rsa_key) { OpenSSL::PKey::RSA.generate(512) }
let(:registry_settings) do
{
enabled: true,
issuer: 'rspec',
key: nil
}
end
let(:payload) { JWT.decode(subject[:token], rsa_key).first }
subject { described_class.new(current_project, current_user, current_params).execute }
before do
allow(Gitlab.config.registry).to receive_messages(registry_settings)
allow_any_instance_of(JSONWebToken::RSAToken).to receive(:key).and_return(rsa_key)
end
shared_examples 'an authenticated' do
it { is_expected.to include(:token) }
it { expect(payload).to include('access') }
end
shared_examples 'a accessible' do
let(:access) do
[{
'type' => 'repository',
'name' => project.path_with_namespace,
'actions' => actions,
}]
end
it_behaves_like 'an authenticated'
it { expect(payload).to include('access' => access) }
end
shared_examples 'a pullable' do
it_behaves_like 'a accessible' do
let(:actions) { ['pull'] }
end
end
shared_examples 'a pushable' do
it_behaves_like 'a accessible' do
let(:actions) { ['push'] }
end
end
shared_examples 'a pullable and pushable' do
it_behaves_like 'a accessible' do
let(:actions) { ['pull', 'push'] }
end
end
shared_examples 'a forbidden' do
it { is_expected.to include(http_status: 403) }
it { is_expected.to_not include(:token) }
end
context 'user authorization' do
let(:project) { create(:project) }
let(:current_user) { create(:user) }
context 'allow to use offline_token' do
let(:current_params) do
{ offline_token: true }
end
it_behaves_like 'an authenticated'
end
context 'allow developer to push images' do
before { project.team << [current_user, :developer] }
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:push" }
end
it_behaves_like 'a pushable'
end
context 'allow reporter to pull images' do
before { project.team << [current_user, :reporter] }
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:pull" }
end
it_behaves_like 'a pullable'
end
context 'return a least of privileges' do
before { project.team << [current_user, :reporter] }
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:push,pull" }
end
it_behaves_like 'a pullable'
end
context 'disallow guest to pull or push images' do
before { project.team << [current_user, :guest] }
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:pull,push" }
end
it_behaves_like 'a forbidden'
end
end
context 'project authorization' do
let(:current_project) { create(:empty_project) }
context 'disallow to use offline_token' do
let(:current_params) do
{ offline_token: true }
end
it_behaves_like 'a forbidden'
end
context 'allow to pull and push images' do
let(:current_params) do
{ scope: "repository:#{current_project.path_with_namespace}:pull,push" }
end
it_behaves_like 'a pullable and pushable' do
let(:project) { current_project }
end
end
context 'for other projects' do
context 'when pulling' do
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:pull" }
end
context 'allow for public' do
let(:project) { create(:empty_project, :public) }
it_behaves_like 'a pullable'
end
context 'disallow for private' do
let(:project) { create(:empty_project, :private) }
it_behaves_like 'a forbidden'
end
end
context 'when pushing' do
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:push" }
end
context 'disallow for all' do
let(:project) { create(:empty_project, :public) }
it_behaves_like 'a forbidden'
end
end
end
end
context 'unauthorized' do
context 'disallow to use offline_token' do
let(:current_params) do
{ offline_token: true }
end
it_behaves_like 'a forbidden'
end
context 'for invalid scope' do
let(:current_params) do
{ scope: 'invalid:aa:bb' }
end
it_behaves_like 'a forbidden'
end
context 'for private project' do
let(:project) { create(:empty_project, :private) }
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:pull" }
end
it_behaves_like 'a forbidden'
end
context 'for public project' do
let(:project) { create(:empty_project, :public) }
context 'when pulling and pushing' do
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:pull,push" }
end
it_behaves_like 'a pullable'
end
context 'when pushing' do
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:push" }
end
it_behaves_like 'a forbidden'
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