Commit 8a26f836 authored by James Lopez's avatar James Lopez

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/project-import_url

parents e937f312 383ead5d
...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.6.0 (unreleased) v 8.6.0 (unreleased)
- Add ability to move issue to another project - Add ability to move issue to another project
- Prevent tokens in the import URL to be showed by the UI
- Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu) - Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
- Make HTTP(s) label consistent on clone bar (Stan Hu) - Make HTTP(s) label consistent on clone bar (Stan Hu)
- Add confidential issues - Add confidential issues
...@@ -60,6 +61,7 @@ v 8.6.0 (unreleased) ...@@ -60,6 +61,7 @@ v 8.6.0 (unreleased)
- User deletion is now done in the background so the request can not time out - User deletion is now done in the background so the request can not time out
- Canceled builds are now ignored in compound build status if marked as `allowed to fail` - Canceled builds are now ignored in compound build status if marked as `allowed to fail`
- Trigger a todo for mentions on commits page - Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.8 v 8.5.8
- Bump Git version requirement to 2.7.4 - Bump Git version requirement to 2.7.4
......
...@@ -1064,6 +1064,3 @@ DEPENDENCIES ...@@ -1064,6 +1064,3 @@ DEPENDENCIES
web-console (~> 2.0) web-console (~> 2.0)
webmock (~> 1.21.0) webmock (~> 1.21.0)
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH
1.11.2
...@@ -139,7 +139,7 @@ $ -> ...@@ -139,7 +139,7 @@ $ ->
# Initialize tooltips # Initialize tooltips
$('body').tooltip( $('body').tooltip(
selector: '.has_tooltip, [data-toggle="tooltip"]' selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) -> placement: (_, el) ->
$el = $(el) $el = $(el)
$el.data('placement') || 'bottom' $el.data('placement') || 'bottom'
......
...@@ -122,7 +122,7 @@ class @AwardsHandler ...@@ -122,7 +122,7 @@ class @AwardsHandler
nodes = [] nodes = []
nodes.push( nodes.push(
"<button class='btn award-control js-emoji-btn has_tooltip active' title='me'>", "<button class='btn award-control js-emoji-btn has-tooltip active' title='me'>",
"<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>", "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
"<span class='award-control-text js-counter'>1</span>", "<span class='award-control-text js-counter'>1</span>",
"</button>" "</button>"
......
...@@ -95,4 +95,4 @@ window.ContributorsStatGraphUtil = ...@@ -95,4 +95,4 @@ window.ContributorsStatGraphUtil =
if date_range is null || date_range[0] <= new Date(date) <= date_range[1] if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
true true
else else
false false
\ No newline at end of file
...@@ -107,10 +107,28 @@ ...@@ -107,10 +107,28 @@
margin: 0; margin: 0;
font-size: 23px; font-size: 23px;
font-weight: normal; font-weight: normal;
margin: 16px 0 5px 0; margin: 16px 0 5px;
color: #4c4e54; color: #4c4e54;
font-size: 23px; font-size: 23px;
line-height: 1.1; line-height: 1.1;
h1 {
color: #313236;
margin-bottom: 6px;
font-size: 23px;
}
.visibility-icon {
display: inline-block;
margin-left: 5px;
font-size: 18px;
color: $gray;
}
p {
padding: 0 $gl-padding;
color: #5c5d5e;
}
} }
.cover-desc { .cover-desc {
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
width: 240px; width: 240px;
margin-top: 2px; margin-top: 2px;
margin-bottom: 0; margin-bottom: 0;
padding: 10px 10px; padding: 10px;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
background-color: $dropdown-bg; background-color: $dropdown-bg;
......
...@@ -117,4 +117,4 @@ body { ...@@ -117,4 +117,4 @@ body {
&.ui_violet { &.ui_violet {
@include gitlab-theme(#98c, $theme-violet, #436, #325); @include gitlab-theme(#98c, $theme-violet, #436, #325);
} }
} }
\ No newline at end of file
...@@ -16,7 +16,7 @@ body { ...@@ -16,7 +16,7 @@ body {
} }
.container .content { .container .content {
margin: 0 0; margin: 0;
} }
.navless-container { .navless-container {
......
/** /**
* Generic mixins * Generic mixins
*/ */
@mixin box-shadow($shadow) { @mixin box-shadow($shadow) {
-webkit-box-shadow: $shadow; -webkit-box-shadow: $shadow;
-moz-box-shadow: $shadow; -moz-box-shadow: $shadow;
-ms-box-shadow: $shadow; -ms-box-shadow: $shadow;
......
...@@ -39,8 +39,8 @@ ...@@ -39,8 +39,8 @@
h1 { h1 {
font-size: 1.3em; font-size: 1.3em;
font-weight: 600; font-weight: 600;
margin: 24px 0 12px 0; margin: 24px 0 12px;
padding: 0 0 10px 0; padding: 0 0 10px;
border-bottom: 1px solid #e7e9ed; border-bottom: 1px solid #e7e9ed;
color: #313236; color: #313236;
} }
...@@ -48,27 +48,27 @@ ...@@ -48,27 +48,27 @@
h2 { h2 {
font-size: 1.2em; font-size: 1.2em;
font-weight: 600; font-weight: 600;
margin: 24px 0 12px 0; margin: 24px 0 12px;
color: #313236; color: #313236;
} }
h3 { h3 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 1.1em; font-size: 1.1em;
} }
h4 { h4 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.98em; font-size: 0.98em;
} }
h5 { h5 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.95em; font-size: 0.95em;
} }
h6 { h6 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.90em; font-size: 0.90em;
} }
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
color: #7f8fa4; color: #7f8fa4;
font-size: inherit; font-size: inherit;
padding: 8px 21px; padding: 8px 21px;
margin: 12px 0 12px; margin: 12px 0;
border-left: 3px solid #e7e9ed; border-left: 3px solid #e7e9ed;
} }
...@@ -88,13 +88,13 @@ ...@@ -88,13 +88,13 @@
p { p {
color: #5c5d5e; color: #5c5d5e;
margin: 6px 0 0 0; margin: 6px 0 0;
} }
table { table {
@extend .table; @extend .table;
@extend .table-bordered; @extend .table-bordered;
margin: 12px 0 12px 0; margin: 12px 0;
color: #5c5d5e; color: #5c5d5e;
th { th {
background: #f8fafc; background: #f8fafc;
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
} }
pre { pre {
margin: 12px 0 12px 0; margin: 12px 0;
font-size: 13px; font-size: 13px;
line-height: 1.6em; line-height: 1.6em;
overflow-x: auto; overflow-x: auto;
...@@ -191,7 +191,7 @@ body { ...@@ -191,7 +191,7 @@ body {
line-height: 1.3; line-height: 1.3;
font-size: 1.25em; font-size: 1.25em;
font-weight: 600; font-weight: 600;
margin: 12px 7px 12px 7px; margin: 12px 7px;
} }
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
......
...@@ -34,9 +34,9 @@ ...@@ -34,9 +34,9 @@
background: #fff background: #fff
} }
.visibility-levels { .visibility-levels {
.controls { .controls {
margin-bottom: 9px; margin-bottom: 9px;
} }
i { i {
......
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
} }
.image-info { .image-info {
font-size: 12px; font-size: 12px;
margin: 5px 0 0 0; margin: 5px 0 0;
color: grey; color: grey;
} }
......
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
.block { .block {
width: $sidebar_collapsed_width - 1px; width: $sidebar_collapsed_width - 1px;
margin-left: -19px; margin-left: -19px;
padding: 15px 0 0 0; padding: 15px 0 0;
border-bottom: none; border-bottom: none;
overflow: hidden; overflow: hidden;
} }
...@@ -273,12 +273,12 @@ ...@@ -273,12 +273,12 @@
} }
.participants-list { .participants-list {
margin: -5px -5px; margin: -5px;
} }
.participants-author { .participants-author {
display: inline-block; display: inline-block;
padding: 5px 5px; padding: 5px;
.author_link { .author_link {
display: block; display: block;
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
.login-heading h3 { .login-heading h3 {
font-weight: 300; font-weight: 300;
line-height: 1.5; line-height: 1.5;
margin: 0 0 10px 0; margin: 0 0 10px;
} }
.login-footer { .login-footer {
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
} }
.account-well { .account-well {
padding: 10px 10px; padding: 10px;
background-color: $help-well-bg; background-color: $help-well-bg;
border: 1px solid $help-well-border; border: 1px solid $help-well-border;
border-radius: $border-radius-base; border-radius: $border-radius-base;
......
...@@ -68,28 +68,6 @@ ...@@ -68,28 +68,6 @@
} }
} }
.project-home-desc {
h1 {
color: #313236;
margin: 0;
margin-bottom: 6px;
font-size: 23px;
font-weight: normal;
}
.visibility-icon {
display: inline-block;
margin-left: 5px;
font-size: 18px;
color: $gray;
}
p {
padding: 0 $gl-padding;
color: #5c5d5e;
}
}
.project-repo-buttons { .project-repo-buttons {
margin-top: 20px; margin-top: 20px;
margin-bottom: 0; margin-bottom: 0;
...@@ -333,7 +311,7 @@ pre.light-well { ...@@ -333,7 +311,7 @@ pre.light-well {
} }
.git-empty { .git-empty {
margin: 0 7px 0 7px; margin: 0 7px;
h5 { h5 {
color: #5c5d5e; color: #5c5d5e;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#contributors { #contributors {
.contributors-list { .contributors-list {
margin: 0 0 10px 0; margin: 0 0 10px;
list-style: none; list-style: none;
padding: 0; padding: 0;
} }
......
...@@ -21,19 +21,19 @@ ...@@ -21,19 +21,19 @@
$l-white: #fff; $l-white: #fff;
.term-bold { .term-bold {
font-weight: bold; font-weight: bold;
} }
.term-italic { .term-italic {
font-style: italic; font-style: italic;
} }
.term-conceal { .term-conceal {
visibility: hidden; visibility: hidden;
} }
.term-underline { .term-underline {
text-decoration: underline; text-decoration: underline;
} }
.term-cross { .term-cross {
text-decoration: line-through; text-decoration: line-through;
} }
.term-fg-black { .term-fg-black {
......
...@@ -61,6 +61,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -61,6 +61,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:session_expire_delay, :session_expire_delay,
:default_project_visibility, :default_project_visibility,
:default_snippet_visibility, :default_snippet_visibility,
:default_group_visibility,
:restricted_signup_domains_raw, :restricted_signup_domains_raw,
:version_check_enabled, :version_check_enabled,
:admin_notification_email, :admin_notification_email,
......
...@@ -59,6 +59,6 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -59,6 +59,6 @@ class Admin::GroupsController < Admin::ApplicationController
end end
def group_params def group_params
params.require(:group).permit(:name, :description, :path, :avatar) params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level)
end end
end end
class Admin::ProjectsController < Admin::ApplicationController class Admin::ProjectsController < Admin::ApplicationController
before_action :project, only: [:show, :transfer] before_action :project, only: [:show, :transfer]
before_action :group, only: [:show, :transfer] before_action :group, only: [:show, :transfer]
before_action :repository, only: [:show, :transfer]
def index def index
@projects = Project.all @projects = Project.all
......
...@@ -23,7 +23,6 @@ class ApplicationController < ActionController::Base ...@@ -23,7 +23,6 @@ class ApplicationController < ActionController::Base
helper_method :abilities, :can?, :current_application_settings helper_method :abilities, :can?, :current_application_settings
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
helper_method :repository, :can_collaborate_with_project?
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception) log_exception(exception)
...@@ -116,47 +115,6 @@ class ApplicationController < ActionController::Base ...@@ -116,47 +115,6 @@ class ApplicationController < ActionController::Base
abilities.allowed?(object, action, subject) abilities.allowed?(object, action, subject)
end end
def project
unless @project
namespace = params[:namespace_id]
id = params[:project_id] || params[:id]
# Redirect from
# localhost/group/project.git
# to
# localhost/group/project
#
if id =~ /\.git\Z/
redirect_to request.original_url.gsub(/\.git\/?\Z/, '') and return
end
project_path = "#{namespace}/#{id}"
@project = Project.find_with_namespace(project_path)
if @project and can?(current_user, :read_project, @project)
if @project.path_with_namespace != project_path
redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return
end
@project
elsif current_user.nil?
@project = nil
authenticate_user!
else
@project = nil
render_404 and return
end
end
@project
end
def repository
@repository ||= project.repository
end
def authorize_project!(action)
return access_denied! unless can?(current_user, action, project)
end
def access_denied! def access_denied!
render "errors/access_denied", layout: "errors", status: 404 render "errors/access_denied", layout: "errors", status: 404
end end
...@@ -165,14 +123,6 @@ class ApplicationController < ActionController::Base ...@@ -165,14 +123,6 @@ class ApplicationController < ActionController::Base
render "errors/git_not_found.html", layout: "errors", status: 404 render "errors/git_not_found.html", layout: "errors", status: 404
end end
def method_missing(method_sym, *arguments, &block)
if method_sym.to_s =~ /\Aauthorize_(.*)!\z/
authorize_project!($1.to_sym)
else
super
end
end
def render_403 def render_403
head :forbidden head :forbidden
end end
...@@ -181,10 +131,6 @@ class ApplicationController < ActionController::Base ...@@ -181,10 +131,6 @@ class ApplicationController < ActionController::Base
render file: Rails.root.join("public", "404"), layout: false, status: "404" render file: Rails.root.join("public", "404"), layout: false, status: "404"
end end
def require_non_empty_project
redirect_to @project if @project.empty_repo?
end
def no_cache_headers def no_cache_headers
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
response.headers["Pragma"] = "no-cache" response.headers["Pragma"] = "no-cache"
...@@ -410,13 +356,6 @@ class ApplicationController < ActionController::Base ...@@ -410,13 +356,6 @@ class ApplicationController < ActionController::Base
current_user.nil? && root_path == request.path current_user.nil? && root_path == request.path
end end
def can_collaborate_with_project?(project = nil)
project ||= @project
can?(current_user, :push_code, project) ||
(current_user && current_user.already_forked?(project))
end
private private
def set_default_sort def set_default_sort
......
module IssuableActions
extend ActiveSupport::Concern
included do
before_action :authorize_destroy_issuable!, only: :destroy
end
def destroy
issuable.destroy
name = issuable.class.name.titleize.downcase
flash[:notice] = "The #{name} was successfully deleted."
redirect_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class])
end
private
def authorize_destroy_issuable!
unless current_user.can?(:"destroy_#{issuable.to_ability_name}", issuable)
return access_denied!
end
end
end
class Explore::GroupsController < Explore::ApplicationController class Explore::GroupsController < Explore::ApplicationController
def index def index
@groups = Group.order_id_desc @groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]) @groups = @groups.page(params[:page])
......
class Groups::ApplicationController < ApplicationController class Groups::ApplicationController < ApplicationController
layout 'group' layout 'group'
skip_before_action :authenticate_user!
before_action :group before_action :group
private private
def group def group
@group ||= Group.find_by(path: params[:group_id]) unless @group
end id = params[:group_id] || params[:id]
@group = Group.find_by(path: id)
unless @group && can?(current_user, :read_group, @group)
@group = nil
def authorize_read_group! if current_user.nil?
unless @group and can?(current_user, :read_group, @group) authenticate_user!
if current_user.nil? else
return authenticate_user! render_404
else end
return render_404
end end
end end
@group
end
def group_projects
@projects ||= GroupProjectsFinder.new(group).execute(current_user)
end end
def authorize_admin_group! def authorize_admin_group!
......
class Groups::AvatarsController < Groups::ApplicationController class Groups::AvatarsController < Groups::ApplicationController
before_action :authorize_admin_group!
def destroy def destroy
@group.remove_avatar! @group.remove_avatar!
@group.save @group.save
......
class Groups::GroupMembersController < Groups::ApplicationController class Groups::GroupMembersController < Groups::ApplicationController
skip_before_action :authenticate_user!, only: [:index]
# Authorize # Authorize
before_action :authorize_read_group!
before_action :authorize_admin_group_member!, except: [:index, :leave] before_action :authorize_admin_group_member!, except: [:index, :leave]
def index def index
......
class Groups::MilestonesController < Groups::ApplicationController class Groups::MilestonesController < Groups::ApplicationController
include GlobalMilestones include GlobalMilestones
before_action :projects before_action :group_projects
before_action :milestones, only: [:index] before_action :milestones, only: [:index]
before_action :milestone, only: [:show, :update] before_action :milestone, only: [:show, :update]
before_action :authorize_group_milestone!, only: [:create, :update] before_action :authorize_admin_milestones!, only: [:new, :create, :update]
def index def index
end end
...@@ -17,7 +17,7 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -17,7 +17,7 @@ class Groups::MilestonesController < Groups::ApplicationController
project_ids = params[:milestone][:project_ids] project_ids = params[:milestone][:project_ids]
title = milestone_params[:title] title = milestone_params[:title]
@group.projects.where(id: project_ids).each do |project| @projects.where(id: project_ids).each do |project|
Milestones::CreateService.new(project, current_user, milestone_params).execute Milestones::CreateService.new(project, current_user, milestone_params).execute
end end
...@@ -37,7 +37,7 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -37,7 +37,7 @@ class Groups::MilestonesController < Groups::ApplicationController
private private
def authorize_group_milestone! def authorize_admin_milestones!
return render_404 unless can?(current_user, :admin_milestones, group) return render_404 unless can?(current_user, :admin_milestones, group)
end end
...@@ -48,8 +48,4 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -48,8 +48,4 @@ class Groups::MilestonesController < Groups::ApplicationController
def milestone_path(title) def milestone_path(title)
group_milestone_path(@group, title.to_slug.to_s, title: title) group_milestone_path(@group, title.to_slug.to_s, title: title)
end end
def projects
@projects ||= @group.projects
end
end end
...@@ -5,16 +5,15 @@ class GroupsController < Groups::ApplicationController ...@@ -5,16 +5,15 @@ class GroupsController < Groups::ApplicationController
respond_to :html respond_to :html
skip_before_action :authenticate_user!, only: [:index, :show, :issues, :merge_requests] before_action :authenticate_user!, only: [:new, :create]
before_action :group, except: [:index, :new, :create] before_action :group, except: [:index, :new, :create]
# Authorize # Authorize
before_action :authorize_read_group!, except: [:index, :show, :new, :create, :autocomplete]
before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects]
before_action :authorize_create_group!, only: [:new, :create] before_action :authorize_create_group!, only: [:new, :create]
# Load group projects # Load group projects
before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete] before_action :group_projects, only: [:show, :projects, :activity, :issues, :merge_requests]
before_action :event_filter, only: [:activity] before_action :event_filter, only: [:activity]
layout :determine_layout layout :determine_layout
...@@ -28,11 +27,9 @@ class GroupsController < Groups::ApplicationController ...@@ -28,11 +27,9 @@ class GroupsController < Groups::ApplicationController
end end
def create def create
@group = Group.new(group_params) @group = Groups::CreateService.new(current_user, group_params).execute
@group.name = @group.path.dup unless @group.name
if @group.save if @group.persisted?
@group.add_owner(current_user)
redirect_to @group, notice: "Group '#{@group.name}' was successfully created." redirect_to @group, notice: "Group '#{@group.name}' was successfully created."
else else
render action: "new" render action: "new"
...@@ -41,12 +38,13 @@ class GroupsController < Groups::ApplicationController ...@@ -41,12 +38,13 @@ class GroupsController < Groups::ApplicationController
def show def show
@last_push = current_user.recent_push if current_user @last_push = current_user.recent_push if current_user
@projects = @projects.includes(:namespace) @projects = @projects.includes(:namespace)
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]) if params[:filter_projects].blank? @projects = @projects.page(params[:page]) if params[:filter_projects].blank?
@shared_projects = @group.shared_projects @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -83,7 +81,7 @@ class GroupsController < Groups::ApplicationController ...@@ -83,7 +81,7 @@ class GroupsController < Groups::ApplicationController
end end
def update def update
if @group.update_attributes(group_params) if Groups::UpdateService.new(@group, current_user, group_params).execute
redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated." redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
else else
render action: "edit" render action: "edit"
...@@ -98,26 +96,6 @@ class GroupsController < Groups::ApplicationController ...@@ -98,26 +96,6 @@ class GroupsController < Groups::ApplicationController
protected protected
def group
@group ||= Group.find_by(path: params[:id])
@group || render_404
end
def load_projects
@projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity
end
# Dont allow unauthorized access to group
def authorize_read_group!
unless @group and (@projects.present? or can?(current_user, :read_group, @group))
if current_user.nil?
return authenticate_user!
else
return render_404
end
end
end
def authorize_create_group! def authorize_create_group!
unless can?(current_user, :create_group, nil) unless can?(current_user, :create_group, nil)
return render_404 return render_404
...@@ -135,7 +113,7 @@ class GroupsController < Groups::ApplicationController ...@@ -135,7 +113,7 @@ class GroupsController < Groups::ApplicationController
end end
def group_params def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :public, :share_with_group_lock) params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock)
end end
def load_events def load_events
......
...@@ -14,7 +14,7 @@ class NamespacesController < ApplicationController ...@@ -14,7 +14,7 @@ class NamespacesController < ApplicationController
if user if user
redirect_to user_path(user) redirect_to user_path(user)
elsif group elsif group && can?(current_user, :read_group, namespace)
redirect_to group_path(group) redirect_to group_path(group)
elsif current_user.nil? elsif current_user.nil?
authenticate_user! authenticate_user!
......
class Projects::ApplicationController < ApplicationController class Projects::ApplicationController < ApplicationController
skip_before_action :authenticate_user!
before_action :project before_action :project
before_action :repository before_action :repository
layout 'project' layout 'project'
def authenticate_user! helper_method :repository, :can_collaborate_with_project?
# Restrict access to Projects area only
# for non-signed users private
if !current_user
def project
unless @project
namespace = params[:namespace_id]
id = params[:project_id] || params[:id] id = params[:project_id] || params[:id]
project_with_namespace = "#{params[:namespace_id]}/#{id}"
@project = Project.find_with_namespace(project_with_namespace)
return if @project && @project.public? # Redirect from
# localhost/group/project.git
# to
# localhost/group/project
#
if id =~ /\.git\Z/
redirect_to request.original_url.gsub(/\.git\/?\Z/, '')
return
end
project_path = "#{namespace}/#{id}"
@project = Project.find_with_namespace(project_path)
if @project && can?(current_user, :read_project, @project)
if @project.path_with_namespace != project_path
redirect_to request.original_url.gsub(project_path, @project.path_with_namespace)
end
else
@project = nil
if current_user.nil?
authenticate_user!
else
render_404
end
end
end
@project
end
def repository
@repository ||= project.repository
end
def can_collaborate_with_project?(project = nil)
project ||= @project
can?(current_user, :push_code, project) ||
(current_user && current_user.already_forked?(project))
end
def authorize_project!(action)
return access_denied! unless can?(current_user, action, project)
end
def method_missing(method_sym, *arguments, &block)
if method_sym.to_s =~ /\Aauthorize_(.*)!\z/
authorize_project!($1.to_sym)
else
super
end end
end
super def require_non_empty_project
redirect_to namespace_project_path(@project.namespace, @project) if @project.empty_repo?
end end
def require_branch_head def require_branch_head
...@@ -26,8 +80,6 @@ class Projects::ApplicationController < ApplicationController ...@@ -26,8 +80,6 @@ class Projects::ApplicationController < ApplicationController
end end
end end
private
def apply_diff_view_cookie! def apply_diff_view_cookie!
view = params[:view] || cookies[:diff_view] view = params[:view] || cookies[:diff_view]
cookies.permanent[:diff_view] = params[:view] = view if view cookies.permanent[:diff_view] = params[:view] = view if view
......
class Projects::AvatarsController < Projects::ApplicationController class Projects::AvatarsController < Projects::ApplicationController
include BlobHelper include BlobHelper
before_action :project before_action :authorize_admin_project!, only: [:destroy]
def show def show
@blob = @repository.blob_at_branch('master', @project.avatar_in_git) @blob = @repository.blob_at_branch('master', @project.avatar_in_git)
......
class Projects::IssuesController < Projects::ApplicationController class Projects::IssuesController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include IssuableActions
before_action :module_enabled before_action :module_enabled
before_action :issue, only: [:edit, :update, :show] before_action :issue, only: [:edit, :update, :show]
...@@ -133,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -133,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
alias_method :subscribable_resource, :issue alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
def authorize_read_issue! def authorize_read_issue!
return render_404 unless can?(current_user, :read_issue, @issue) return render_404 unless can?(current_user, :read_issue, @issue)
......
class Projects::MergeRequestsController < Projects::ApplicationController class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include DiffHelper include DiffHelper
include IssuableActions
before_action :module_enabled before_action :module_enabled
before_action :merge_request, only: [ before_action :merge_request, only: [
...@@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request ||= @project.merge_requests.find_by!(iid: params[:id]) @merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
end end
alias_method :subscribable_resource, :merge_request alias_method :subscribable_resource, :merge_request
alias_method :issuable, :merge_request
def closes_issues def closes_issues
@closes_issues ||= @merge_request.closes_issues @closes_issues ||= @merge_request.closes_issues
......
class Projects::UploadsController < Projects::ApplicationController class Projects::UploadsController < Projects::ApplicationController
skip_before_action :authenticate_user!, :reject_blocked!, :project, skip_before_action :reject_blocked!, :project,
:repository, if: -> { action_name == 'show' && image? } :repository, if: -> { action_name == 'show' && image? }
before_action :authorize_upload_file!, only: [:create]
def create def create
link_to_file = ::Projects::UploadService.new(project, params[:file]). link_to_file = ::Projects::UploadService.new(project, params[:file]).
execute execute
...@@ -26,6 +28,8 @@ class Projects::UploadsController < Projects::ApplicationController ...@@ -26,6 +28,8 @@ class Projects::UploadsController < Projects::ApplicationController
send_file uploader.file.path, disposition: disposition send_file uploader.file.path, disposition: disposition
end end
private
def uploader def uploader
return @uploader if defined?(@uploader) return @uploader if defined?(@uploader)
......
class ProjectsController < ApplicationController class ProjectsController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
skip_before_action :authenticate_user!, only: [:show, :activity] before_action :authenticate_user!, except: [:show, :activity]
before_action :project, except: [:new, :create] before_action :project, except: [:new, :create]
before_action :repository, except: [:new, :create] before_action :repository, except: [:new, :create]
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists? before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
......
...@@ -108,7 +108,7 @@ class UsersController < ApplicationController ...@@ -108,7 +108,7 @@ class UsersController < ApplicationController
end end
def load_groups def load_groups
@groups = @user.groups.order_id_desc @groups = JoinedGroupsFinder.new(@user).execute(current_user)
end end
def projects_for_current_user def projects_for_current_user
......
class ContributedProjectsFinder class ContributedProjectsFinder < UnionFinder
def initialize(user) def initialize(user)
@user = user @user = user
end end
...@@ -11,27 +11,19 @@ class ContributedProjectsFinder ...@@ -11,27 +11,19 @@ class ContributedProjectsFinder
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def execute(current_user = nil) def execute(current_user = nil)
if current_user segments = all_projects(current_user)
relation = projects_visible_to_user(current_user)
else
relation = public_projects
end
relation.includes(:namespace).order_id_desc find_union(segments, Project).includes(:namespace).order_id_desc
end end
private private
def projects_visible_to_user(current_user) def all_projects(current_user)
authorized = @user.contributed_projects.visible_to_user(current_user) projects = []
union = Gitlab::SQL::Union. projects << @user.contributed_projects.visible_to_user(current_user) if current_user
new([authorized.select(:id), public_projects.select(:id)]) projects << @user.contributed_projects.public_to_user(current_user)
Project.where("projects.id IN (#{union.to_sql})") projects
end
def public_projects
@user.contributed_projects.public_only
end end
end end
class GroupProjectsFinder < UnionFinder
def initialize(group, options = {})
@group = group
@options = options
end
def execute(current_user = nil)
segments = group_projects(current_user)
find_union(segments, Project)
end
private
def group_projects(current_user)
only_owned = @options.fetch(:only_owned, false)
only_shared = @options.fetch(:only_shared, false)
projects = []
if current_user
if @group.users.include?(current_user)
projects << @group.projects unless only_shared
projects << @group.shared_projects unless only_owned
else
unless only_shared
projects << @group.projects.visible_to_user(current_user)
projects << @group.projects.public_to_user(current_user)
end
unless only_owned
projects << @group.shared_projects.visible_to_user(current_user)
projects << @group.shared_projects.public_to_user(current_user)
end
end
else
projects << @group.projects.public_only unless only_shared
projects << @group.shared_projects.public_only unless only_owned
end
projects
end
end
class GroupsFinder < UnionFinder
def execute(current_user = nil)
segments = all_groups(current_user)
find_union(segments, Group).order_id_desc
end
private
def all_groups(current_user)
groups = []
groups << current_user.authorized_groups if current_user
groups << Group.unscoped.public_to_user(current_user)
groups
end
end
...@@ -80,9 +80,10 @@ class IssuableFinder ...@@ -80,9 +80,10 @@ class IssuableFinder
@projects = project @projects = project
elsif current_user && params[:authorized_only].presence && !current_user_related? elsif current_user && params[:authorized_only].presence && !current_user_related?
@projects = current_user.authorized_projects.reorder(nil) @projects = current_user.authorized_projects.reorder(nil)
elsif group
@projects = GroupProjectsFinder.new(group).execute(current_user).reorder(nil)
else else
@projects = ProjectsFinder.new.execute(current_user, group: group). @projects = ProjectsFinder.new.execute(current_user).reorder(nil)
reorder(nil)
end end
end end
...@@ -171,14 +172,12 @@ class IssuableFinder ...@@ -171,14 +172,12 @@ class IssuableFinder
def by_scope(items) def by_scope(items)
case params[:scope] case params[:scope]
when 'created-by-me', 'authored' then when 'created-by-me', 'authored'
items.where(author_id: current_user.id) items.where(author_id: current_user.id)
when 'all' then when 'assigned-to-me'
items
when 'assigned-to-me' then
items.where(assignee_id: current_user.id) items.where(assignee_id: current_user.id)
else else
raise 'You must specify default scope' items
end end
end end
...@@ -198,8 +197,7 @@ class IssuableFinder ...@@ -198,8 +197,7 @@ class IssuableFinder
end end
def by_group(items) def by_group(items)
items = items.of_group(group) if group # Selection by group is already covered by `by_project` and `projects`
items items
end end
......
class JoinedGroupsFinder < UnionFinder
def initialize(user)
@user = user
end
# Finds the groups of the source user, optionally limited to those visible to
# the current user.
def execute(current_user = nil)
segments = all_groups(current_user)
find_union(segments, Group).order_id_desc
end
private
def all_groups(current_user)
groups = []
groups << @user.authorized_groups.visible_to_user(current_user) if current_user
groups << @user.authorized_groups.public_to_user(current_user)
groups
end
end
class PersonalProjectsFinder class PersonalProjectsFinder < UnionFinder
def initialize(user) def initialize(user)
@user = user @user = user
end end
...@@ -11,31 +11,19 @@ class PersonalProjectsFinder ...@@ -11,31 +11,19 @@ class PersonalProjectsFinder
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def execute(current_user = nil) def execute(current_user = nil)
if current_user segments = all_projects(current_user)
relation = projects_visible_to_user(current_user)
else
relation = public_projects
end
relation.includes(:namespace).order_id_desc find_union(segments, Project).includes(:namespace).order_id_desc
end end
private private
def projects_visible_to_user(current_user) def all_projects(current_user)
authorized = @user.personal_projects.visible_to_user(current_user) projects = []
union = Gitlab::SQL::Union. projects << @user.personal_projects.visible_to_user(current_user) if current_user
new([authorized.select(:id), public_and_internal_projects.select(:id)]) projects << @user.personal_projects.public_to_user(current_user)
Project.where("projects.id IN (#{union.to_sql})") projects
end
def public_projects
@user.personal_projects.public_only
end
def public_and_internal_projects
@user.personal_projects.public_and_internal_only
end end
end end
class ProjectsFinder class ProjectsFinder < UnionFinder
# Returns all projects, optionally including group projects a user has access
# to.
#
# ## Examples
#
# Retrieving all public projects:
#
# ProjectsFinder.new.execute
#
# Retrieving all public/internal projects and those the given user has access
# to:
#
# ProjectsFinder.new.execute(some_user)
#
# Retrieving all public/internal projects as well as the group's projects the
# user has access to:
#
# ProjectsFinder.new.execute(some_user, group: some_group)
#
# Returns an ActiveRecord::Relation.
def execute(current_user = nil, options = {}) def execute(current_user = nil, options = {})
group = options[:group] segments = all_projects(current_user)
if group find_union(segments, Project)
segments = group_projects(current_user, group)
else
segments = all_projects(current_user)
end
if segments.length > 1
union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
Project.where("projects.id IN (#{union.to_sql})")
else
segments.first
end
end end
private private
def group_projects(current_user, group)
return [group.projects.public_only] unless current_user
user_group_projects = [
group_projects_for_user(current_user, group),
group.shared_projects.visible_to_user(current_user)
]
if current_user.external?
user_group_projects << group.projects.public_only
else
user_group_projects << group.projects.public_and_internal_only
end
end
def all_projects(current_user) def all_projects(current_user)
return [public_projects] unless current_user projects = []
if current_user.external? projects << current_user.authorized_projects if current_user
[current_user.authorized_projects, public_projects] projects << Project.unscoped.public_to_user(current_user)
else
[current_user.authorized_projects, public_and_internal_projects]
end
end
def group_projects_for_user(current_user, group)
if group.users.include?(current_user)
group.projects
else
group.projects.visible_to_user(current_user)
end
end
def public_projects
Project.unscoped.public_only
end
def public_and_internal_projects projects
Project.unscoped.public_and_internal_only
end end
end end
class UnionFinder
def find_union(segments, klass)
if segments.length > 1
union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
klass.where("#{klass.table_name}.id IN (#{union.to_sql})")
else
segments.first
end
end
end
...@@ -27,7 +27,7 @@ module BlobHelper ...@@ -27,7 +27,7 @@ module BlobHelper
link_opts) link_opts)
if !on_top_of_branch?(project, ref) if !on_top_of_branch?(project, ref)
button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } button_tag "Edit", class: "btn btn-default disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref) elsif can_edit_blob?(blob, project, ref)
link_to "Edit", edit_path, class: 'btn' link_to "Edit", edit_path, class: 'btn'
elsif can?(current_user, :fork_project, project) elsif can?(current_user, :fork_project, project)
...@@ -50,9 +50,9 @@ module BlobHelper ...@@ -50,9 +50,9 @@ module BlobHelper
return unless blob return unless blob
if !on_top_of_branch?(project, ref) if !on_top_of_branch?(project, ref)
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' } button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
elsif blob.lfs_pointer? elsif blob.lfs_pointer?
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' } button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref) elsif can_edit_blob?(blob, project, ref)
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project) elsif can?(current_user, :fork_project, project)
......
...@@ -24,7 +24,7 @@ module ButtonHelper ...@@ -24,7 +24,7 @@ module ButtonHelper
def http_clone_button(project) def http_clone_button(project)
klass = 'http-selector' klass = 'http-selector'
klass << ' has_tooltip' if current_user.try(:require_password?) klass << ' has-tooltip' if current_user.try(:require_password?)
protocol = gitlab_config.protocol.upcase protocol = gitlab_config.protocol.upcase
...@@ -41,7 +41,7 @@ module ButtonHelper ...@@ -41,7 +41,7 @@ module ButtonHelper
def ssh_clone_button(project) def ssh_clone_button(project)
klass = 'ssh-selector' klass = 'ssh-selector'
klass << ' has_tooltip' if current_user.try(:require_ssh_key?) klass << ' has-tooltip' if current_user.try(:require_ssh_key?)
content_tag :a, 'SSH', content_tag :a, 'SSH',
class: klass, class: klass,
......
...@@ -182,7 +182,7 @@ module CommitsHelper ...@@ -182,7 +182,7 @@ module CommitsHelper
end end
options = { options = {
class: "commit-#{options[:source]}-link has_tooltip", class: "commit-#{options[:source]}-link has-tooltip",
data: { 'original-title'.to_sym => sanitize(source_email) } data: { 'original-title'.to_sym => sanitize(source_email) }
} }
......
...@@ -19,6 +19,10 @@ module GroupsHelper ...@@ -19,6 +19,10 @@ module GroupsHelper
end end
end end
def can_change_group_visibility_level?(group)
can?(current_user, :change_visibility_level, group)
end
def group_icon(group) def group_icon(group)
if group.is_a?(String) if group.is_a?(String)
group = Group.find_by(path: group) group = Group.find_by(path: group)
......
...@@ -56,7 +56,7 @@ module LabelsHelper ...@@ -56,7 +56,7 @@ module LabelsHelper
# Intentionally not using content_tag here so that this method can be called # Intentionally not using content_tag here so that this method can be called
# by LabelReferenceFilter # by LabelReferenceFilter
span = %(<span class="label color-label #{"has_tooltip" if tooltip}" ) + span = %(<span class="label color-label #{"has-tooltip" if tooltip}" ) +
%(style="background-color: #{label_color}; color: #{text_color}" ) + %(style="background-color: #{label_color}; color: #{text_color}" ) +
%(title="#{escape_once(label.description)}" data-container="body">) + %(title="#{escape_once(label.description)}" data-container="body">) +
%(#{escape_once(label.name)}#{label_suffix}</span>) %(#{escape_once(label.name)}#{label_suffix}</span>)
......
...@@ -52,7 +52,7 @@ module ProjectsHelper ...@@ -52,7 +52,7 @@ module ProjectsHelper
link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
else else
title = opts[:title].sub(":name", sanitize(author.name)) title = opts[:title].sub(":name", sanitize(author.name))
link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
end end
end end
......
...@@ -19,6 +19,8 @@ module VisibilityLevelHelper ...@@ -19,6 +19,8 @@ module VisibilityLevelHelper
case form_model case form_model
when Project when Project
project_visibility_level_description(level) project_visibility_level_description(level)
when Group
group_visibility_level_description(level)
when Snippet when Snippet
snippet_visibility_level_description(level, form_model) snippet_visibility_level_description(level, form_model)
end end
...@@ -35,6 +37,17 @@ module VisibilityLevelHelper ...@@ -35,6 +37,17 @@ module VisibilityLevelHelper
end end
end end
def group_visibility_level_description(level)
case level
when Gitlab::VisibilityLevel::PRIVATE
"The group and its projects can only be viewed by members."
when Gitlab::VisibilityLevel::INTERNAL
"The group and any internal projects can be viewed by any logged in user."
when Gitlab::VisibilityLevel::PUBLIC
"The group and any public projects can be viewed without any authentication."
end
end
def snippet_visibility_level_description(level, snippet = nil) def snippet_visibility_level_description(level, snippet = nil)
case level case level
when Gitlab::VisibilityLevel::PRIVATE when Gitlab::VisibilityLevel::PRIVATE
...@@ -50,6 +63,23 @@ module VisibilityLevelHelper ...@@ -50,6 +63,23 @@ module VisibilityLevelHelper
end end
end end
def visibility_icon_description(form_model)
case form_model
when Project
project_visibility_icon_description(form_model.visibility_level)
when Group
group_visibility_icon_description(form_model.visibility_level)
end
end
def group_visibility_icon_description(level)
"#{visibility_level_label(level)} - #{group_visibility_level_description(level)}"
end
def project_visibility_icon_description(level)
"#{visibility_level_label(level)} - #{project_visibility_level_description(level)}"
end
def visibility_level_label(level) def visibility_level_label(level)
Project.visibility_levels.key(level) Project.visibility_levels.key(level)
end end
...@@ -67,8 +97,11 @@ module VisibilityLevelHelper ...@@ -67,8 +97,11 @@ module VisibilityLevelHelper
current_application_settings.default_snippet_visibility current_application_settings.default_snippet_visibility
end end
def default_group_visibility
current_application_settings.default_group_visibility
end
def skip_level?(form_model, level) def skip_level?(form_model, level)
form_model.is_a?(Project) && form_model.is_a?(Project) && !form_model.visibility_level_allowed?(level)
!form_model.visibility_level_allowed?(level)
end end
end end
...@@ -85,7 +85,7 @@ class Ability ...@@ -85,7 +85,7 @@ class Ability
subject.group subject.group
end end
if group && group.projects.public_only.any? if group && group.public?
[:read_group] [:read_group]
else else
[] []
...@@ -114,6 +114,13 @@ class Ability ...@@ -114,6 +114,13 @@ class Ability
# Push abilities on the users team role # Push abilities on the users team role
rules.push(*project_team_rules(project.team, user)) rules.push(*project_team_rules(project.team, user))
if project.owner == user ||
(project.group && project.group.has_owner?(user)) ||
user.admin?
rules.push(*project_owner_rules)
end
if project.public? || (project.internal? && !user.external?) if project.public? || (project.internal? && !user.external?)
rules.push(*public_project_rules) rules.push(*public_project_rules)
...@@ -121,14 +128,6 @@ class Ability ...@@ -121,14 +128,6 @@ class Ability
rules << :read_build if project.public_builds? rules << :read_build if project.public_builds?
end end
if project.owner == user || user.admin?
rules.push(*project_admin_rules)
end
if project.group && project.group.has_owner?(user)
rules.push(*project_admin_rules)
end
if project.archived? if project.archived?
rules -= project_archived_rules rules -= project_archived_rules
end end
...@@ -171,7 +170,8 @@ class Ability ...@@ -171,7 +170,8 @@ class Ability
:read_note, :read_note,
:create_project, :create_project,
:create_issue, :create_issue,
:create_note :create_note,
:upload_file
] ]
end end
...@@ -228,14 +228,16 @@ class Ability ...@@ -228,14 +228,16 @@ class Ability
] ]
end end
def project_admin_rules def project_owner_rules
@project_admin_rules ||= project_master_rules + [ @project_owner_rules ||= project_master_rules + [
:change_namespace, :change_namespace,
:change_visibility_level, :change_visibility_level,
:rename_project, :rename_project,
:remove_project, :remove_project,
:archive_project, :archive_project,
:remove_fork_project :remove_fork_project,
:destroy_merge_request,
:destroy_issue
] ]
end end
...@@ -273,11 +275,9 @@ class Ability ...@@ -273,11 +275,9 @@ class Ability
def group_abilities(user, group) def group_abilities(user, group)
rules = [] rules = []
if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any? rules << :read_group if can_read_group?(user, group)
rules << :read_group
end
# Only group masters and group owners can create new projects in group # Only group masters and group owners can create new projects
if group.has_master?(user) || group.has_owner?(user) || user.admin? if group.has_master?(user) || group.has_owner?(user) || user.admin?
rules += [ rules += [
:create_projects, :create_projects,
...@@ -290,13 +290,23 @@ class Ability ...@@ -290,13 +290,23 @@ class Ability
rules += [ rules += [
:admin_group, :admin_group,
:admin_namespace, :admin_namespace,
:admin_group_member :admin_group_member,
:change_visibility_level
] ]
end end
rules.flatten rules.flatten
end end
def can_read_group?(user, group)
return true if user.admin?
return true if group.public?
return true if group.internal? && !user.external?
return true if group.users.include?(user)
GroupProjectsFinder.new(group).execute(user).any?
end
def namespace_abilities(user, namespace) def namespace_abilities(user, namespace)
rules = [] rules = []
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
# max_attachment_size :integer default(10), not null # max_attachment_size :integer default(10), not null
# default_project_visibility :integer # default_project_visibility :integer
# default_snippet_visibility :integer # default_snippet_visibility :integer
# default_group_visibility :integer
# restricted_signup_domains :text # restricted_signup_domains :text
# user_oauth_applications :boolean default(TRUE) # user_oauth_applications :boolean default(TRUE)
# after_sign_out_path :string(255) # after_sign_out_path :string(255)
......
...@@ -7,7 +7,10 @@ module InternalId ...@@ -7,7 +7,10 @@ module InternalId
end end
def set_iid def set_iid
max_iid = project.send(self.class.name.tableize).maximum(:iid) records = project.send(self.class.name.tableize)
records = records.with_deleted if self.paranoid?
max_iid = records.maximum(:iid)
self.iid = max_iid.to_i + 1 self.iid = max_iid.to_i + 1
end end
......
...@@ -58,6 +58,8 @@ module Issuable ...@@ -58,6 +58,8 @@ module Issuable
attr_mentionable :description, cache: true attr_mentionable :description, cache: true
participant :author, :assignee, :notes_with_associations participant :author, :assignee, :notes_with_associations
strip_attributes :title strip_attributes :title
acts_as_paranoid
end end
module ClassMethods module ClassMethods
......
...@@ -2,15 +2,16 @@ ...@@ -2,15 +2,16 @@
# #
# Table name: namespaces # Table name: namespaces
# #
# id :integer not null, primary key # id :integer not null, primary key
# name :string(255) not null # name :string(255) not null
# path :string(255) not null # path :string(255) not null
# owner_id :integer # owner_id :integer
# created_at :datetime # visibility_level :integer default(20), not null
# updated_at :datetime # created_at :datetime
# type :string(255) # updated_at :datetime
# description :string(255) default(""), not null # type :string(255)
# avatar :string(255) # description :string(255) default(""), not null
# avatar :string(255)
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -18,6 +19,7 @@ require 'file_size_validator' ...@@ -18,6 +19,7 @@ require 'file_size_validator'
class Group < Namespace class Group < Namespace
include Gitlab::ConfigHelper include Gitlab::ConfigHelper
include Gitlab::VisibilityLevel
include Referable include Referable
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
...@@ -27,6 +29,8 @@ class Group < Namespace ...@@ -27,6 +29,8 @@ class Group < Namespace
has_many :shared_projects, through: :project_group_links, source: :project has_many :shared_projects, through: :project_group_links, source: :project
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :visibility_level_allowed_by_projects
validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
mount_uploader :avatar, AvatarUploader mount_uploader :avatar, AvatarUploader
...@@ -74,6 +78,21 @@ class Group < Namespace ...@@ -74,6 +78,21 @@ class Group < Namespace
name name
end end
def visibility_level_field
visibility_level
end
def visibility_level_allowed_by_projects
allowed_by_projects = self.projects.where('visibility_level > ?', self.visibility_level).none?
unless allowed_by_projects
level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase
self.errors.add(:visibility_level, "#{level_name} is not allowed since there are projects with higher visibility.")
end
allowed_by_projects
end
def avatar_url(size = nil) def avatar_url(size = nil)
if avatar.present? if avatar.present?
[gitlab_config.url, avatar.url].join [gitlab_config.url, avatar.url].join
......
...@@ -36,9 +36,6 @@ class Issue < ActiveRecord::Base ...@@ -36,9 +36,6 @@ class Issue < ActiveRecord::Base
validates :project, presence: true validates :project, presence: true
scope :of_group,
->(group) { where(project_id: group.projects.select(:id).reorder(nil)) }
scope :cared, ->(user) { where(assignee_id: user) } scope :cared, ->(user) { where(assignee_id: user) }
scope :open_for, ->(user) { opened.assigned_to(user) } scope :open_for, ->(user) { opened.assigned_to(user) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids) } scope :in_projects, ->(project_ids) { where(project_id: project_ids) }
......
...@@ -131,7 +131,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -131,7 +131,6 @@ class MergeRequest < ActiveRecord::Base
validate :validate_branches validate :validate_branches
validate :validate_fork validate :validate_fork
scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id).reorder(nil)) }
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
......
...@@ -73,7 +73,7 @@ class Project < ActiveRecord::Base ...@@ -73,7 +73,7 @@ class Project < ActiveRecord::Base
update_column(:last_activity_at, self.created_at) update_column(:last_activity_at, self.created_at)
end end
# update visibility_levet of forks # update visibility_level of forks
after_update :update_forks_visibility_level after_update :update_forks_visibility_level
def update_forks_visibility_level def update_forks_visibility_level
return unless visibility_level < visibility_level_was return unless visibility_level < visibility_level_was
...@@ -197,6 +197,8 @@ class Project < ActiveRecord::Base ...@@ -197,6 +197,8 @@ class Project < ActiveRecord::Base
validate :avatar_type, validate :avatar_type,
if: ->(project) { project.avatar.present? && project.avatar_changed? } if: ->(project) { project.avatar.present? && project.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
validate :visibility_level_allowed_by_group
validate :visibility_level_allowed_as_fork
add_authentication_token_field :runners_token add_authentication_token_field :runners_token
before_save :ensure_runners_token before_save :ensure_runners_token
...@@ -215,8 +217,6 @@ class Project < ActiveRecord::Base ...@@ -215,8 +217,6 @@ class Project < ActiveRecord::Base
scope :in_group_namespace, -> { joins(:group) } scope :in_group_namespace, -> { joins(:group) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) } scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) } scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
scope :non_archived, -> { where(archived: false) } scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
...@@ -246,10 +246,6 @@ class Project < ActiveRecord::Base ...@@ -246,10 +246,6 @@ class Project < ActiveRecord::Base
end end
class << self class << self
def public_and_internal_levels
[Project::PUBLIC, Project::INTERNAL]
end
def abandoned def abandoned
where('projects.last_activity_at < ?', 6.months.ago) where('projects.last_activity_at < ?', 6.months.ago)
end end
...@@ -464,10 +460,25 @@ class Project < ActiveRecord::Base ...@@ -464,10 +460,25 @@ class Project < ActiveRecord::Base
def check_limit def check_limit
unless creator.can_create_project? or namespace.kind == 'group' unless creator.can_create_project? or namespace.kind == 'group'
errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it") self.errors.add(:limit_reached, "Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
end end
rescue rescue
errors[:base] << ("Can't check your ability to create project") self.errors.add(:base, "Can't check your ability to create project")
end
def visibility_level_allowed_by_group
return if visibility_level_allowed_by_group?
level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
group_level_name = Gitlab::VisibilityLevel.level_name(self.group.visibility_level).downcase
self.errors.add(:visibility_level, "#{level_name} is not allowed in a #{group_level_name} group.")
end
def visibility_level_allowed_as_fork
return if visibility_level_allowed_as_fork?
level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.")
end end
def to_param def to_param
...@@ -983,9 +994,25 @@ class Project < ActiveRecord::Base ...@@ -983,9 +994,25 @@ class Project < ActiveRecord::Base
issues.opened.count issues.opened.count
end end
def visibility_level_allowed?(level) def visibility_level_allowed_as_fork?(level = self.visibility_level)
return true unless forked? return true unless forked?
Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
# self.forked_from_project will be nil before the project is saved, so
# we need to go through the relation
original_project = forked_project_link.forked_from_project
return true unless original_project
level <= original_project.visibility_level
end
def visibility_level_allowed_by_group?(level = self.visibility_level)
return true unless group
level <= group.visibility_level
end
def visibility_level_allowed?(level = self.visibility_level)
visibility_level_allowed_as_fork?(level) && visibility_level_allowed_by_group?(level)
end end
def runners_token def runners_token
......
...@@ -43,12 +43,9 @@ class BaseService ...@@ -43,12 +43,9 @@ class BaseService
def deny_visibility_level(model, denied_visibility_level = nil) def deny_visibility_level(model, denied_visibility_level = nil)
denied_visibility_level ||= model.visibility_level denied_visibility_level ||= model.visibility_level
level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level) level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level).downcase
model.errors.add( model.errors.add(:visibility_level, "#{level_name} has been restricted by your GitLab administrator")
:visibility_level,
"#{level_name} visibility has been restricted by your GitLab administrator"
)
end end
private private
......
...@@ -6,8 +6,7 @@ class CreateSnippetService < BaseService ...@@ -6,8 +6,7 @@ class CreateSnippetService < BaseService
snippet = project.snippets.build(params) snippet = project.snippets.build(params)
end end
unless Gitlab::VisibilityLevel.allowed_for?(current_user, unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
params[:visibility_level])
deny_visibility_level(snippet) deny_visibility_level(snippet)
return snippet return snippet
end end
......
module Groups
class BaseService < ::BaseService
attr_accessor :group, :current_user, :params
def initialize(group, user, params = {})
@group, @current_user, @params = group, user, params.dup
end
end
end
module Groups
class CreateService < Groups::BaseService
def initialize(user, params = {})
@current_user, @params = user, params.dup
end
def execute
@group = Group.new(params)
unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
deny_visibility_level(@group)
return @group
end
@group.name ||= @group.path.dup
@group.save
@group.add_owner(current_user)
@group
end
end
end
module Groups
class UpdateService < Groups::BaseService
def execute
# check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != group.visibility_level
unless can?(current_user, :change_visibility_level, group) &&
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(group, new_visibility)
return group
end
end
group.assign_attributes(params)
group.save
end
end
end
...@@ -9,10 +9,8 @@ module Projects ...@@ -9,10 +9,8 @@ module Projects
@project = Project.new(params) @project = Project.new(params)
# Make sure that the user is allowed to use the specified visibility # Make sure that the user is allowed to use the specified visibility level
# level unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
unless Gitlab::VisibilityLevel.allowed_for?(current_user,
params[:visibility_level])
deny_visibility_level(@project) deny_visibility_level(@project)
return @project return @project
end end
...@@ -55,9 +53,7 @@ module Projects ...@@ -55,9 +53,7 @@ module Projects
@project.save @project.save
if @project.persisted? && !@project.import? if @project.persisted? && !@project.import?
unless @project.create_repository raise 'Failed to create repository' unless @project.create_repository
raise 'Failed to create repository'
end
end end
end end
......
...@@ -3,16 +3,13 @@ module Projects ...@@ -3,16 +3,13 @@ module Projects
def execute def execute
# check that user is allowed to set specified visibility_level # check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level] new_visibility = params[:visibility_level]
if new_visibility if new_visibility && new_visibility.to_i != project.visibility_level
if new_visibility.to_i != project.visibility_level unless can?(current_user, :change_visibility_level, project) &&
unless can?(current_user, :change_visibility_level, project) && Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(project, new_visibility) deny_visibility_level(project, new_visibility)
return project return project
end
end end
return false unless visibility_level_allowed?(new_visibility)
end end
new_branch = params[:default_branch] new_branch = params[:default_branch]
...@@ -27,19 +24,5 @@ module Projects ...@@ -27,19 +24,5 @@ module Projects
end end
end end
end end
private
def visibility_level_allowed?(level)
return true if project.visibility_level_allowed?(level)
level_name = Gitlab::VisibilityLevel.level_name(level)
project.errors.add(
:visibility_level,
"#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive"
)
false
end
end end
end end
...@@ -9,7 +9,6 @@ class UpdateSnippetService < BaseService ...@@ -9,7 +9,6 @@ class UpdateSnippetService < BaseService
def execute def execute
# check that user is allowed to set specified visibility_level # check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level] new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != snippet.visibility_level if new_visibility && new_visibility.to_i != snippet.visibility_level
unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(snippet, new_visibility) deny_visibility_level(snippet, new_visibility)
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
= f.label :default_snippet_visibility, class: 'control-label col-sm-2' = f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
.form-group.group-visibility-level-holder
= f.label :default_group_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
.form-group .form-group
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2' = f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
- submit_btn_css ||= 'btn btn-link btn-remove btn-sm' - submit_btn_css ||= 'btn btn-link btn-remove btn-sm'
= form_tag admin_application_path(application) do = form_tag admin_application_path(application) do
%input{:name => "_method", :type => "hidden", :value => "delete"}/ %input{:name => "_method", :type => "hidden", :value => "delete"}/
= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
\ No newline at end of file
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
.col-sm-10 .col-sm-10
= render 'shared/choose_group_avatar_button', f: f = render 'shared/choose_group_avatar_button', f: f
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
- if @group.new_record? - if @group.new_record?
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
......
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
%h4 %h4
= link_to [:admin, group] do = link_to [:admin, group] do
%span{ class: visibility_level_color(group.visibility_level) }
= visibility_level_icon(group.visibility_level)
%i.fa.fa-folder %i.fa.fa-folder
= group.name = group.name
......
...@@ -27,6 +27,11 @@ ...@@ -27,6 +27,11 @@
%strong %strong
= @group.description = @group.description
%li
%span.light Visibility level:
%strong
= visibility_level_label(@group.visibility_level)
%li %li
%span.light Created on: %span.light Created on:
%strong %strong
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
%li %li
%span.light fs: %span.light fs:
%strong %strong
= @repository.path_to_repo = @project.repository.path_to_repo
%li %li
%span.light Size %span.light Size
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
%label{for: "remember_me"} %label{for: "remember_me"}
= check_box_tag :remember_me, '1', false, id: 'remember_me' = check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me %span Remember me
= button_tag "Sign in", class: "btn-save btn" = button_tag "Sign in", class: "btn-save btn"
\ No newline at end of file
...@@ -4,4 +4,4 @@ ...@@ -4,4 +4,4 @@
%hr %hr
= render 'form', application: @application = render 'form', application: @application
\ No newline at end of file
%h3.page-title An error has occurred %h3.page-title An error has occurred
%main{:role => "main"} %main{:role => "main"}
%pre= @pre_auth.error_response.body[:error_description] %pre= @pre_auth.error_response.body[:error_description]
\ No newline at end of file
%h3.page-title Authorization code: %h3.page-title Authorization code:
%main{:role => "main"} %main{:role => "main"}
%code#authorization_code= params[:code] %code#authorization_code= params[:code]
\ No newline at end of file
...@@ -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.to_reference, [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]
= event_preposition(event) = event_preposition(event)
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
%hr %hr
= link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
.form-group .form-group
%hr %hr
= f.label :share_with_group_lock, class: 'control-label' do = f.label :share_with_group_lock, class: 'control-label' do
...@@ -32,6 +34,7 @@ ...@@ -32,6 +34,7 @@
= f.check_box :share_with_group_lock = f.check_box :share_with_group_lock
%span.descr Prevent sharing a project with another group within this group %span.descr Prevent sharing a project with another group within this group
.form-actions .form-actions
= f.submit 'Save group', class: "btn btn-save" = f.submit 'Save group', class: "btn btn-save"
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
.col-sm-10 .col-sm-10
= render 'shared/choose_group_avatar_button', f: f = render 'shared/choose_group_avatar_button', f: f
= render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
= render 'shared/group_tips' = render 'shared/group_tips'
......
- @no_container = true - @no_container = true
- unless can?(current_user, :read_group, @group)
- @disable_search_panel = true
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - if current_user
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
...@@ -18,7 +15,10 @@ ...@@ -18,7 +15,10 @@
= link_to group_icon(@group), target: '_blank' do = link_to group_icon(@group), target: '_blank' do
= image_tag group_icon(@group), class: "avatar group-avatar s90" = image_tag group_icon(@group), class: "avatar group-avatar s90"
.cover-title .cover-title
= @group.name %h1
= @group.name
%span.visibility-icon.has_tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
= visibility_level_icon(@group.visibility_level, fw: false)
.cover-desc.username .cover-desc.username
@#{@group.path} @#{@group.path}
...@@ -27,34 +27,29 @@ ...@@ -27,34 +27,29 @@
.cover-desc.description .cover-desc.description
= markdown(@group.description, pipeline: :description) = markdown(@group.description, pipeline: :description)
- if can?(current_user, :read_group, @group) %div{ class: container_class }
%div{ class: container_class } .top-area
.top-area %ul.nav-links
%ul.nav-links %li.active
%li.active = link_to "#projects", 'data-toggle' => 'tab' do
= link_to "#projects", 'data-toggle' => 'tab' do All Projects
All Projects
- if @shared_projects.present?
%li
= link_to "#shared", 'data-toggle' => 'tab' do
Shared Projects
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
= render 'shared/projects/dropdown'
- if can? current_user, :create_projects, @group
= link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
= icon('plus')
New Project
.tab-content
.tab-pane.active#projects
= render "projects", projects: @projects
- if @shared_projects.present? - if @shared_projects.present?
.tab-pane#shared %li
= render "shared_projects", projects: @shared_projects = link_to "#shared", 'data-toggle' => 'tab' do
Shared Projects
- else .nav-controls
%p.nav-links.no-top = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
No projects to show = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
= render 'shared/projects/dropdown'
- if can? current_user, :create_projects, @group
= link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
= icon('plus')
New Project
.tab-content
.tab-pane.active#projects
= render "projects", projects: @projects
- if @shared_projects.present?
.tab-pane#shared
= render "shared_projects", projects: @shared_projects
...@@ -7,9 +7,8 @@ ...@@ -7,9 +7,8 @@
.navbar-collapse.collapse .navbar-collapse.collapse
%ul.nav.navbar-nav.pull-right %ul.nav.navbar-nav.pull-right
- unless @disable_search_panel %li.hidden-sm.hidden-xs
%li.hidden-sm.hidden-xs = render 'layouts/search'
= render 'layouts/search'
%li.visible-sm.visible-xs %li.visible-sm.visible-xs
= link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('search') = icon('search')
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do
= link_to dashboard_projects_path, title: 'Projects' do = link_to dashboard_projects_path, title: 'Projects' do
= icon('home fw') = icon('bookmark fw')
%span %span
Projects Projects
= nav_link(controller: :todos) do = nav_link(controller: :todos) do
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do = link_to explore_root_path, title: 'Projects' do
= icon('home fw') = icon('bookmark fw')
%span %span
Projects Projects
= nav_link(controller: :groups) do = nav_link(controller: :groups) do
......
...@@ -12,40 +12,38 @@ ...@@ -12,40 +12,38 @@
= icon('group fw') = icon('group fw')
%span %span
Group Group
- if can?(current_user, :read_group, @group) = nav_link(path: 'groups#activity') do
= nav_link(path: 'groups#activity') do = link_to activity_group_path(@group), title: 'Activity' do
= link_to activity_group_path(@group), title: 'Activity' do = icon('dashboard fw')
= icon('dashboard fw') %span
%span Activity
Activity = nav_link(controller: [:group, :milestones]) do
- if current_user = link_to group_milestones_path(@group), title: 'Milestones' do
= nav_link(controller: [:group, :milestones]) do = icon('clock-o fw')
= link_to group_milestones_path(@group), title: 'Milestones' do %span
= icon('clock-o fw') Milestones
%span = nav_link(path: 'groups#issues') do
Milestones = link_to issues_group_path(@group), title: 'Issues' do
= nav_link(path: 'groups#issues') do = icon('exclamation-circle fw')
= link_to issues_group_path(@group), title: 'Issues' do %span
= icon('exclamation-circle fw') Issues
%span - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
Issues %span.count= number_with_delimiter(issues.count)
- if current_user = nav_link(path: 'groups#merge_requests') do
%span.count= number_with_delimiter(Issue.opened.of_group(@group).count) = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
= nav_link(path: 'groups#merge_requests') do = icon('tasks fw')
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do %span
= icon('tasks fw') Merge Requests
%span - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute
Merge Requests %span.count= number_with_delimiter(merge_requests.count)
- if current_user = nav_link(controller: [:group_members]) do
%span.count= number_with_delimiter(MergeRequest.opened.of_group(@group).count) = link_to group_group_members_path(@group), title: 'Members' do
= nav_link(controller: [:group_members]) do = icon('users fw')
= link_to group_group_members_path(@group), title: 'Members' do %span
= icon('users fw') Members
- if can?(current_user, :admin_group, @group)
= nav_link(html_options: { class: "separate-item" }) do
= link_to edit_group_path(@group), title: 'Settings' do
= icon ('cogs fw')
%span %span
Members Settings
- if can?(current_user, :admin_group, @group)
= nav_link(html_options: { class: "separate-item" }) do
= link_to edit_group_path(@group), title: 'Settings' do
= icon ('cogs fw')
%span
Settings
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= key.fingerprint = key.fingerprint
.pull-right .pull-right
%span.key-created-at %span.key-created-at
created #{time_ago_with_tooltip(key.created_at)} ago created #{time_ago_with_tooltip(key.created_at)}
= link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do = link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do
%span.sr-only Remove %span.sr-only Remove
= icon('trash') = icon('trash')
...@@ -2,21 +2,21 @@ ...@@ -2,21 +2,21 @@
.project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)}
.project-identicon-holder .project-identicon-holder
= project_icon(@project, alt: '', class: 'project-avatar avatar s90') = project_icon(@project, alt: '', class: 'project-avatar avatar s90')
.project-home-desc .cover-title.project-home-desc
%h1 %h1
= @project.name = @project.name
%span.visibility-icon.has_tooltip{data: { container: 'body' }, %span.visibility-icon.has_tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)}
title: "#{visibility_level_label(@project.visibility_level)} - #{project_visibility_level_description(@project.visibility_level)}"}
= visibility_level_icon(@project.visibility_level, fw: false) = visibility_level_icon(@project.visibility_level, fw: false)
- if @project.description.present? - if @project.description.present?
.cover-desc.project-home-desc
= markdown(@project.description, pipeline: :description) = markdown(@project.description, pipeline: :description)
- if forked_from_project = @project.forked_from_project - if forked_from_project = @project.forked_from_project
%p .cover-desc
Forked from Forked from
= link_to project_path(forked_from_project) do = link_to project_path(forked_from_project) do
= forked_from_project.namespace.try(:name) = forked_from_project.namespace.try(:name)
.cover-controls .cover-controls
- if current_user - if current_user
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
- if branch.name == @repository.root_ref - if branch.name == @repository.root_ref
%span.label.label-primary default %span.label.label-primary default
- elsif @repository.merged_to_root_ref? branch.name - elsif @repository.merged_to_root_ref? branch.name
%span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}") %span.label.label-info.has-tooltip(title="Merged into #{@repository.root_ref}")
merged merged
- if @project.protected_branch? branch.name - if @project.protected_branch? branch.name
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
Compare Compare
- if can_remove_branch?(@project, branch.name) - if can_remove_branch?(@project, branch.name)
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
= icon("trash-o") = icon("trash-o")
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
......
- unless @project.empty_repo? - unless @project.empty_repo?
- if can? current_user, :download_code, @project - if can? current_user, :download_code, @project
= link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has-tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do
= icon('download') = icon('download')
- unless @project.empty_repo? - unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) - if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do
= icon('code-fork fw') = icon('code-fork fw')
Fork Fork
%div.count-with-arrow %div.count-with-arrow
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
%span.count %span.count
= @project.forks_count = @project.forks_count
- else - else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
= icon('code-fork fw') = icon('code-fork fw')
Fork Fork
%div.count-with-arrow %div.count-with-arrow
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
= notification_list_item(level, @membership) = notification_list_item(level, @membership)
- when GroupMember - when GroupMember
.btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} .btn.disabled.notifications-btn.has-tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
= icon('bell') = icon('bell')
= notification_label(@membership) = notification_label(@membership)
= icon('angle-down') = icon('angle-down')
- if current_user - if current_user
= link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: "Star project" do
- if current_user.starred?(@project) - if current_user.starred?(@project)
= icon('star fw') = icon('star fw')
%span.starred Unstar %span.starred Unstar
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= @project.star_count = @project.star_count
- else - else
= link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do
= icon('star fw') = icon('star fw')
Star Star
%div.count-with-arrow %div.count-with-arrow
......
= form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do = form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do
.clearfix .clearfix
- if params[:to] && params[:from] - if params[:to] && params[:from]
= link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'} = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'}
.form-group .form-group
.input-group.inline-input-group .input-group.inline-input-group
%span.input-group-addon from %span.input-group-addon from
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.file-actions.hidden-xs .file-actions.hidden-xs
- if blob_text_viewable?(blob) - if blob_text_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn active has_tooltip', title: "Toggle comments for this file" do = link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file" do
= icon('comments') = icon('comments')
\ \
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
.col-md-2.col-sm-3 .col-md-2.col-sm-3
- if fork = namespace.find_fork_of(@project) - if fork = namespace.find_fork_of(@project)
.fork-thumbnail .fork-thumbnail
= link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do = link_to project_path(fork), title: "Visit project fork", class: 'has-tooltip' do
= image_tag namespace_icon(namespace, 100) = image_tag namespace_icon(namespace, 100)
.caption .caption
%strong %strong
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
- else - else
.fork-thumbnail .fork-thumbnail
= link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has-tooltip' do
= image_tag namespace_icon(namespace, 100) = image_tag namespace_icon(namespace, 100)
.caption .caption
%strong %strong
......
...@@ -45,7 +45,6 @@ ...@@ -45,7 +45,6 @@
- if can?(current_user, :update_issue, @issue) - if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
= link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do
= icon('pencil-square-o') = icon('pencil-square-o')
Edit Edit
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
- if merge_request.open? && merge_request.broken? - if merge_request.open? && merge_request.broken?
%li %li
= link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do = link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
= icon('exclamation-triangle') = icon('exclamation-triangle')
- if merge_request.assignee - if merge_request.assignee
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
- if @merge_request.open? - if @merge_request.open?
= link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close merge request' = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close merge request'
= link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do
%i.fa.fa-pencil-square-o = icon('pencil-square-o')
Edit Edit
- if @merge_request.closed? - if @merge_request.closed?
= link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request' = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request'
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
= render 'projects/tags/download', ref: tag.name, project: @project = render 'projects/tags/download', ref: tag.name, project: @project
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has_tooltip', title: "Edit release notes" do = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has-tooltip', title: "Edit release notes" do
= icon("pencil") = icon("pencil")
- if can?(current_user, :admin_project, @project) - if can?(current_user, :admin_project, @project)
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
= icon("trash-o") = icon("trash-o")
- if commit - if commit
......
...@@ -5,17 +5,17 @@ ...@@ -5,17 +5,17 @@
.gray-content-block .gray-content-block
.pull-right .pull-right
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has_tooltip', title: 'Edit release notes' do = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has-tooltip', title: 'Edit release notes' do
= icon("pencil") = icon("pencil")
= link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse files' do = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse files' do
= icon('files-o') = icon('files-o')
= link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse commits' do = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse commits' do
= icon('history') = icon('history')
- if can? current_user, :download_code, @project - if can? current_user, :download_code, @project
= render 'projects/tags/download', ref: @tag.name, project: @project = render 'projects/tags/download', ref: @tag.name, project: @project
- if can?(current_user, :admin_project, @project) - if can?(current_user, :admin_project, @project)
.pull-right .pull-right
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
%i.fa.fa-trash-o %i.fa.fa-trash-o
.title .title
%span.item-title= @tag.name %span.item-title= @tag.name
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
- if current_user - if current_user
%li %li
- if !on_top_of_branch? - if !on_top_of_branch?
%span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }} %span.btn.btn-sm.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
= icon('plus') = icon('plus')
- else - else
%span.dropdown %span.dropdown
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
- if milestone.description.present? - if milestone.description.present?
.description.term .description.term
= preserve do = preserve do
= search_md_sanitize(markdown(milestone.description)) = search_md_sanitize(markdown(milestone.description))
\ No newline at end of file
%ul %ul
%li A group is a collection of several projects %li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access %li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace %li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group %li Existing projects may be moved into a group
...@@ -14,13 +14,16 @@ ...@@ -14,13 +14,16 @@
.stats .stats
%span %span
= icon('home') = icon('bookmark')
= number_with_delimiter(group.projects.count) = number_with_delimiter(group.projects.count)
%span %span
= icon('users') = icon('users')
= number_with_delimiter(group.users.count) = number_with_delimiter(group.users.count)
%span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
= visibility_level_icon(group.visibility_level, fw: false)
= image_tag group_icon(group), class: "avatar s40 hidden-xs" = image_tag group_icon(group), class: "avatar s40 hidden-xs"
.title .title
= link_to group, class: 'group-name' do = link_to group, class: 'group-name' do
......
...@@ -127,7 +127,11 @@ ...@@ -127,7 +127,11 @@
for this project. for this project.
- if issuable.new_record? - if issuable.new_record?
- cancel_project = issuable.source_project = link_to 'Cancel', namespace_project_issues_path(@project.namespace, @project), class: 'btn btn-cancel'
- else - else
- cancel_project = issuable.project .pull-right
= link_to 'Cancel', [cancel_project.namespace.becomes(Namespace), cancel_project, issuable], class: 'btn btn-cancel' - if current_user.can?(:"destroy_#{issuable.to_ability_name}", @project)
= link_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), method: :delete, class: 'btn btn-grouped' do
= icon('trash-o')
Delete
= link_to 'Cancel', namespace_project_issue_path(@project.namespace, @project, issuable), class: 'btn btn-grouped btn-cancel'
...@@ -23,5 +23,5 @@ ...@@ -23,5 +23,5 @@
- if assignee - if assignee
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
class: 'has_tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do class: 'has-tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do
- image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
...@@ -27,8 +27,7 @@ ...@@ -27,8 +27,7 @@
%span %span
= icon('star') = icon('star')
= project.star_count = project.star_count
%span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project)}
title: "#{visibility_level_label(project.visibility_level)} - #{project_visibility_level_description(project.visibility_level)}"}
= visibility_level_icon(project.visibility_level, fw: false) = visibility_level_icon(project.visibility_level, fw: false)
.title .title
......
.detail-page-header .detail-page-header
.snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }} .snippet-box.has-tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
= visibility_level_icon(@snippet.visibility_level, fw: false) = visibility_level_icon(@snippet.visibility_level, fw: false)
= visibility_level_label(@snippet.visibility_level) = visibility_level_label(@snippet.visibility_level)
%span.identifier %span.identifier
......
.awards.votes-block .awards.votes-block
- awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes| - awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes|
%button.btn.award-control.js-emoji-btn.has_tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}} %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}}
= emoji_icon(emoji) = emoji_icon(emoji)
%span.award-control-text.js-counter %span.award-control-text.js-counter
= notes.count = notes.count
......
...@@ -613,7 +613,7 @@ Rails.application.routes.draw do ...@@ -613,7 +613,7 @@ Rails.application.routes.draw do
end end
end end
resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do resources :merge_requests, constraints: { id: /\d+/ } do
member do member do
get :commits get :commits
get :diffs get :diffs
...@@ -684,7 +684,7 @@ Rails.application.routes.draw do ...@@ -684,7 +684,7 @@ Rails.application.routes.draw do
end end
end end
resources :issues, constraints: { id: /\d+/ }, except: [:destroy] do resources :issues, constraints: { id: /\d+/ } do
member do member do
post :toggle_subscription post :toggle_subscription
end end
......
class AddDeleteAtToIssues < ActiveRecord::Migration
def change
add_column :issues, :deleted_at, :datetime
add_index :issues, :deleted_at
end
end
class AddDeleteAtToMergeRequests < ActiveRecord::Migration
def change
add_column :merge_requests, :deleted_at, :datetime
add_index :merge_requests, :deleted_at
end
end
class AddVisibilityLevelToGroups < ActiveRecord::Migration
def up
add_column :namespaces, :visibility_level, :integer, null: false, default: Gitlab::VisibilityLevel::PUBLIC
add_index :namespaces, :visibility_level
# Unfortunately, this is needed on top of the `default`, since we don't want the configuration specific
# `allowed_visibility_level` to end up in schema.rb
if allowed_visibility_level < Gitlab::VisibilityLevel::PUBLIC
execute("UPDATE namespaces SET visibility_level = #{allowed_visibility_level}")
end
end
def down
remove_column :namespaces, :visibility_level
end
private
def allowed_visibility_level
application_settings = select_one("SELECT restricted_visibility_levels FROM application_settings ORDER BY id DESC LIMIT 1")
if application_settings
restricted_visibility_levels = YAML.safe_load(application_settings["restricted_visibility_levels"]) rescue nil
end
restricted_visibility_levels ||= []
allowed_levels = Gitlab::VisibilityLevel.values - restricted_visibility_levels
allowed_levels.max
end
end
# Create visibility level field on DB
# Sets default_visibility_level to value on settings if not restricted
# If value is restricted takes higher visibility level allowed
class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration
def up
add_column :application_settings, :default_group_visibility, :integer
# Unfortunately, this can't be a `default`, since we don't want the configuration specific
# `allowed_visibility_level` to end up in schema.rb
execute("UPDATE application_settings SET default_group_visibility = #{allowed_visibility_level}")
end
def down
remove_column :application_settings, :default_group_visibility
end
private
def allowed_visibility_level
application_settings = select_one("SELECT restricted_visibility_levels FROM application_settings ORDER BY id DESC LIMIT 1")
if application_settings
restricted_visibility_levels = YAML.safe_load(application_settings["restricted_visibility_levels"]) rescue nil
end
restricted_visibility_levels ||= []
allowed_levels = Gitlab::VisibilityLevel.values - restricted_visibility_levels
allowed_levels.max
end
end
class IndexNamespacesOnVisibilityLevel < ActiveRecord::Migration
def change
unless index_exists?(:namespaces, :visibility_level)
add_index :namespaces, :visibility_level
end
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160317092222) do ActiveRecord::Schema.define(version: 20160320204112) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -77,6 +77,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -77,6 +77,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do
t.boolean "akismet_enabled", default: false t.boolean "akismet_enabled", default: false
t.string "akismet_api_key" t.string "akismet_api_key"
t.boolean "email_author_in_body", default: false t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility"
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
...@@ -418,6 +419,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -418,6 +419,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do
t.integer "updated_by_id" t.integer "updated_by_id"
t.integer "moved_to_id" t.integer "moved_to_id"
t.boolean "confidential", default: false t.boolean "confidential", default: false
t.datetime "deleted_at"
end end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
...@@ -425,6 +427,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -425,6 +427,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do
add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree
add_index "issues", ["created_at", "id"], name: "index_issues_on_created_at_and_id", using: :btree add_index "issues", ["created_at", "id"], name: "index_issues_on_created_at_and_id", using: :btree
add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree
add_index "issues", ["deleted_at"], name: "index_issues_on_deleted_at", using: :btree
add_index "issues", ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "issues", ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
...@@ -547,12 +550,14 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -547,12 +550,14 @@ ActiveRecord::Schema.define(version: 20160317092222) do
t.boolean "merge_when_build_succeeds", default: false, null: false t.boolean "merge_when_build_succeeds", default: false, null: false
t.integer "merge_user_id" t.integer "merge_user_id"
t.string "merge_commit_sha" t.string "merge_commit_sha"
t.datetime "deleted_at"
end end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
add_index "merge_requests", ["created_at", "id"], name: "index_merge_requests_on_created_at_and_id", using: :btree add_index "merge_requests", ["created_at", "id"], name: "index_merge_requests_on_created_at_and_id", using: :btree
add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
add_index "merge_requests", ["deleted_at"], name: "index_merge_requests_on_deleted_at", using: :btree
add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
...@@ -591,6 +596,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -591,6 +596,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do
t.string "description", default: "", null: false t.string "description", default: "", null: false
t.string "avatar" t.string "avatar"
t.boolean "share_with_group_lock", default: false t.boolean "share_with_group_lock", default: false
t.integer "visibility_level", default: 20, null: false
end end
add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
...@@ -600,6 +606,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -600,6 +606,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do
add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree
add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
add_index "namespaces", ["visibility_level"], name: "index_namespaces_on_visibility_level", using: :btree
create_table "notes", force: :cascade do |t| create_table "notes", force: :cascade do |t|
t.text "note" t.text "note"
......
...@@ -111,6 +111,7 @@ Parameters: ...@@ -111,6 +111,7 @@ Parameters:
- `name` (required) - The name of the group - `name` (required) - The name of the group
- `path` (required) - The path of the group - `path` (required) - The path of the group
- `description` (optional) - The group's description - `description` (optional) - The group's description
- `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public.
## Transfer project to group ## Transfer project to group
......
...@@ -326,17 +326,25 @@ Example response: ...@@ -326,17 +326,25 @@ Example response:
} }
``` ```
## Delete existing issue (**Deprecated**) ## Delete an issue
This call is deprecated and returns a `405 Method Not Allowed` error if called. Only for admins and project owners. Soft deletes the issue in question.
An issue gets now closed and is done by calling If the operation is successful, a status code `200` is returned. In case you cannot
`PUT /projects/:id/issues/:issue_id` with the parameter `state_event` set to destroy this issue, or it is not present, code `404` is given.
`close`. See [edit issue](#edit-issue) for more details.
``` ```
DELETE /projects/:id/issues/:issue_id DELETE /projects/:id/issues/:issue_id
``` ```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85
```
## Comments on issues ## Comments on issues
Comments are done via the [notes](notes.md) resource. Comments are done via the [notes](notes.md) resource.
...@@ -380,6 +380,25 @@ Parameters: ...@@ -380,6 +380,25 @@ Parameters:
If the operation is successful, 200 and the updated merge request is returned. If the operation is successful, 200 and the updated merge request is returned.
If an error occurs, an error number and a message explaining the reason is returned. If an error occurs, an error number and a message explaining the reason is returned.
## Delete a merge request
Only for admins and project owners. Soft deletes the merge request in question.
If the operation is successful, a status code `200` is returned. In case you cannot
destroy this merge request, or it is not present, code `404` is given.
```
DELETE /projects/:id/merge_requests/:merge_request_id
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
| `merge_request_id` | integer | yes | The ID of a project's merge request |
```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/merge_request/85
```
## Accept MR ## Accept MR
Merge changes submitted with MR using this API. Merge changes submitted with MR using this API.
......
...@@ -85,7 +85,7 @@ module API ...@@ -85,7 +85,7 @@ module API
end end
class Group < Grape::Entity class Group < Grape::Entity
expose :id, :name, :path, :description expose :id, :name, :path, :description, :visibility_level
expose :avatar_url expose :avatar_url
expose :web_url do |group, options| expose :web_url do |group, options|
...@@ -340,6 +340,7 @@ module API ...@@ -340,6 +340,7 @@ module API
expose :session_expire_delay expose :session_expire_delay
expose :default_project_visibility expose :default_project_visibility
expose :default_snippet_visibility expose :default_snippet_visibility
expose :default_group_visibility
expose :restricted_signup_domains expose :restricted_signup_domains
expose :user_oauth_applications expose :user_oauth_applications
expose :after_sign_out_path expose :after_sign_out_path
......
...@@ -31,7 +31,7 @@ module API ...@@ -31,7 +31,7 @@ module API
authorize! :create_group, current_user authorize! :create_group, current_user
required_attributes! [:name, :path] required_attributes! [:name, :path]
attrs = attributes_for_keys [:name, :path, :description] attrs = attributes_for_keys [:name, :path, :description, :visibility_level]
@group = Group.new(attrs) @group = Group.new(attrs)
if @group.save if @group.save
......
...@@ -118,9 +118,7 @@ module API ...@@ -118,9 +118,7 @@ module API
end end
def authorize!(action, subject) def authorize!(action, subject)
unless abilities.allowed?(current_user, action, subject) forbidden! unless abilities.allowed?(current_user, action, subject)
forbidden!
end
end end
def authorize_push_project def authorize_push_project
......
...@@ -191,7 +191,7 @@ module API ...@@ -191,7 +191,7 @@ module API
end end
end end
# Delete a project issue (deprecated) # Delete a project issue
# #
# Parameters: # Parameters:
# id (required) - The ID of a project # id (required) - The ID of a project
...@@ -199,7 +199,10 @@ module API ...@@ -199,7 +199,10 @@ module API
# Example Request: # Example Request:
# DELETE /projects/:id/issues/:issue_id # DELETE /projects/:id/issues/:issue_id
delete ":id/issues/:issue_id" do delete ":id/issues/:issue_id" do
not_allowed! issue = user_project.issues.find_by(id: params[:issue_id])
authorize!(:destroy_issue, issue)
issue.destroy
end end
end end
end end
......
...@@ -100,6 +100,18 @@ module API ...@@ -100,6 +100,18 @@ module API
end end
end end
# Delete a MR
#
# Parameters:
# id (required) - The ID of the project
# merge_request_id (required) - The MR id
delete ":id/merge_requests/:merge_request_id" do
merge_request = user_project.merge_requests.find_by(id: params[:merge_request_id])
authorize!(:destroy_merge_request, merge_request)
merge_request.destroy
end
# Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0 # Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0
# Use "merge_requests/:merge_request_id/..." instead. # Use "merge_requests/:merge_request_id/..." instead.
# #
......
...@@ -6,6 +6,14 @@ ...@@ -6,6 +6,14 @@
module Gitlab module Gitlab
module VisibilityLevel module VisibilityLevel
extend CurrentSettings extend CurrentSettings
extend ActiveSupport::Concern
included do
scope :public_only, -> { where(visibility_level: PUBLIC) }
scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
scope :public_to_user, -> (user) { user && !user.external ? public_and_internal_only : public_only }
end
PRIVATE = 0 unless const_defined?(:PRIVATE) PRIVATE = 0 unless const_defined?(:PRIVATE)
INTERNAL = 10 unless const_defined?(:INTERNAL) INTERNAL = 10 unless const_defined?(:INTERNAL)
...@@ -48,10 +56,6 @@ module Gitlab ...@@ -48,10 +56,6 @@ module Gitlab
options.has_value?(level) options.has_value?(level)
end end
def allowed_fork_levels(origin_level)
[PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level }
end
def level_name(level) def level_name(level)
level_name = 'Unknown' level_name = 'Unknown'
options.each do |name, lvl| options.each do |name, lvl|
......
...@@ -30,44 +30,4 @@ describe ApplicationController do ...@@ -30,44 +30,4 @@ describe ApplicationController do
controller.send(:check_password_expiration) controller.send(:check_password_expiration)
end end
end end
describe 'check labels authorization' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:controller) { ApplicationController.new }
before do
project.team << [user, :guest]
allow(controller).to receive(:current_user).and_return(user)
allow(controller).to receive(:project).and_return(project)
end
it 'should succeed if issues and MRs are enabled' do
project.issues_enabled = true
project.merge_requests_enabled = true
controller.send(:authorize_read_label!)
expect(response.status).to eq(200)
end
it 'should succeed if issues are enabled, MRs are disabled' do
project.issues_enabled = true
project.merge_requests_enabled = false
controller.send(:authorize_read_label!)
expect(response.status).to eq(200)
end
it 'should succeed if issues are disabled, MRs are enabled' do
project.issues_enabled = false
project.merge_requests_enabled = true
controller.send(:authorize_read_label!)
expect(response.status).to eq(200)
end
it 'should fail if issues and MRs are disabled' do
project.issues_enabled = false
project.merge_requests_enabled = false
expect(controller).to receive(:access_denied!)
controller.send(:authorize_read_label!)
end
end
end end
...@@ -2,9 +2,10 @@ require 'spec_helper' ...@@ -2,9 +2,10 @@ require 'spec_helper'
describe Groups::AvatarsController do describe Groups::AvatarsController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, owner: user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
before do before do
group.add_owner(user)
sign_in(user) sign_in(user)
end end
......
...@@ -15,14 +15,9 @@ describe NamespacesController do ...@@ -15,14 +15,9 @@ describe NamespacesController do
end end
context "when the namespace belongs to a group" do context "when the namespace belongs to a group" do
let!(:group) { create(:group) } let!(:group) { create(:group) }
let!(:project) { create(:project, namespace: group) }
context "when the group has public projects" do
before do
project.update_attribute(:visibility_level, Project::PUBLIC)
end
context "when the group is public" do
context "when not signed in" do context "when not signed in" do
it "redirects to the group's page" do it "redirects to the group's page" do
get :show, id: group.path get :show, id: group.path
...@@ -44,27 +39,31 @@ describe NamespacesController do ...@@ -44,27 +39,31 @@ describe NamespacesController do
end end
end end
context "when the project doesn't have public projects" do context "when the group is private" do
before do
group.update_attribute(:visibility_level, Group::PRIVATE)
end
context "when not signed in" do context "when not signed in" do
it "does not redirect to the sign in page" do it "redirects to the sign in page" do
get :show, id: group.path get :show, id: group.path
expect(response).not_to redirect_to(new_user_session_path) expect(response).to redirect_to(new_user_session_path)
end end
end end
context "when signed in" do context "when signed in" do
before do before do
sign_in(user) sign_in(user)
end end
context "when the user has access to the project" do context "when the user has access to the group" do
before do before do
project.team << [user, :master] group.add_developer(user)
end end
context "when the user is blocked" do context "when the user is blocked" do
before do before do
user.block user.block
project.team << [user, :master]
end end
it "redirects to the sign in page" do it "redirects to the sign in page" do
...@@ -83,11 +82,11 @@ describe NamespacesController do ...@@ -83,11 +82,11 @@ describe NamespacesController do
end end
end end
context "when the user doesn't have access to the project" do context "when the user doesn't have access to the group" do
it "redirects to the group's page" do it "responds with status 404" do
get :show, id: group.path get :show, id: group.path
expect(response).to redirect_to(group_path(group)) expect(response.status).to eq(404)
end end
end end
end end
......
...@@ -6,7 +6,7 @@ describe Projects::AvatarsController do ...@@ -6,7 +6,7 @@ describe Projects::AvatarsController do
before do before do
sign_in(user) sign_in(user)
project.team << [user, :developer] project.team << [user, :master]
controller.instance_variable_set(:@project, project) controller.instance_variable_set(:@project, project)
end end
......
require('spec_helper') require('spec_helper')
describe Projects::IssuesController do describe Projects::IssuesController do
describe "GET #index" do let(:project) { create(:project_empty_repo) }
let(:project) { create(:project_empty_repo) } let(:user) { create(:user) }
let(:user) { create(:user) } let(:issue) { create(:issue, project: project) }
let(:issue) { create(:issue, project: project) }
describe "GET #index" do
before do before do
sign_in(user) sign_in(user)
project.team << [user, :developer] project.team << [user, :developer]
...@@ -186,4 +186,29 @@ describe Projects::IssuesController do ...@@ -186,4 +186,29 @@ describe Projects::IssuesController do
end end
end end
end end
describe "DELETE #destroy" do
context "when the user is a developer" do
before { sign_in(user) }
it "rejects a developer to destroy an issue" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
expect(response.status).to eq(404)
end
end
context "when the user is owner" do
let(:owner) { create(:user) }
let(:namespace) { create(:namespace, owner: owner) }
let(:project) { create(:project, namespace: namespace) }
before { sign_in(owner) }
it "deletes the issue" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
expect(response.status).to eq(302)
expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now
end
end
end
end end
...@@ -157,6 +157,29 @@ describe Projects::MergeRequestsController do ...@@ -157,6 +157,29 @@ describe Projects::MergeRequestsController do
end end
end end
describe "DELETE #destroy" do
it "denies access to users unless they're admin or project owner" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
expect(response.status).to eq(404)
end
context "when the user is owner" do
let(:owner) { create(:user) }
let(:namespace) { create(:namespace, owner: owner) }
let(:project) { create(:project, namespace: namespace) }
before { sign_in owner }
it "deletes the merge request" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
expect(response.status).to eq(302)
expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now
end
end
end
describe 'GET diffs' do describe 'GET diffs' do
def go(format: 'html') def go(format: 'html')
get :diffs, get :diffs,
......
...@@ -30,7 +30,7 @@ describe UploadsController do ...@@ -30,7 +30,7 @@ describe UploadsController do
end end
end end
end end
context "when not signed in" do context "when not signed in" do
it "responds with status 200" do it "responds with status 200" do
get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
...@@ -126,14 +126,9 @@ describe UploadsController do ...@@ -126,14 +126,9 @@ describe UploadsController do
end end
context "when viewing a group avatar" do context "when viewing a group avatar" do
let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
let!(:project) { create(:project, namespace: group) }
context "when the group has public projects" do
before do
project.update_attribute(:visibility_level, Project::PUBLIC)
end
context "when the group is public" do
context "when not signed in" do context "when not signed in" do
it "responds with status 200" do it "responds with status 200" do
get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
...@@ -155,7 +150,11 @@ describe UploadsController do ...@@ -155,7 +150,11 @@ describe UploadsController do
end end
end end
context "when the project doesn't have public projects" do context "when the group is private" do
before do
group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
end
context "when signed in" do context "when signed in" do
before do before do
sign_in(user) sign_in(user)
...@@ -163,13 +162,12 @@ describe UploadsController do ...@@ -163,13 +162,12 @@ describe UploadsController do
context "when the user has access to the project" do context "when the user has access to the project" do
before do before do
project.team << [user, :master] group.add_developer(user)
end end
context "when the user is blocked" do context "when the user is blocked" do
before do before do
user.block user.block
project.team << [user, :master]
end end
it "redirects to the sign in page" do it "redirects to the sign in page" do
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
FactoryGirl.define do FactoryGirl.define do
factory :broadcast_message do factory :broadcast_message do
message "MyText" message "MyText"
starts_at Date.today starts_at Date.yesterday
ends_at Date.tomorrow ends_at Date.tomorrow
trait :expired do trait :expired do
......
...@@ -3,5 +3,17 @@ FactoryGirl.define do ...@@ -3,5 +3,17 @@ FactoryGirl.define do
sequence(:name) { |n| "group#{n}" } sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
type 'Group' type 'Group'
trait :public do
visibility_level Gitlab::VisibilityLevel::PUBLIC
end
trait :internal do
visibility_level Gitlab::VisibilityLevel::INTERNAL
end
trait :private do
visibility_level Gitlab::VisibilityLevel::PRIVATE
end
end end
end end
require 'rails_helper'
describe 'Internal Group access', feature: true do
include AccessMatchers
let(:group) { create(:group, :internal) }
let(:project) { create(:project, :internal, group: group) }
let(:owner) { create(:user) }
let(:master) { create(:user) }
let(:developer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:project_guest) { create(:user) }
before do
group.add_owner(owner)
group.add_master(master)
group.add_developer(developer)
group.add_reporter(reporter)
group.add_guest(guest)
project.team << [project_guest, :guest]
end
describe "Group should be internal" do
describe '#internal?' do
subject { group.internal? }
it { is_expected.to be_truthy }
end
end
describe 'GET /groups/:path' do
subject { group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for project_guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
it { is_expected.to be_denied_for :external }
end
end
require 'rails_helper'
describe 'Private Group access', feature: true do
include AccessMatchers
let(:group) { create(:group, :private) }
let(:project) { create(:project, :private, group: group) }
let(:owner) { create(:user) }
let(:master) { create(:user) }
let(:developer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:project_guest) { create(:user) }
before do
group.add_owner(owner)
group.add_master(master)
group.add_developer(developer)
group.add_reporter(reporter)
group.add_guest(guest)
project.team << [project_guest, :guest]
end
describe "Group should be private" do
describe '#private?' do
subject { group.private? }
it { is_expected.to be_truthy }
end
end
describe 'GET /groups/:path' do
subject { group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for project_guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
it { is_expected.to be_denied_for :external }
end
end
require 'rails_helper'
describe 'Public Group access', feature: true do
include AccessMatchers
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, group: group) }
let(:owner) { create(:user) }
let(:master) { create(:user) }
let(:developer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:project_guest) { create(:user) }
before do
group.add_owner(owner)
group.add_master(master)
group.add_developer(developer)
group.add_reporter(reporter)
group.add_guest(guest)
project.team << [project_guest, :guest]
end
describe "Group should be public" do
describe '#public?' do
subject { group.public? }
it { is_expected.to be_truthy }
end
end
describe 'GET /groups/:path' do
subject { group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external }
it { is_expected.to be_allowed_for :visitor }
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external }
it { is_expected.to be_allowed_for :visitor }
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external }
it { is_expected.to be_allowed_for :visitor }
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for project_guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external }
it { is_expected.to be_allowed_for :visitor }
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for project_guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
it { is_expected.to be_denied_for :external }
end
end
require 'rails_helper'
describe 'Group access', feature: true do
include AccessMatchers
def group
@group ||= create(:group)
end
def create_project(access_level)
if access_level == :mixed
create(:empty_project, :public, group: group)
create(:empty_project, :internal, group: group)
else
create(:empty_project, access_level, group: group)
end
end
def group_member(access_level, grp = group())
level = Object.const_get("Gitlab::Access::#{access_level.upcase}")
create(:user).tap do |user|
grp.add_user(user, level)
end
end
describe 'GET /groups/new' do
subject { new_group_path }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path' do
subject { group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
...@@ -5,25 +5,22 @@ describe "Internal Project Access", feature: true do ...@@ -5,25 +5,22 @@ describe "Internal Project Access", feature: true do
let(:project) { create(:project, :internal) } let(:project) { create(:project, :internal) }
let(:master) { create(:user) } let(:owner) { project.owner }
let(:guest) { create(:user) } let(:master) { create(:user) }
let(:reporter) { create(:user) } let(:developer) { create(:user) }
let(:external_team_member) { create(:user, external: true) } let(:reporter) { create(:user) }
let(:guest) { create(:user) }
before do before do
# full access
project.team << [master, :master] project.team << [master, :master]
project.team << [external_team_member, :master] project.team << [developer, :developer]
# readonly
project.team << [reporter, :reporter] project.team << [reporter, :reporter]
project.team << [guest, :guest]
end end
describe "Project should be internal" do describe "Project should be internal" do
subject { project }
describe '#internal?' do describe '#internal?' do
subject { super().internal? } subject { project.internal? }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end end
end end
...@@ -31,78 +28,84 @@ describe "Internal Project Access", feature: true do ...@@ -31,78 +28,84 @@ describe "Internal Project Access", feature: true do
describe "GET /:project_path" do describe "GET /:project_path" do
subject { namespace_project_path(project.namespace, project) } subject { namespace_project_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/tree/master" do describe "GET /:project_path/tree/master" do
subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/commits/master" do describe "GET /:project_path/commits/master" do
subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/commit/:sha" do describe "GET /:project_path/commit/:sha" do
subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/compare" do describe "GET /:project_path/compare" do
subject { namespace_project_compare_index_path(project.namespace, project) } subject { namespace_project_compare_index_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/project_members" do describe "GET /:project_path/project_members" do
subject { namespace_project_project_members_path(project.namespace, project) } subject { namespace_project_project_members_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -110,52 +113,56 @@ describe "Internal Project Access", feature: true do ...@@ -110,52 +113,56 @@ describe "Internal Project Access", feature: true do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) } subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/edit" do describe "GET /:project_path/edit" do
subject { edit_namespace_project_path(project.namespace, project) } subject { edit_namespace_project_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/deploy_keys" do describe "GET /:project_path/deploy_keys" do
subject { namespace_project_deploy_keys_path(project.namespace, project) } subject { namespace_project_deploy_keys_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/issues" do describe "GET /:project_path/issues" do
subject { namespace_project_issues_path(project.namespace, project) } subject { namespace_project_issues_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -163,65 +170,70 @@ describe "Internal Project Access", feature: true do ...@@ -163,65 +170,70 @@ describe "Internal Project Access", feature: true do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
subject { edit_namespace_project_issue_path(project.namespace, project, issue) } subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/snippets" do describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) } subject { namespace_project_snippets_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/snippets/new" do describe "GET /:project_path/snippets/new" do
subject { new_namespace_project_snippet_path(project.namespace, project) } subject { new_namespace_project_snippet_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/merge_requests" do describe "GET /:project_path/merge_requests" do
subject { namespace_project_merge_requests_path(project.namespace, project) } subject { namespace_project_merge_requests_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/merge_requests/new" do describe "GET /:project_path/merge_requests/new" do
subject { new_namespace_project_merge_request_path(project.namespace, project) } subject { new_namespace_project_merge_request_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -233,13 +245,14 @@ describe "Internal Project Access", feature: true do ...@@ -233,13 +245,14 @@ describe "Internal Project Access", feature: true do
allow_any_instance_of(Project).to receive(:branches).and_return([]) allow_any_instance_of(Project).to receive(:branches).and_return([])
end end
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -251,26 +264,28 @@ describe "Internal Project Access", feature: true do ...@@ -251,26 +264,28 @@ describe "Internal Project Access", feature: true do
allow_any_instance_of(Project).to receive(:tags).and_return([]) allow_any_instance_of(Project).to receive(:tags).and_return([])
end end
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/hooks" do describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) } subject { namespace_project_hooks_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
end end
...@@ -3,27 +3,24 @@ require 'spec_helper' ...@@ -3,27 +3,24 @@ require 'spec_helper'
describe "Private Project Access", feature: true do describe "Private Project Access", feature: true do
include AccessMatchers include AccessMatchers
let(:project) { create(:project) } let(:project) { create(:project, :private) }
let(:master) { create(:user) } let(:owner) { project.owner }
let(:guest) { create(:user) } let(:master) { create(:user) }
let(:reporter) { create(:user) } let(:developer) { create(:user) }
let(:external_team_member) { create(:user, external: true) } let(:reporter) { create(:user) }
let(:guest) { create(:user) }
before do before do
# full access
project.team << [master, :master] project.team << [master, :master]
project.team << [external_team_member, :master] project.team << [developer, :developer]
# readonly
project.team << [reporter, :reporter] project.team << [reporter, :reporter]
project.team << [guest, :guest]
end end
describe "Project should be private" do describe "Project should be private" do
subject { project }
describe '#private?' do describe '#private?' do
subject { super().private? } subject { project.private? }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end end
end end
...@@ -31,77 +28,84 @@ describe "Private Project Access", feature: true do ...@@ -31,77 +28,84 @@ describe "Private Project Access", feature: true do
describe "GET /:project_path" do describe "GET /:project_path" do
subject { namespace_project_path(project.namespace, project) } subject { namespace_project_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/tree/master" do describe "GET /:project_path/tree/master" do
subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/commits/master" do describe "GET /:project_path/commits/master" do
subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/commit/:sha" do describe "GET /:project_path/commit/:sha" do
subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_allowed_for external_team_member } it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/compare" do describe "GET /:project_path/compare" do
subject { namespace_project_compare_index_path(project.namespace, project) } subject { namespace_project_compare_index_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/project_members" do describe "GET /:project_path/project_members" do
subject { namespace_project_project_members_path(project.namespace, project) } subject { namespace_project_project_members_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -109,52 +113,56 @@ describe "Private Project Access", feature: true do ...@@ -109,52 +113,56 @@ describe "Private Project Access", feature: true do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore'))} subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore'))}
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/edit" do describe "GET /:project_path/edit" do
subject { edit_namespace_project_path(project.namespace, project) } subject { edit_namespace_project_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/deploy_keys" do describe "GET /:project_path/deploy_keys" do
subject { namespace_project_deploy_keys_path(project.namespace, project) } subject { namespace_project_deploy_keys_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/issues" do describe "GET /:project_path/issues" do
subject { namespace_project_issues_path(project.namespace, project) } subject { namespace_project_issues_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -162,39 +170,42 @@ describe "Private Project Access", feature: true do ...@@ -162,39 +170,42 @@ describe "Private Project Access", feature: true do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
subject { edit_namespace_project_issue_path(project.namespace, project, issue) } subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/snippets" do describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) } subject { namespace_project_snippets_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/merge_requests" do describe "GET /:project_path/merge_requests" do
subject { namespace_project_merge_requests_path(project.namespace, project) } subject { namespace_project_merge_requests_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -206,13 +217,14 @@ describe "Private Project Access", feature: true do ...@@ -206,13 +217,14 @@ describe "Private Project Access", feature: true do
allow_any_instance_of(Project).to receive(:branches).and_return([]) allow_any_instance_of(Project).to receive(:branches).and_return([])
end end
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
...@@ -224,26 +236,28 @@ describe "Private Project Access", feature: true do ...@@ -224,26 +236,28 @@ describe "Private Project Access", feature: true do
allow_any_instance_of(Project).to receive(:tags).and_return([]) allow_any_instance_of(Project).to receive(:tags).and_return([])
end end
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/hooks" do describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) } subject { namespace_project_hooks_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_allowed_for external_team_member }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
end end
...@@ -3,29 +3,24 @@ require 'spec_helper' ...@@ -3,29 +3,24 @@ require 'spec_helper'
describe "Public Project Access", feature: true do describe "Public Project Access", feature: true do
include AccessMatchers include AccessMatchers
let(:project) { create(:project) } let(:project) { create(:project, :public) }
let(:master) { create(:user) } let(:owner) { project.owner }
let(:guest) { create(:user) } let(:master) { create(:user) }
let(:reporter) { create(:user) } let(:developer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
before do before do
# public project
project.visibility_level = Gitlab::VisibilityLevel::PUBLIC
project.save!
# full access
project.team << [master, :master] project.team << [master, :master]
project.team << [developer, :developer]
# readonly
project.team << [reporter, :reporter] project.team << [reporter, :reporter]
project.team << [guest, :guest]
end end
describe "Project should be public" do describe "Project should be public" do
subject { project }
describe '#public?' do describe '#public?' do
subject { super().public? } subject { project.public? }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end end
end end
...@@ -33,9 +28,11 @@ describe "Public Project Access", feature: true do ...@@ -33,9 +28,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path" do describe "GET /:project_path" do
subject { namespace_project_path(project.namespace, project) } subject { namespace_project_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -45,9 +42,11 @@ describe "Public Project Access", feature: true do ...@@ -45,9 +42,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/tree/master" do describe "GET /:project_path/tree/master" do
subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -57,9 +56,11 @@ describe "Public Project Access", feature: true do ...@@ -57,9 +56,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/commits/master" do describe "GET /:project_path/commits/master" do
subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -69,9 +70,11 @@ describe "Public Project Access", feature: true do ...@@ -69,9 +70,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/commit/:sha" do describe "GET /:project_path/commit/:sha" do
subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -81,9 +84,11 @@ describe "Public Project Access", feature: true do ...@@ -81,9 +84,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/compare" do describe "GET /:project_path/compare" do
subject { namespace_project_compare_index_path(project.namespace, project) } subject { namespace_project_compare_index_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -93,9 +98,11 @@ describe "Public Project Access", feature: true do ...@@ -93,9 +98,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/project_members" do describe "GET /:project_path/project_members" do
subject { namespace_project_project_members_path(project.namespace, project) } subject { namespace_project_project_members_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -108,9 +115,11 @@ describe "Public Project Access", feature: true do ...@@ -108,9 +115,11 @@ describe "Public Project Access", feature: true do
context "when allowed for public" do context "when allowed for public" do
before { project.update(public_builds: true) } before { project.update(public_builds: true) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -120,9 +129,11 @@ describe "Public Project Access", feature: true do ...@@ -120,9 +129,11 @@ describe "Public Project Access", feature: true do
context "when disallowed for public" do context "when disallowed for public" do
before { project.update(public_builds: false) } before { project.update(public_builds: false) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -138,9 +149,11 @@ describe "Public Project Access", feature: true do ...@@ -138,9 +149,11 @@ describe "Public Project Access", feature: true do
context "when allowed for public" do context "when allowed for public" do
before { project.update(public_builds: true) } before { project.update(public_builds: true) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -150,9 +163,11 @@ describe "Public Project Access", feature: true do ...@@ -150,9 +163,11 @@ describe "Public Project Access", feature: true do
context "when disallowed for public" do context "when disallowed for public" do
before { project.update(public_builds: false) } before { project.update(public_builds: false) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -165,9 +180,11 @@ describe "Public Project Access", feature: true do ...@@ -165,9 +180,11 @@ describe "Public Project Access", feature: true do
subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) } subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor } it { is_expected.to be_allowed_for :visitor }
...@@ -176,9 +193,11 @@ describe "Public Project Access", feature: true do ...@@ -176,9 +193,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/edit" do describe "GET /:project_path/edit" do
subject { edit_namespace_project_path(project.namespace, project) } subject { edit_namespace_project_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -188,9 +207,11 @@ describe "Public Project Access", feature: true do ...@@ -188,9 +207,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/deploy_keys" do describe "GET /:project_path/deploy_keys" do
subject { namespace_project_deploy_keys_path(project.namespace, project) } subject { namespace_project_deploy_keys_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -200,9 +221,11 @@ describe "Public Project Access", feature: true do ...@@ -200,9 +221,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/issues" do describe "GET /:project_path/issues" do
subject { namespace_project_issues_path(project.namespace, project) } subject { namespace_project_issues_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -213,9 +236,11 @@ describe "Public Project Access", feature: true do ...@@ -213,9 +236,11 @@ describe "Public Project Access", feature: true do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
subject { edit_namespace_project_issue_path(project.namespace, project, issue) } subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -225,9 +250,11 @@ describe "Public Project Access", feature: true do ...@@ -225,9 +250,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/snippets" do describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) } subject { namespace_project_snippets_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -237,9 +264,11 @@ describe "Public Project Access", feature: true do ...@@ -237,9 +264,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/snippets/new" do describe "GET /:project_path/snippets/new" do
subject { new_namespace_project_snippet_path(project.namespace, project) } subject { new_namespace_project_snippet_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -249,9 +278,11 @@ describe "Public Project Access", feature: true do ...@@ -249,9 +278,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/merge_requests" do describe "GET /:project_path/merge_requests" do
subject { namespace_project_merge_requests_path(project.namespace, project) } subject { namespace_project_merge_requests_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -261,9 +292,11 @@ describe "Public Project Access", feature: true do ...@@ -261,9 +292,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/merge_requests/new" do describe "GET /:project_path/merge_requests/new" do
subject { new_namespace_project_merge_request_path(project.namespace, project) } subject { new_namespace_project_merge_request_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
...@@ -278,9 +311,11 @@ describe "Public Project Access", feature: true do ...@@ -278,9 +311,11 @@ describe "Public Project Access", feature: true do
allow_any_instance_of(Project).to receive(:branches).and_return([]) allow_any_instance_of(Project).to receive(:branches).and_return([])
end end
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -295,9 +330,11 @@ describe "Public Project Access", feature: true do ...@@ -295,9 +330,11 @@ describe "Public Project Access", feature: true do
allow_any_instance_of(Project).to receive(:tags).and_return([]) allow_any_instance_of(Project).to receive(:tags).and_return([])
end end
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter } it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest } it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external } it { is_expected.to be_allowed_for :external }
...@@ -307,9 +344,11 @@ describe "Public Project Access", feature: true do ...@@ -307,9 +344,11 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path/hooks" do describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) } subject { namespace_project_hooks_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master } it { is_expected.to be_allowed_for master }
it { is_expected.to be_denied_for developer }
it { is_expected.to be_denied_for reporter } it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest } it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
......
require 'spec_helper'
describe GroupProjectsFinder do
let(:group) { create(:group) }
let(:current_user) { create(:user) }
let(:finder) { described_class.new(source_user) }
let!(:public_project) { create(:project, :public, group: group, path: '1') }
let!(:private_project) { create(:project, :private, group: group, path: '2') }
let!(:shared_project_1) { create(:project, :public, path: '3') }
let!(:shared_project_2) { create(:project, :private, path: '4') }
let!(:shared_project_3) { create(:project, :internal, path: '5') }
before do
shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
shared_project_2.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
shared_project_3.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
end
describe 'with a group member current user' do
before { group.add_user(current_user, Gitlab::Access::MASTER) }
context "only shared" do
subject { described_class.new(group, only_shared: true).execute(current_user) }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
end
context "only owned" do
subject { described_class.new(group, only_owned: true).execute(current_user) }
it { is_expected.to eq([private_project, public_project]) }
end
context "all" do
subject { described_class.new(group).execute(current_user) }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) }
end
end
describe 'without group member current_user' do
before { shared_project_2.team << [current_user, Gitlab::Access::MASTER] }
context "only shared" do
context "without external user" do
subject { described_class.new(group, only_shared: true).execute(current_user) }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
end
context "with external user" do
before { current_user.update_attributes(external: true) }
subject { described_class.new(group, only_shared: true).execute(current_user) }
it { is_expected.to eq([shared_project_2, shared_project_1]) }
end
end
context "only owned" do
context "without external user" do
before { private_project.team << [current_user, Gitlab::Access::MASTER] }
subject { described_class.new(group, only_owned: true).execute(current_user) }
it { is_expected.to eq([private_project, public_project]) }
end
context "with external user" do
before { current_user.update_attributes(external: true) }
subject { described_class.new(group, only_owned: true).execute(current_user) }
it { is_expected.to eq([public_project]) }
end
context "all" do
subject { described_class.new(group).execute(current_user) }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, public_project]) }
end
end
end
describe "no user" do
context "only shared" do
subject { described_class.new(group, only_shared: true).execute(current_user) }
it { is_expected.to eq([shared_project_3, shared_project_1]) }
end
context "only owned" do
subject { described_class.new(group, only_owned: true).execute(current_user) }
it { is_expected.to eq([public_project]) }
end
end
end
require 'spec_helper'
describe GroupsFinder do
describe '#execute' do
let(:user) { create(:user) }
let!(:private_group) { create(:group, :private) }
let!(:internal_group) { create(:group, :internal) }
let!(:public_group) { create(:group, :public) }
let(:finder) { described_class.new }
describe 'execute' do
describe 'without a user' do
subject { finder.execute }
it { is_expected.to eq([public_group]) }
end
describe 'with a user' do
subject { finder.execute(user) }
context 'normal user' do
it { is_expected.to eq([public_group, internal_group]) }
end
context 'external user' do
let(:user) { create(:user, external: true) }
it { is_expected.to eq([public_group]) }
end
end
end
end
end
require 'spec_helper'
describe JoinedGroupsFinder do
describe '#execute' do
let!(:profile_owner) { create(:user) }
let!(:profile_visitor) { create(:user) }
let!(:private_group) { create(:group, :private) }
let!(:private_group_2) { create(:group, :private) }
let!(:internal_group) { create(:group, :internal) }
let!(:internal_group_2) { create(:group, :internal) }
let!(:public_group) { create(:group, :public) }
let!(:public_group_2) { create(:group, :public) }
let!(:finder) { described_class.new(profile_owner) }
context 'without a user' do
before do
public_group.add_master(profile_owner)
end
it 'only shows public groups from profile owner' do
expect(finder.execute).to eq([public_group])
end
end
context "with a user" do
before do
private_group.add_master(profile_owner)
internal_group.add_master(profile_owner)
public_group.add_master(profile_owner)
end
context "when the profile visitor is in the private group" do
before do
private_group.add_developer(profile_visitor)
end
it 'only shows groups where both users are authorized to see' do
expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group])
end
end
context 'if profile visitor is in one of the private group projects' do
before do
project = create(:project, :private, group: private_group, name: 'B', path: 'B')
project.team.add_user(profile_visitor, Gitlab::Access::DEVELOPER)
end
it 'shows group' do
expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group])
end
end
context 'external users' do
before do
profile_visitor.update_attributes(external: true)
end
context 'if not a member' do
it "does not show internal groups" do
expect(finder.execute(profile_visitor)).to eq([public_group])
end
end
context "if authorized" do
before do
internal_group.add_master(profile_visitor)
end
it "shows internal groups if authorized" do
expect(finder.execute(profile_visitor)).to eq([public_group, internal_group])
end
end
end
end
end
end
require 'spec_helper' require 'spec_helper'
describe PersonalProjectsFinder do describe PersonalProjectsFinder do
let(:source_user) { create(:user) } let(:source_user) { create(:user) }
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
let(:finder) { described_class.new(source_user) }
let!(:public_project) { create(:project, :public, namespace: source_user.namespace) }
let(:finder) { described_class.new(source_user) } let!(:private_project) do
create(:project, :private, namespace: source_user.namespace, path: 'mepmep')
let!(:public_project) do
create(:project, :public, namespace: source_user.namespace, name: 'A',
path: 'A')
end end
let!(:private_project) do let!(:internal_project) do
create(:project, :private, namespace: source_user.namespace, name: 'B', create(:project, :internal, namespace: source_user.namespace, path: 'C')
path: 'B')
end end
before do before do
...@@ -29,6 +27,14 @@ describe PersonalProjectsFinder do ...@@ -29,6 +27,14 @@ describe PersonalProjectsFinder do
describe 'with a current user' do describe 'with a current user' do
subject { finder.execute(current_user) } subject { finder.execute(current_user) }
it { is_expected.to eq([private_project, public_project]) } context 'normal user' do
it { is_expected.to eq([internal_project, private_project, public_project]) }
end
context 'external' do
before { current_user.update_attributes(external: true) }
it { is_expected.to eq([private_project, public_project]) }
end
end end
end end
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe ProjectsFinder do describe ProjectsFinder do
describe '#execute' do describe '#execute' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group, :public) }
let!(:private_project) do let!(:private_project) do
create(:project, :private, name: 'A', path: 'A') create(:project, :private, name: 'A', path: 'A')
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe SnippetsFinder do describe SnippetsFinder do
let(:user) { create :user } let(:user) { create :user }
let(:user1) { create :user } let(:user1) { create :user }
let(:group) { create :group } let(:group) { create :group, :public }
let(:project1) { create(:empty_project, :public, group: group) } let(:project1) { create(:empty_project, :public, group: group) }
let(:project2) { create(:empty_project, :private, group: group) } let(:project2) { create(:empty_project, :private, group: group) }
......
...@@ -11,7 +11,7 @@ describe LabelsHelper do ...@@ -11,7 +11,7 @@ describe LabelsHelper do
end end
it 'uses the instance variable' do it 'uses the instance variable' do
expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has_tooltip".*</span></a>} expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has-tooltip".*</span></a>}
end end
end end
...@@ -41,8 +41,8 @@ describe LabelsHelper do ...@@ -41,8 +41,8 @@ describe LabelsHelper do
context 'with a tooltip argument' do context 'with a tooltip argument' do
context 'set to false' do context 'set to false' do
it 'does not include the has_tooltip class' do it 'does not include the has-tooltip class' do
expect(link_to_label(label, tooltip: false)).not_to match %r{has_tooltip} expect(link_to_label(label, tooltip: false)).not_to match %r{has-tooltip}
end end
end end
end end
......
...@@ -11,16 +11,8 @@ describe ProjectsHelper do ...@@ -11,16 +11,8 @@ describe ProjectsHelper do
describe "can_change_visibility_level?" do describe "can_change_visibility_level?" do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:fork_project) do
fork_project = create(:forked_project_with_submodules)
fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
fork_project.save
fork_project
end
let(:user) { create(:user) } let(:user) { create(:user) }
let(:fork_project) { Projects::ForkService.new(project, user).execute }
it "returns false if there are no appropriate permissions" do it "returns false if there are no appropriate permissions" do
allow(helper).to receive(:can?) { false } allow(helper).to receive(:can?) { false }
......
...@@ -8,6 +8,7 @@ describe VisibilityLevelHelper do ...@@ -8,6 +8,7 @@ describe VisibilityLevelHelper do
end end
let(:project) { build(:project) } let(:project) { build(:project) }
let(:group) { build(:group) }
let(:personal_snippet) { build(:personal_snippet) } let(:personal_snippet) { build(:personal_snippet) }
let(:project_snippet) { build(:project_snippet) } let(:project_snippet) { build(:project_snippet) }
...@@ -19,6 +20,13 @@ describe VisibilityLevelHelper do ...@@ -19,6 +20,13 @@ describe VisibilityLevelHelper do
end end
end end
context 'used with a Group' do
it 'delegates groups to #group_visibility_level_description' do
expect(visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, group))
.to match /group/i
end
end
context 'called with a Snippet' do context 'called with a Snippet' do
it 'delegates snippets to #snippet_visibility_level_description' do it 'delegates snippets to #snippet_visibility_level_description' do
expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet)) expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet))
...@@ -58,13 +66,8 @@ describe VisibilityLevelHelper do ...@@ -58,13 +66,8 @@ describe VisibilityLevelHelper do
describe "skip_level?" do describe "skip_level?" do
describe "forks" do describe "forks" do
let(:project) { create(:project, :internal) } let(:project) { create(:project, :internal) }
let(:fork_project) { create(:forked_project_with_submodules) } let(:fork_project) { create(:project, forked_from_project: project) }
before do
fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
fork_project.save
end
it "skips levels" do it "skips levels" do
expect(skip_level?(fork_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy expect(skip_level?(fork_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy
......
...@@ -56,7 +56,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do ...@@ -56,7 +56,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
describe 'label span element' do describe 'label span element' do
it 'includes default classes' do it 'includes default classes' do
doc = reference_filter("Label #{reference}") doc = reference_filter("Label #{reference}")
expect(doc.css('a span').first.attr('class')).to eq 'label color-label has_tooltip' expect(doc.css('a span').first.attr('class')).to eq 'label color-label has-tooltip'
end end
it 'includes a style attribute' do it 'includes a style attribute' do
......
...@@ -119,7 +119,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do ...@@ -119,7 +119,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
context 'with data-group' do context 'with data-group' do
it 'removes unpermitted Group references' do it 'removes unpermitted Group references' do
user = create(:user) user = create(:user)
group = create(:group) group = create(:group, :private)
link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
doc = filter(link, current_user: user) doc = filter(link, current_user: user)
...@@ -129,7 +129,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do ...@@ -129,7 +129,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
it 'allows permitted Group references' do it 'allows permitted Group references' do
user = create(:user) user = create(:user)
group = create(:group) group = create(:group, :private)
group.add_developer(user) group.add_developer(user)
link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
......
...@@ -56,6 +56,23 @@ describe Group, models: true do ...@@ -56,6 +56,23 @@ describe Group, models: true do
end end
end end
describe 'scopes' do
let!(:private_group) { create(:group, :private) }
let!(:internal_group) { create(:group, :internal) }
describe 'public_only' do
subject { described_class.public_only.to_a }
it{ is_expected.to eq([group]) }
end
describe 'public_and_internal_only' do
subject { described_class.public_and_internal_only.to_a }
it{ is_expected.to match_array([group, internal_group]) }
end
end
describe '#to_reference' do describe '#to_reference' do
it 'returns a String reference to the object' do it 'returns a String reference to the object' do
expect(group.to_reference).to eq "@#{group.name}" expect(group.to_reference).to eq "@#{group.name}"
......
...@@ -37,6 +37,11 @@ describe Issue, models: true do ...@@ -37,6 +37,11 @@ describe Issue, models: true do
subject { create(:issue) } subject { create(:issue) }
describe "act_as_paranoid" do
it { is_expected.to have_db_column(:deleted_at) }
it { is_expected.to have_db_index(:deleted_at) }
end
describe '#to_reference' do describe '#to_reference' do
it 'returns a String reference to the object' do it 'returns a String reference to the object' do
expect(subject.to_reference).to eq "##{subject.iid}" expect(subject.to_reference).to eq "##{subject.iid}"
......
...@@ -49,6 +49,11 @@ describe MergeRequest, models: true do ...@@ -49,6 +49,11 @@ describe MergeRequest, models: true do
it { is_expected.to include_module(Taskable) } it { is_expected.to include_module(Taskable) }
end end
describe "act_as_paranoid" do
it { is_expected.to have_db_column(:deleted_at) }
it { is_expected.to have_db_index(:deleted_at) }
end
describe 'validation' do describe 'validation' do
it { is_expected.to validate_presence_of(:target_branch) } it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) } it { is_expected.to validate_presence_of(:source_branch) }
......
...@@ -18,11 +18,11 @@ describe Project, models: true do ...@@ -18,11 +18,11 @@ describe Project, models: true do
let(:report_actions) { Ability.project_report_rules } let(:report_actions) { Ability.project_report_rules }
let(:dev_actions) { Ability.project_dev_rules } let(:dev_actions) { Ability.project_dev_rules }
let(:master_actions) { Ability.project_master_rules } let(:master_actions) { Ability.project_master_rules }
let(:admin_actions) { Ability.project_admin_rules } let(:owner_actions) { Ability.project_owner_rules }
describe "Non member rules" do describe "Non member rules" do
it "should deny for non-project users any actions" do it "should deny for non-project users any actions" do
admin_actions.each do |action| owner_actions.each do |action|
expect(@abilities.allowed?(@u1, action, @p1)).to be_falsey expect(@abilities.allowed?(@u1, action, @p1)).to be_falsey
end end
end end
...@@ -90,20 +90,20 @@ describe Project, models: true do ...@@ -90,20 +90,20 @@ describe Project, models: true do
end end
end end
describe "Admin Rules" do describe "Owner Rules" do
before do before do
@p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::DEVELOPER) @p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::DEVELOPER)
@p1.project_members.create(project: @p1, user: @u3, access_level: ProjectMember::MASTER) @p1.project_members.create(project: @p1, user: @u3, access_level: ProjectMember::MASTER)
end end
it "should deny for masters admin-specific actions" do it "should deny for masters admin-specific actions" do
[admin_actions - master_actions].each do |action| [owner_actions - master_actions].each do |action|
expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
end end
end end
it "should allow for project owner any admin actions" do it "should allow for project owner any admin actions" do
admin_actions.each do |action| owner_actions.each do |action|
expect(@abilities.allowed?(@u4, action, @p1)).to be_truthy expect(@abilities.allowed?(@u4, action, @p1)).to be_truthy
end end
end end
......
...@@ -442,7 +442,7 @@ describe Project, models: true do ...@@ -442,7 +442,7 @@ describe Project, models: true do
end end
describe '.trending' do describe '.trending' do
let(:group) { create(:group) } let(:group) { create(:group, :public) }
let(:project1) { create(:empty_project, :public, group: group) } let(:project1) { create(:empty_project, :public, group: group) }
let(:project2) { create(:empty_project, :public, group: group) } let(:project2) { create(:empty_project, :public, group: group) }
...@@ -571,12 +571,8 @@ describe Project, models: true do ...@@ -571,12 +571,8 @@ describe Project, models: true do
end end
context 'when checking on forked project' do context 'when checking on forked project' do
let(:forked_project) { create :forked_project_with_submodules } let(:project) { create(:project, :internal) }
let(:forked_project) { create(:project, forked_from_project: project) }
before do
forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
forked_project.save
end
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy } it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy } it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
...@@ -721,6 +717,22 @@ describe Project, models: true do ...@@ -721,6 +717,22 @@ describe Project, models: true do
end end
end end
context 'when checking projects from groups' do
let(:private_group) { create(:group, visibility_level: 0) }
let(:internal_group) { create(:group, visibility_level: 10) }
let(:private_project) { create :project, :private, group: private_group }
let(:internal_project) { create :project, :internal, group: internal_group }
context 'when group is private project can not be internal' do
it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey }
end
context 'when group is internal project can not be public' do
it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
end
end
describe '#create_repository' do describe '#create_repository' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:shell) { Gitlab::Shell.new } let(:shell) { Gitlab::Shell.new }
......
...@@ -11,7 +11,7 @@ describe API::API, api: true do ...@@ -11,7 +11,7 @@ describe API::API, api: true do
let(:stranger) { create(:user) } let(:stranger) { create(:user) }
let!(:group_with_members) do let!(:group_with_members) do
group = create(:group) group = create(:group, :private)
group.add_users([reporter.id], GroupMember::REPORTER) group.add_users([reporter.id], GroupMember::REPORTER)
group.add_users([developer.id], GroupMember::DEVELOPER) group.add_users([developer.id], GroupMember::DEVELOPER)
group.add_users([master.id], GroupMember::MASTER) group.add_users([master.id], GroupMember::MASTER)
......
...@@ -9,7 +9,7 @@ describe API::API, api: true do ...@@ -9,7 +9,7 @@ describe API::API, api: true do
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') } let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') }
let!(:group1) { create(:group, avatar: File.open(avatar_file_path)) } let!(:group1) { create(:group, avatar: File.open(avatar_file_path)) }
let!(:group2) { create(:group) } let!(:group2) { create(:group, :private) }
let!(:project1) { create(:project, namespace: group1) } let!(:project1) { create(:project, namespace: group1) }
let!(:project2) { create(:project, namespace: group2) } let!(:project2) { create(:project, namespace: group2) }
......
...@@ -2,12 +2,12 @@ require 'spec_helper' ...@@ -2,12 +2,12 @@ require 'spec_helper'
describe API::API, api: true do describe API::API, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:author) { create(:author) } let(:author) { create(:author) }
let(:assignee) { create(:assignee) } let(:assignee) { create(:assignee) }
let(:admin) { create(:admin) } let(:admin) { create(:user, :admin) }
let!(:project) { create(:project, :public, namespace: user.namespace ) } let!(:project) { create(:project, :public, namespace: user.namespace ) }
let!(:closed_issue) do let!(:closed_issue) do
create :closed_issue, create :closed_issue,
author: user, author: user,
...@@ -469,9 +469,25 @@ describe API::API, api: true do ...@@ -469,9 +469,25 @@ describe API::API, api: true do
end end
describe "DELETE /projects/:id/issues/:issue_id" do describe "DELETE /projects/:id/issues/:issue_id" do
it "should delete a project issue" do it "rejects a non member from deleting an issue" do
delete api("/projects/#{project.id}/issues/#{issue.id}", user) delete api("/projects/#{project.id}/issues/#{issue.id}", non_member)
expect(response.status).to eq(405) expect(response.status).to be(403)
end
it "rejects a developer from deleting an issue" do
delete api("/projects/#{project.id}/issues/#{issue.id}", author)
expect(response.status).to be(403)
end
context "when the user is project owner" do
let(:owner) { create(:user) }
let(:project) { create(:project, namespace: owner.namespace) }
it "deletes the issue if an admin requests it" do
delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
expect(response.status).to eq(200)
expect(json_response['state']).to eq 'opened'
end
end end
end end
end end
...@@ -2,15 +2,17 @@ require "spec_helper" ...@@ -2,15 +2,17 @@ require "spec_helper"
describe API::API, api: true do describe API::API, api: true do
include ApiHelpers include ApiHelpers
let(:base_time) { Time.now } let(:base_time) { Time.now }
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } let(:admin) { create(:user, :admin) }
let(:non_member) { create(:user) }
let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) } let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) } let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) } let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") } let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) } let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
before do before do
project.team << [user, :reporters] project.team << [user, :reporters]
...@@ -315,6 +317,29 @@ describe API::API, api: true do ...@@ -315,6 +317,29 @@ describe API::API, api: true do
end end
end end
describe "DELETE /projects/:id/merge_requests/:merge_request_id" do
context "when the user is developer" do
let(:developer) { create(:user) }
before do
project.team << [developer, :developer]
end
it "denies the deletion of the merge request" do
delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", developer)
expect(response.status).to be(403)
end
end
context "when the user is project owner" do
it "destroys the merge request owners can destroy" do
delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
expect(response.status).to eq(200)
end
end
end
describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
it "should return merge_request" do it "should return merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close" put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
......
...@@ -275,6 +275,7 @@ describe API::API, api: true do ...@@ -275,6 +275,7 @@ describe API::API, api: true do
it 'should not allow a non-admin to use a restricted visibility level' do it 'should not allow a non-admin to use a restricted visibility level' do
post api('/projects', user), @project post api('/projects', user), @project
expect(response.status).to eq(400) expect(response.status).to eq(400)
expect(json_response['message']['visibility_level'].first).to( expect(json_response['message']['visibility_level'].first).to(
match('restricted by your GitLab administrator') match('restricted by your GitLab administrator')
......
...@@ -23,7 +23,7 @@ describe CreateSnippetService, services: true do ...@@ -23,7 +23,7 @@ describe CreateSnippetService, services: true do
snippet = create_snippet(nil, @user, @opts) snippet = create_snippet(nil, @user, @opts)
expect(snippet.errors.messages).to have_key(:visibility_level) expect(snippet.errors.messages).to have_key(:visibility_level)
expect(snippet.errors.messages[:visibility_level].first).to( expect(snippet.errors.messages[:visibility_level].first).to(
match('Public visibility has been restricted') match('has been restricted')
) )
end end
......
require 'spec_helper'
describe Groups::CreateService, services: true do
let!(:user) { create(:user) }
let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
describe "execute" do
let!(:service) { described_class.new(user, group_params ) }
subject { service.execute }
context "create groups without restricted visibility level" do
it { is_expected.to be_persisted }
end
context "cannot create group with restricted visibility level" do
before { allow(current_application_settings).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) }
it { is_expected.to_not be_persisted }
end
end
end
require 'spec_helper'
describe Groups::UpdateService, services: true do
let!(:user) { create(:user) }
let!(:private_group) { create(:group, :private) }
let!(:internal_group) { create(:group, :internal) }
let!(:public_group) { create(:group, :public) }
describe "#execute" do
context "project visibility_level validation" do
context "public group with public projects" do
let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL ) }
before do
public_group.add_user(user, Gitlab::Access::MASTER)
create(:project, :public, group: public_group)
end
it "does not change permission level" do
service.execute
expect(public_group.errors.count).to eq(1)
end
end
context "internal group with internal project" do
let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE ) }
before do
internal_group.add_user(user, Gitlab::Access::MASTER)
create(:project, :internal, group: internal_group)
end
it "does not change permission level" do
service.execute
expect(internal_group.errors.count).to eq(1)
end
end
end
end
context "unauthorized visibility_level validation" do
let!(:service) { described_class.new(internal_group, user, visibility_level: 99 ) }
before do
internal_group.add_user(user, Gitlab::Access::MASTER)
end
it "does not change permission level" do
service.execute
expect(internal_group.errors.count).to eq(1)
end
end
end
...@@ -25,7 +25,7 @@ describe UpdateSnippetService, services: true do ...@@ -25,7 +25,7 @@ describe UpdateSnippetService, services: true do
update_snippet(@project, @user, @snippet, @opts) update_snippet(@project, @user, @snippet, @opts)
expect(@snippet.errors.messages).to have_key(:visibility_level) expect(@snippet.errors.messages).to have_key(:visibility_level)
expect(@snippet.errors.messages[:visibility_level].first).to( expect(@snippet.errors.messages[:visibility_level].first).to(
match('Public visibility has been restricted') match('has been restricted')
) )
expect(@snippet.visibility_level).to eq(old_visibility) expect(@snippet.visibility_level).to eq(old_visibility)
end end
......
...@@ -28,7 +28,7 @@ module AccessMatchers ...@@ -28,7 +28,7 @@ module AccessMatchers
if user.kind_of?(User) if user.kind_of?(User)
# User#inspect displays too much information for RSpec's description # User#inspect displays too much information for RSpec's description
# messages # messages
"be #{type} for supplied User" "be #{type} for the specified user"
else else
"be #{type} for #{user}" "be #{type} for #{user}"
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