Commit 3a78f7ca authored by Robert Speicher's avatar Robert Speicher

Merge branch 'issue_12658' into 'master'

Add group visibility level

Supersedes https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3051

Closes #12658

See merge request !3323
parents 0305dd98 503244eb
...@@ -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
...@@ -111,6 +111,24 @@ ...@@ -111,6 +111,24 @@
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 {
......
...@@ -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;
......
...@@ -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
......
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!
unless @group and can?(current_user, :read_group, @group)
if current_user.nil? if current_user.nil?
return authenticate_user! authenticate_user!
else else
return render_404 render_404
end end
end end
end end
@group
end
def group_projects
@projects ||= GroupProjectsFinder.new(group).execute(current_user)
end
def authorize_admin_group! def authorize_admin_group!
unless can?(current_user, :admin_group, group) unless can?(current_user, :admin_group, group)
return render_404 return render_404
......
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 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 super
end end
end
def require_non_empty_project
redirect_to namespace_project_path(@project.namespace, @project) if @project.empty_repo?
end
def require_branch_head def require_branch_head
unless @repository.branch_names.include?(@ref) unless @repository.branch_names.include?(@ref)
...@@ -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::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.
new([authorized.select(:id), public_projects.select(:id)])
Project.where("projects.id IN (#{union.to_sql})") projects << @user.contributed_projects.visible_to_user(current_user) if current_user
end projects << @user.contributed_projects.public_to_user(current_user)
def public_projects 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.
new([authorized.select(:id), public_and_internal_projects.select(:id)])
Project.where("projects.id IN (#{union.to_sql})") projects << @user.personal_projects.visible_to_user(current_user) if current_user
end projects << @user.personal_projects.public_to_user(current_user)
def public_projects
@user.personal_projects.public_only
end
def public_and_internal_projects 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]
if group
segments = group_projects(current_user, group)
else
segments = all_projects(current_user) segments = all_projects(current_user)
end
if segments.length > 1 find_union(segments, Project)
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
...@@ -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)
......
...@@ -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,8 +228,8 @@ class Ability ...@@ -228,8 +228,8 @@ 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,
...@@ -275,11 +275,9 @@ class Ability ...@@ -275,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,
...@@ -292,13 +290,23 @@ class Ability ...@@ -292,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)
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
# 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
# visibility_level :integer default(20), not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# type :string(255) # type :string(255)
...@@ -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
...@@ -443,10 +439,25 @@ class Project < ActiveRecord::Base ...@@ -443,10 +439,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
...@@ -962,9 +973,25 @@ class Project < ActiveRecord::Base ...@@ -962,9 +973,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,18 +3,15 @@ module Projects ...@@ -3,18 +3,15 @@ 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 end
return false unless visibility_level_allowed?(new_visibility)
end
new_branch = params[:default_branch] new_branch = params[:default_branch]
if project.repository.exists? && new_branch && new_branch != project.default_branch if project.repository.exists? && new_branch && new_branch != project.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
......
...@@ -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
......
...@@ -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
%h1
= @group.name = @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,8 +27,7 @@ ...@@ -27,8 +27,7 @@
.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
...@@ -54,7 +53,3 @@ ...@@ -54,7 +53,3 @@
- if @shared_projects.present? - if @shared_projects.present?
.tab-pane#shared .tab-pane#shared
= render "shared_projects", projects: @shared_projects = render "shared_projects", projects: @shared_projects
- else
%p.nav-links.no-top
No projects to show
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
.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
......
...@@ -12,13 +12,11 @@ ...@@ -12,13 +12,11 @@
= 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
- if current_user
= nav_link(controller: [:group, :milestones]) do = nav_link(controller: [:group, :milestones]) do
= link_to group_milestones_path(@group), title: 'Milestones' do = link_to group_milestones_path(@group), title: 'Milestones' do
= icon('clock-o fw') = icon('clock-o fw')
...@@ -29,15 +27,15 @@ ...@@ -29,15 +27,15 @@
= icon('exclamation-circle fw') = icon('exclamation-circle fw')
%span %span
Issues Issues
- if current_user - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.count= number_with_delimiter(Issue.opened.of_group(@group).count) %span.count= number_with_delimiter(issues.count)
= nav_link(path: 'groups#merge_requests') do = nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
= icon('tasks fw') = icon('tasks fw')
%span %span
Merge Requests Merge Requests
- if current_user - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.count= number_with_delimiter(MergeRequest.opened.of_group(@group).count) %span.count= number_with_delimiter(merge_requests.count)
= nav_link(controller: [:group_members]) do = nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
= icon('users fw') = icon('users fw')
......
...@@ -2,18 +2,18 @@ ...@@ -2,18 +2,18 @@
.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)
......
%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
...@@ -21,6 +21,9 @@ ...@@ -21,6 +21,9 @@
= 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
......
...@@ -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
......
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
add_index :namespaces, :visibility_level
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|
...@@ -595,6 +596,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -595,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
...@@ -604,6 +606,7 @@ ActiveRecord::Schema.define(version: 20160317092222) do ...@@ -604,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
......
...@@ -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
......
...@@ -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
......
...@@ -16,13 +16,8 @@ describe NamespacesController do ...@@ -16,13 +16,8 @@ describe NamespacesController do
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
......
...@@ -127,13 +127,8 @@ describe UploadsController do ...@@ -127,13 +127,8 @@ describe UploadsController do
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
This diff is collapsed.
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
...@@ -3,17 +3,15 @@ require 'spec_helper' ...@@ -3,17 +3,15 @@ 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(:finder) { described_class.new(source_user) }
let!(:public_project) { create(:project, :public, namespace: source_user.namespace) }
let!(:public_project) do let!(:private_project) do
create(:project, :public, namespace: source_user.namespace, name: 'A', create(:project, :private, namespace: source_user.namespace, path: 'mepmep')
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) }
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]) } 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,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))
...@@ -59,12 +67,7 @@ describe VisibilityLevelHelper do ...@@ -59,12 +67,7 @@ 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
......
...@@ -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}"
......
...@@ -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) }
......
...@@ -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
......
This diff is collapsed.
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