Commit d9bb4230 authored by Jason Hollingsworth's avatar Jason Hollingsworth

Adding authenticated public mode (internal).

Added visibility_level icons to project view (rather than just text).
Added public projects to search results.
Added ability to restrict visibility levels standard users can set.
parent 51b5509b
...@@ -365,6 +365,10 @@ table { ...@@ -365,6 +365,10 @@ table {
&.input-large { &.input-large {
width: 210px; width: 210px;
} }
&.input-clamp {
max-width: 100%;
}
} }
.user-result { .user-result {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
.cblue { color: #29A } .cblue { color: #29A }
.cblack { color: #111 } .cblack { color: #111 }
.cdark { color: #444 } .cdark { color: #444 }
.camber { color: #ffc000 }
.cwhite { color: #fff!important } .cwhite { color: #fff!important }
.bgred { background: #F2DEDE!important } .bgred { background: #F2DEDE!important }
......
...@@ -20,6 +20,15 @@ ...@@ -20,6 +20,15 @@
label { width: 110px; } label { width: 110px; }
.controls { margin-left: 130px; } .controls { margin-left: 130px; }
.form-actions { padding-left: 130px; background: #fff } .form-actions { padding-left: 130px; background: #fff }
.visibility-levels {
.controls {
margin-bottom: 9px;
}
i {
color: inherit;
}
}
} }
.broadcast-messages { .broadcast-messages {
......
...@@ -19,6 +19,12 @@ ...@@ -19,6 +19,12 @@
padding-bottom: 25px; padding-bottom: 25px;
margin-bottom: 30px; margin-bottom: 30px;
&.empty-project {
border-bottom: 0px;
padding-bottom: 15px;
margin-bottom: 0px;
}
.project-home-title { .project-home-title {
font-size: 18px; font-size: 18px;
color: #777; color: #777;
...@@ -45,7 +51,7 @@ ...@@ -45,7 +51,7 @@
} }
} }
.public-label { .visibility-level-label {
font-size: 14px; font-size: 14px;
background: #f1f1f1; background: #f1f1f1;
padding: 8px 10px; padding: 8px 10px;
...@@ -53,6 +59,10 @@ ...@@ -53,6 +59,10 @@
margin-left: 10px; margin-left: 10px;
color: #888; color: #888;
text-shadow: 0 1px 1px #FFF; text-shadow: 0 1px 1px #FFF;
i {
color: inherit;
}
} }
} }
...@@ -87,9 +97,33 @@ ...@@ -87,9 +97,33 @@
} }
} }
.project-public-holder { .project-visibility-level-holder {
.help-inline { .controls {
padding-top: 7px; padding-bottom: 9px;
}
.controls {
input {
float: left;
}
.descr {
display: block;
margin-left: 1.5em;
&.restricted {
color: #888;
}
}
.info {
display: block;
margin-top: 5px;
}
strong {
display: inline-block;
width: 4em;
}
}
i {
color: inherit;
} }
} }
...@@ -130,7 +164,8 @@ ul.nav.nav-projects-tabs { ...@@ -130,7 +164,8 @@ ul.nav.nav-projects-tabs {
margin: 0px; margin: 0px;
} }
.my-projects { .my-projects,
.public-projects {
li { li {
.project-info { .project-info {
margin-bottom: 10px; margin-bottom: 10px;
......
...@@ -8,6 +8,11 @@ module Projects ...@@ -8,6 +8,11 @@ module Projects
# get namespace id # get namespace id
namespace_id = params.delete(:namespace_id) namespace_id = params.delete(:namespace_id)
# check that user is allowed to set specified visibility_level
unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
params.delete(:visibility_level)
end
# Load default feature settings # Load default feature settings
default_features = Gitlab.config.gitlab.default_projects_features default_features = Gitlab.config.gitlab.default_projects_features
...@@ -17,7 +22,7 @@ module Projects ...@@ -17,7 +22,7 @@ module Projects
wall_enabled: default_features.wall, wall_enabled: default_features.wall,
snippets_enabled: default_features.snippets, snippets_enabled: default_features.snippets,
merge_requests_enabled: default_features.merge_requests, merge_requests_enabled: default_features.merge_requests,
public: default_features.public visibility_level: default_features.visibility_level
}.stringify_keys }.stringify_keys
@project = Project.new(default_opts.merge(params)) @project = Project.new(default_opts.merge(params))
......
...@@ -2,7 +2,11 @@ module Projects ...@@ -2,7 +2,11 @@ module Projects
class UpdateContext < BaseContext class UpdateContext < BaseContext
def execute(role = :default) def execute(role = :default)
params[:project].delete(:namespace_id) params[:project].delete(:namespace_id)
params[:project].delete(:public) unless can?(current_user, :change_public_mode, project) # check that user is allowed to set specified visibility_level
unless can?(current_user, :change_visibility_level, project) && Gitlab::VisibilityLevel.allowed_for?(current_user, params[:project][:visibility_level])
params[:project].delete(:visibility_level)
end
new_branch = params[:project].delete(:default_branch) new_branch = params[:project].delete(:default_branch)
if project.repository.exists? && new_branch != project.repository.root_ref if project.repository.exists? && new_branch != project.repository.root_ref
......
class SearchContext class SearchContext
attr_accessor :project_ids, :params attr_accessor :project_ids, :current_user, :params
def initialize(project_ids, params) def initialize(project_ids, user, params)
@project_ids, @params = project_ids, params.dup @project_ids, @current_user, @params = project_ids, user, params.dup
end end
def execute def execute
...@@ -10,7 +10,8 @@ class SearchContext ...@@ -10,7 +10,8 @@ class SearchContext
query = Shellwords.shellescape(query) if query.present? query = Shellwords.shellescape(query) if query.present?
return result unless query.present? return result unless query.present?
result[:projects] = Project.where("projects.id in (?) OR projects.public = true", project_ids).search(query).limit(20) visibility_levels = @current_user ? [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ] : [ Gitlab::VisibilityLevel::PUBLIC ]
result[:projects] = Project.where("projects.id in (?) OR projects.visibility_level in (?)", project_ids, visibility_levels).search(query).limit(20)
# Search inside single project # Search inside single project
single_project_search(Project.where(id: project_ids), query) single_project_search(Project.where(id: project_ids), query)
......
...@@ -8,7 +8,7 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -8,7 +8,7 @@ class Admin::ProjectsController < Admin::ApplicationController
user = User.find_by_id(owner_id) user = User.find_by_id(owner_id)
@projects = user ? user.owned_projects : Project.scoped @projects = user ? user.owned_projects : Project.scoped
@projects = @projects.where(public: true) if params[:public_only].present? @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
@projects = @projects.with_push if params[:with_push].present? @projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present? @projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.search(params[:name]) if params[:name].present?
......
...@@ -102,7 +102,7 @@ class ApplicationController < ActionController::Base ...@@ -102,7 +102,7 @@ class ApplicationController < ActionController::Base
end end
def authorize_code_access! def authorize_code_access!
return access_denied! unless can?(current_user, :download_code, project) or project.public? return access_denied! unless can?(current_user, :download_code, project)
end end
def authorize_push! def authorize_push!
......
...@@ -10,7 +10,7 @@ class Projects::ApplicationController < ApplicationController ...@@ -10,7 +10,7 @@ class Projects::ApplicationController < ApplicationController
id = params[:project_id] || params[:id] id = params[:project_id] || params[:id]
@project = Project.find_with_namespace(id) @project = Project.find_with_namespace(id)
return if @project && @project.public return if @project && @project.public?
end end
super super
......
...@@ -55,7 +55,7 @@ class ProjectsController < ApplicationController ...@@ -55,7 +55,7 @@ class ProjectsController < ApplicationController
end end
def show def show
return authenticate_user! unless @project.public || current_user return authenticate_user! unless @project.public? || current_user
limit = (params[:limit] || 20).to_i limit = (params[:limit] || 20).to_i
@events = @project.events.recent @events = @project.events.recent
......
...@@ -6,7 +6,7 @@ class Public::ProjectsController < ApplicationController ...@@ -6,7 +6,7 @@ class Public::ProjectsController < ApplicationController
layout 'public' layout 'public'
def index def index
@projects = Project.public_only @projects = Project.public_or_internal_only(current_user)
@projects = @projects.search(params[:search]) if params[:search].present? @projects = @projects.search(params[:search]) if params[:search].present?
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
end end
......
...@@ -14,7 +14,7 @@ class SearchController < ApplicationController ...@@ -14,7 +14,7 @@ class SearchController < ApplicationController
project_ids.select! { |id| id == project_id.to_i} project_ids.select! { |id| id == project_id.to_i}
end end
result = SearchContext.new(project_ids, params).execute result = SearchContext.new(project_ids, current_user, params).execute
@projects = result[:projects] @projects = result[:projects]
@merge_requests = result[:merge_requests] @merge_requests = result[:merge_requests]
......
...@@ -11,6 +11,10 @@ module IconsHelper ...@@ -11,6 +11,10 @@ module IconsHelper
content_tag :i, nil, class: 'icon-globe cblue' content_tag :i, nil, class: 'icon-globe cblue'
end end
def internal_icon
content_tag :i, nil, class: 'icon-shield camber'
end
def private_icon def private_icon
content_tag :i, nil, class: 'icon-lock cgreen' content_tag :i, nil, class: 'icon-lock cgreen'
end end
......
module SearchHelper module SearchHelper
def search_autocomplete_source def search_autocomplete_source
return unless current_user return unless current_user
[ [
groups_autocomplete, groups_autocomplete,
projects_autocomplete, projects_autocomplete,
public_projects_autocomplete,
default_autocomplete, default_autocomplete,
project_autocomplete, project_autocomplete,
help_autocomplete help_autocomplete
...@@ -75,4 +75,11 @@ module SearchHelper ...@@ -75,4 +75,11 @@ module SearchHelper
{ label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
end end
end end
# Autocomplete results for the current user's projects
def public_projects_autocomplete
Project.public_or_internal_only(current_user).map do |p|
{ label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
end
end
end end
module VisibilityLevelHelper
def visibility_level_color(level)
case level
when Gitlab::VisibilityLevel::PRIVATE
'cgreen'
when Gitlab::VisibilityLevel::INTERNAL
'camber'
when Gitlab::VisibilityLevel::PUBLIC
'cblue'
end
end
def visibility_level_description(level)
capture_haml do
haml_tag :span do
case level
when Gitlab::VisibilityLevel::PRIVATE
haml_concat "Project access must be granted explicitly for each user."
when Gitlab::VisibilityLevel::INTERNAL
haml_concat "The project can be cloned by"
haml_tag :em, "any logged in user."
haml_concat "It will also be listed on the #{link_to "public access directory", public_root_path} for logged in users."
haml_tag :em, "Any logged in user"
haml_concat "will have #{link_to "Guest", help_permissions_path} permissions on the repository."
when Gitlab::VisibilityLevel::PUBLIC
haml_concat "The project can be cloned"
haml_tag :em, "without any"
haml_concat "authentication."
haml_concat "It will also be listed on the #{link_to "public access directory", public_root_path}."
haml_tag :em, "Any logged in user"
haml_concat "will have #{link_to "Guest", help_permissions_path} permissions on the repository."
end
end
end
end
def visibility_level_icon(level)
case level
when Gitlab::VisibilityLevel::PRIVATE
private_icon
when Gitlab::VisibilityLevel::INTERNAL
internal_icon
when Gitlab::VisibilityLevel::PUBLIC
public_icon
end
end
def visibility_level_label(level)
Project.visibility_levels.key(level)
end
def restricted_visibility_levels
current_user.is_admin? ? [] : gitlab_config.restricted_visibility_levels
end
end
\ No newline at end of file
...@@ -29,7 +29,7 @@ class Ability ...@@ -29,7 +29,7 @@ class Ability
nil nil
end end
if project && project.public if project && project.public?
[ [
:read_project, :read_project,
:read_wiki, :read_wiki,
...@@ -71,7 +71,7 @@ class Ability ...@@ -71,7 +71,7 @@ class Ability
rules << project_guest_rules rules << project_guest_rules
end end
if project.public? if project.public? || project.internal?
rules << public_project_rules rules << public_project_rules
end end
...@@ -89,7 +89,7 @@ class Ability ...@@ -89,7 +89,7 @@ class Ability
def public_project_rules def public_project_rules
project_guest_rules + [ project_guest_rules + [
:download_code, :download_code,
:fork_project, :fork_project
] ]
end end
...@@ -145,7 +145,7 @@ class Ability ...@@ -145,7 +145,7 @@ class Ability
def project_admin_rules def project_admin_rules
project_master_rules + [ project_master_rules + [
:change_namespace, :change_namespace,
:change_public_mode, :change_visibility_level,
:rename_project, :rename_project,
:remove_project :remove_project
] ]
......
...@@ -14,24 +14,25 @@ ...@@ -14,24 +14,25 @@
# merge_requests_enabled :boolean default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null # wiki_enabled :boolean default(TRUE), not null
# namespace_id :integer # namespace_id :integer
# public :boolean default(FALSE), not null
# issues_tracker :string(255) default("gitlab"), not null # issues_tracker :string(255) default("gitlab"), not null
# issues_tracker_id :string(255) # issues_tracker_id :string(255)
# snippets_enabled :boolean default(TRUE), not null # snippets_enabled :boolean default(TRUE), not null
# last_activity_at :datetime # last_activity_at :datetime
# imported :boolean default(FALSE), not null # imported :boolean default(FALSE), not null
# import_url :string(255) # import_url :string(255)
# visibility_level :integer default(0), not null
# #
class Project < ActiveRecord::Base class Project < ActiveRecord::Base
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
extend Enumerize extend Enumerize
ActsAsTaggableOn.strict_case_match = true ActsAsTaggableOn.strict_case_match = true
attr_accessible :name, :path, :description, :issues_tracker, :label_list, attr_accessible :name, :path, :description, :issues_tracker, :label_list,
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
:wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin] :wiki_enabled, :visibility_level, :import_url, :last_activity_at, as: [:default, :admin]
attr_accessible :namespace_id, :creator_id, as: :admin attr_accessible :namespace_id, :creator_id, as: :admin
...@@ -108,7 +109,8 @@ class Project < ActiveRecord::Base ...@@ -108,7 +109,8 @@ class Project < ActiveRecord::Base
scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
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(public: true) } scope :public_only, -> { where(visibility_level: PUBLIC) }
scope :public_or_internal_only, ->(user) { where("visibility_level IN (:levels)", levels: user ? [ INTERNAL, PUBLIC ] : [ PUBLIC ]) }
enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
...@@ -140,6 +142,10 @@ class Project < ActiveRecord::Base ...@@ -140,6 +142,10 @@ class Project < ActiveRecord::Base
where(path: id, namespace_id: nil).last where(path: id, namespace_id: nil).last
end end
end end
def visibility_levels
Gitlab::VisibilityLevel.options
end
end end
def team def team
...@@ -451,4 +457,8 @@ class Project < ActiveRecord::Base ...@@ -451,4 +457,8 @@ class Project < ActiveRecord::Base
def default_branch def default_branch
@default_branch ||= repository.root_ref if repository.exists? @default_branch ||= repository.root_ref if repository.exists?
end end
def visibility_level_field
visibility_level
end
end end
...@@ -10,11 +10,15 @@ ...@@ -10,11 +10,15 @@
.control-group .control-group
= label_tag :owner_id, 'Owner:', class: 'control-label' = label_tag :owner_id, 'Owner:', class: 'control-label'
.controls .controls
= users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large' = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large input-clamp'
.control-group .control-group.visibility-levels
= label_tag :public_only, 'Public Only', class: 'control-label' = label_tag :visibility_level, 'Visibility Levels', class: 'control-label'
- Project.visibility_levels.each do |label, level|
.controls .controls
= check_box_tag :public_only, 1, params[:public_only] = check_box_tag 'visibility_levels[]', level, params[:visibility_levels].present? && params[:visibility_levels].include?(level.to_s)
%span.descr
= visibility_level_icon(level)
= label
.control-group .control-group
= label_tag :with_push, 'Not empty', class: 'control-label' = label_tag :with_push, 'Not empty', class: 'control-label'
.controls .controls
...@@ -42,10 +46,7 @@ ...@@ -42,10 +46,7 @@
%ul.well-list %ul.well-list
- @projects.each do |project| - @projects.each do |project|
%li %li
- if project.public = visibility_level_icon(project.visibility_level)
= public_icon
- else
= private_icon
= link_to project.name_with_namespace, [:admin, project] = link_to project.name_with_namespace, [:admin, project]
.pull-right .pull-right
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
......
...@@ -66,14 +66,10 @@ ...@@ -66,14 +66,10 @@
%li %li
%span.light access: %span.light access:
%strong %strong
- if @project.public %span{ class: visibility_level_color(@project.visibility_level) }
%span.cblue = visibility_level_icon(@project.visibility_level)
%i.icon-share = visibility_level_label(@project.visibility_level)
Public
- else
%span.cgreen
%i.icon-lock
Private
.ui-box .ui-box
.title .title
Transfer project Transfer project
...@@ -88,9 +84,6 @@ ...@@ -88,9 +84,6 @@
.controls .controls
= f.submit 'Transfer', class: 'btn btn-primary' = f.submit 'Transfer', class: 'btn btn-primary'
.span6 .span6
- if @group - if @group
.ui-box .ui-box
......
...@@ -58,10 +58,10 @@ ...@@ -58,10 +58,10 @@
%h4.project-title %h4.project-title
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
= project.name_with_namespace = project.name_with_namespace
- if project.public - unless project.private?
%small.access-icon %small.access-icon
= public_icon = visibility_level_icon(project.visibility_level)
Public = visibility_level_label(project.visibility_level)
- if current_user.can_leave_project?(project) - if current_user.can_leave_project?(project)
.pull-right .pull-right
......
...@@ -51,10 +51,7 @@ ...@@ -51,10 +51,7 @@
%ul.well-list %ul.well-list
- @group.projects.each do |project| - @group.projects.each do |project|
%li %li
- if project.public = visibility_level_icon(project.visibility_level)
= public_icon
- else
= private_icon
= link_to project.name_with_namespace, project = link_to project.name_with_namespace, project
.pull-right .pull-right
= link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
......
...@@ -143,7 +143,7 @@ ...@@ -143,7 +143,7 @@
%td.permission-x &#10003; %td.permission-x &#10003;
%td.permission-x &#10003; %td.permission-x &#10003;
%tr %tr
%td Switch public mode %td Switch visibility level
%td %td
%td %td
%td %td
......
...@@ -2,14 +2,20 @@ ...@@ -2,14 +2,20 @@
%h3.page-title Public Access %h3.page-title Public Access
%p %p
GitLab allows you to open selected projects to be accessed publicly. GitLab allows you to open selected projects to be accessed publicly or internally.
These projects will be cloneable Projects with either of these visibility levels will be listed in the #{link_to "public access directory", public_root_path}. Internal projects will only be available to authenticated users.
%p
= public_icon
Public projects will be cloneable
%em without any %em without any
authentication. authentication.
Also they will be listed on the #{link_to "public access directory", public_root_path}. %p
= internal_icon
Internal projects will be cloneable by
%em any authenticated user.
%ol %ol
%li Go to your project dashboard %li Go to your project dashboard
%li Click on the "Edit" tab %li Click on the "Edit" tab
%li Select "Public clone access" %li Change "Visibility Level"
- empty_repo = @project.empty_repo?
.project-home-panel{:class => ("empty-project" if empty_repo)}
.row
.span5
%h4.project-home-title
= @project.name_with_namespace
%span.visibility-level-label
= visibility_level_icon(@project.visibility_level)
= visibility_level_label(@project.visibility_level)
.span7
- unless empty_repo
.project-home-dropdown
= render "dropdown"
.form-horizontal
= render "shared/clone_panel"
.project-home-extra.clearfix
.project-home-desc
- if @project.description.present?
= @project.description
- if can?(current_user, :admin_project, @project)
&ndash;
%strong= link_to 'Edit', edit_project_path
- unless empty_repo
.project-home-links
= link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
= link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)
= link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)
%span.light.prepend-left-20= repository_size
\ No newline at end of file
.control-group.project-visibility-level-holder
= f.label :visibility_level, "Visibility Level"
- if can_change_visibility_level
- Gitlab::VisibilityLevel.values.each do |level|
- restricted = restricted_visibility_levels.include?(level)
.controls
= f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
%span.descr{:class => ("restricted" if restricted)}
= visibility_level_icon(level)
%strong
= visibility_level_label(level)
= visibility_level_description(level)
- unless restricted_visibility_levels.empty?
.controls
%span.info
Some visibility level settings have been restricted by the administrator.
- else
.controls
%span.info
= visibility_level_icon(visibility_level)
%strong
= visibility_level_label(visibility_level)
= visibility_level_description(visibility_level)
\ No newline at end of file
...@@ -29,22 +29,7 @@ ...@@ -29,22 +29,7 @@
.controls= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen'}) .controls= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen'})
- if can?(current_user, :change_public_mode, @project) = render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project)
%fieldset.public-mode
%legend
Public mode:
.control-group
= f.label :public, class: 'control-label' do
%span Public access
.controls
= f.check_box :public
%span.descr
If checked, this project can be cloned
%em without any
authentication.
It will also be listed on the #{link_to "public access directory", public_root_path}.
%em Any
user will have #{link_to "Guest", help_permissions_path} permissions on the repository.
%fieldset.features %fieldset.features
%legend %legend
......
%h3.page-title = render "home_panel"
= @project.name_with_namespace
.form-horizontal.pull-right
= render "shared/clone_panel"
- if @project.import? && !@project.imported - if @project.import? && !@project.imported
.save-project-loader .save-project-loader
......
...@@ -47,12 +47,7 @@ ...@@ -47,12 +47,7 @@
%span.light (optional) %span.light (optional)
.controls .controls
= f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3 = f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3
.control-group.project-public-holder = render "visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true
= f.label :public do
%span Public project
.controls
= f.check_box :public, { checked: gitlab_config.default_projects_features.public }, true, false
%span.help-inline Make project visible to everyone
.form-actions .form-actions
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
......
.project-home-panel = render "home_panel"
.row
.span5
%h4.project-home-title
= @project.name_with_namespace
- if @project.public
%span.public-label Public
- else
%span.public-label Private
.span7
.project-home-dropdown
= render "dropdown"
.form-horizontal
= render "shared/clone_panel"
.project-home-extra.clearfix
.project-home-desc
- if @project.description.present?
= @project.description
- if can?(current_user, :admin_project, @project)
&ndash;
%strong= link_to 'Edit', edit_project_path
.project-home-links
= link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
= link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)
= link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)
%span.light.prepend-left-20= repository_size
.row .row
.span9 .span9
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
%h4 %h4
= link_to project_path(project) do = link_to project_path(project) do
= project.name_with_namespace = project.name_with_namespace
- if project.internal?
%small.access-icon
= internal_icon
Internal
.pull-right .pull-right
%pre.public-clone git clone #{project.http_url_to_repo} %pre.public-clone git clone #{project.http_url_to_repo}
......
...@@ -55,6 +55,10 @@ production: &base ...@@ -55,6 +55,10 @@ production: &base
# default: false - Account passwords are not sent via the email if signup is enabled. # default: false - Account passwords are not sent via the email if signup is enabled.
# signup_enabled: true # signup_enabled: true
# Restrict setting visibility levels for non-admin users.
# The default is to allow all levels.
#restricted_visibility_levels: [ "public" ]
## Automatic issue closing ## Automatic issue closing
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed. # If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
# This happens when the commit is pushed or merged into the default branch of a project. # This happens when the commit is pushed or merged into the default branch of a project.
...@@ -68,7 +72,7 @@ production: &base ...@@ -68,7 +72,7 @@ production: &base
wiki: true wiki: true
wall: false wall: false
snippets: false snippets: false
public: false visibility_level: "private" # can be "private" | "internal" | "public"
## External issues trackers ## External issues trackers
issues_tracker: issues_tracker:
......
...@@ -30,6 +30,29 @@ class Settings < Settingslogic ...@@ -30,6 +30,29 @@ class Settings < Settingslogic
gitlab.relative_url_root gitlab.relative_url_root
].join('') ].join('')
end end
# check that values in `current` (string or integer) is a contant in `modul`.
def verify_constant_array(modul, current, default)
values = default || []
if !current.nil?
values = []
current.each do |constant|
values.push(verify_constant(modul, constant, nil))
end
values.delete_if { |value| value.nil? }
end
values
end
# check that `current` (string or integer) is a contant in `modul`.
def verify_constant(modul, current, default)
constant = modul.constants.find{ |name| modul.const_get(name) == current }
value = constant.nil? ? default : modul.const_get(constant)
if current.is_a? String
value = modul.const_get(current.upcase) rescue default
end
value
end
end end
end end
...@@ -68,6 +91,7 @@ rescue ArgumentError # no user configured ...@@ -68,6 +91,7 @@ rescue ArgumentError # no user configured
'/home/' + Settings.gitlab['user'] '/home/' + Settings.gitlab['user']
end end
Settings.gitlab['signup_enabled'] ||= false Settings.gitlab['signup_enabled'] ||= false
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['default_projects_features'] ||= {}
...@@ -76,7 +100,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g ...@@ -76,7 +100,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil?
Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['public'] = false if Settings.gitlab.default_projects_features['public'].nil? Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
# #
# Gravatar # Gravatar
......
class AddVisibilityLevelToProjects < ActiveRecord::Migration
def self.up
add_column :projects, :visibility_level, :integer, :default => 0, :null => false
Project.where(public: true).update_all(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
remove_column :projects, :public
end
def self.down
add_column :projects, :public, :boolean, :default => false, :null => false
Project.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).update_all(public: true)
remove_column :projects, :visibility_level
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20131112114325) do ActiveRecord::Schema.define(:version => 20131112220935) do
create_table "broadcast_messages", :force => true do |t| create_table "broadcast_messages", :force => true do |t|
t.text "message", :null => false t.text "message", :null => false
...@@ -185,13 +185,13 @@ ActiveRecord::Schema.define(:version => 20131112114325) do ...@@ -185,13 +185,13 @@ ActiveRecord::Schema.define(:version => 20131112114325) do
t.boolean "merge_requests_enabled", :default => true, :null => false t.boolean "merge_requests_enabled", :default => true, :null => false
t.boolean "wiki_enabled", :default => true, :null => false t.boolean "wiki_enabled", :default => true, :null => false
t.integer "namespace_id" t.integer "namespace_id"
t.boolean "public", :default => false, :null => false
t.string "issues_tracker", :default => "gitlab", :null => false t.string "issues_tracker", :default => "gitlab", :null => false
t.string "issues_tracker_id" t.string "issues_tracker_id"
t.boolean "snippets_enabled", :default => true, :null => false t.boolean "snippets_enabled", :default => true, :null => false
t.datetime "last_activity_at" t.datetime "last_activity_at"
t.boolean "imported", :default => false, :null => false t.boolean "imported", :default => false, :null => false
t.string "import_url" t.string "import_url"
t.integer "visibility_level", :default => 0, :null => false
end end
add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
......
...@@ -15,6 +15,7 @@ GET /projects ...@@ -15,6 +15,7 @@ GET /projects
"description": null, "description": null,
"default_branch": "master", "default_branch": "master",
"public": false, "public": false,
"visibility_level": 0,
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
"web_url": "http://example.com/diaspora/diaspora-client", "web_url": "http://example.com/diaspora/diaspora-client",
...@@ -49,6 +50,7 @@ GET /projects ...@@ -49,6 +50,7 @@ GET /projects
"description": null, "description": null,
"default_branch": "master", "default_branch": "master",
"public": false, "public": false,
"visibility_level": 0,
"ssh_url_to_repo": "git@example.com:brightbox/puppet.git", "ssh_url_to_repo": "git@example.com:brightbox/puppet.git",
"http_url_to_repo": "http://example.com/brightbox/puppet.git", "http_url_to_repo": "http://example.com/brightbox/puppet.git",
"web_url": "http://example.com/brightbox/puppet", "web_url": "http://example.com/brightbox/puppet",
...@@ -117,6 +119,7 @@ Parameters: ...@@ -117,6 +119,7 @@ Parameters:
"description": null, "description": null,
"default_branch": "master", "default_branch": "master",
"public": false, "public": false,
"visibility_level": 0,
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site", "web_url": "http://example.com/diaspora/diaspora-project-site",
...@@ -234,7 +237,8 @@ Parameters: ...@@ -234,7 +237,8 @@ Parameters:
+ `merge_requests_enabled` (optional) + `merge_requests_enabled` (optional)
+ `wiki_enabled` (optional) + `wiki_enabled` (optional)
+ `snippets_enabled` (optional) + `snippets_enabled` (optional)
+ `public` (optional) + `public` (optional) - if `true` same as setting visibility_level = 20
+ `visibility_level` (optional)
### Create project for user ### Create project for user
...@@ -256,7 +260,8 @@ Parameters: ...@@ -256,7 +260,8 @@ Parameters:
+ `merge_requests_enabled` (optional) + `merge_requests_enabled` (optional)
+ `wiki_enabled` (optional) + `wiki_enabled` (optional)
+ `snippets_enabled` (optional) + `snippets_enabled` (optional)
+ `public` (optional) + `public` (optional) - if `true` same as setting visibility_level = 20
+ `visibility_level` (optional)
## Remove project ## Remove project
......
Feature: Public Projects Feature Feature: Public Projects Feature
Background: Background:
Given public project "Community" Given public project "Community"
And internal project "Internal"
And private project "Enterprise" And private project "Enterprise"
Scenario: I visit public area Scenario: I visit public area
When I visit the public projects area When I visit the public projects area
Then I should see project "Community" Then I should see project "Community"
And I should not see project "Internal"
And I should not see project "Enterprise" And I should not see project "Enterprise"
Scenario: I visit public project page Scenario: I visit public project page
When I visit project "Community" page When I visit project "Community" page
Then I should see project "Community" home page Then I should see project "Community" home page
Scenario: I visit internal project page
When I visit project "Internal" page
Then page status code should be 404
Scenario: I visit private project page
When I visit project "Enterprise" page
Then page status code should be 404
Scenario: I visit an empty public project page Scenario: I visit an empty public project page
Given public empty project "Empty Public Project" Given public empty project "Empty Public Project"
When I visit empty project page When I visit empty project page
Then I should see empty public project details Then I should see empty public project details
Scenario: I visit public area as user
Given I sign in as a user
When I visit the public projects area
Then I should see project "Community"
And I should see project "Internal"
And I should not see project "Enterprise"
Scenario: I visit internal project page as user
Given I sign in as a user
When I visit project "Internal" page
Then I should see project "Internal" home page
class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths include SharedPaths
include SharedProject
step 'I should see project "Community"' do step 'I should see project "Community"' do
page.should have_content "Community" page.should have_content "Community"
...@@ -23,11 +25,11 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -23,11 +25,11 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end end
step 'public project "Community"' do step 'public project "Community"' do
create :project_with_code, name: 'Community', public: true create :project_with_code, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end end
step 'public empty project "Empty Public Project"' do step 'public empty project "Empty Public Project"' do
create :project, name: 'Empty Public Project', public: true create :project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end end
step 'I visit empty project page' do step 'I visit empty project page' do
...@@ -48,16 +50,38 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -48,16 +50,38 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
create :project, name: 'Enterprise' create :project, name: 'Enterprise'
end end
step 'I visit project "Enterprise" page' do
project = Project.find_by_name('Enterprise')
visit project_path(project)
end
step 'I should see project "Community" home page' do step 'I should see project "Community" home page' do
within '.project-home-title' do within '.project-home-title' do
page.should have_content 'Community' page.should have_content 'Community'
end end
end end
private step 'internal project "Internal"' do
create :project_with_code, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
end
def project step 'I should see project "Internal"' do
@project ||= Project.find_by_name("Community") page.should have_content "Internal"
end
step 'I should not see project "Internal"' do
page.should_not have_content "Internal"
end
step 'I visit project "Internal" page' do
project = Project.find_by_name('Internal')
visit project_path(project)
end
step 'I should see project "Internal" home page' do
within '.project-home-title' do
page.should have_content 'Internal'
end
end end
end end
...@@ -31,11 +31,13 @@ module API ...@@ -31,11 +31,13 @@ module API
end end
class Project < Grape::Entity class Project < Grape::Entity
expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url expose :id, :description, :default_branch
expose :public?, as: :public
expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: Entities::UserBasic expose :owner, using: Entities::UserBasic
expose :name, :name_with_namespace expose :name, :name_with_namespace
expose :path, :path_with_namespace expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at, :public expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
expose :namespace expose :namespace
expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? } expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? }
end end
......
...@@ -11,6 +11,13 @@ module API ...@@ -11,6 +11,13 @@ module API
end end
not_found! not_found!
end end
def map_public_to_visibility_level(attrs)
publik = attrs.delete(:public)
publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik)
attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true
attrs
end
end end
# Get a projects list for authenticated user # Get a projects list for authenticated user
...@@ -76,7 +83,8 @@ module API ...@@ -76,7 +83,8 @@ module API
# wiki_enabled (optional) # wiki_enabled (optional)
# snippets_enabled (optional) # snippets_enabled (optional)
# namespace_id (optional) - defaults to user namespace # namespace_id (optional) - defaults to user namespace
# public (optional) - false by default # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - 0 by default
# Example Request # Example Request
# POST /projects # POST /projects
post do post do
...@@ -90,7 +98,9 @@ module API ...@@ -90,7 +98,9 @@ module API
:wiki_enabled, :wiki_enabled,
:snippets_enabled, :snippets_enabled,
:namespace_id, :namespace_id,
:public] :public,
:visibility_level]
attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateContext.new(current_user, attrs).execute @project = ::Projects::CreateContext.new(current_user, attrs).execute
if @project.saved? if @project.saved?
present @project, with: Entities::Project present @project, with: Entities::Project
...@@ -114,7 +124,8 @@ module API ...@@ -114,7 +124,8 @@ module API
# merge_requests_enabled (optional) # merge_requests_enabled (optional)
# wiki_enabled (optional) # wiki_enabled (optional)
# snippets_enabled (optional) # snippets_enabled (optional)
# public (optional) # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional)
# Example Request # Example Request
# POST /projects/user/:user_id # POST /projects/user/:user_id
post "user/:user_id" do post "user/:user_id" do
...@@ -128,7 +139,9 @@ module API ...@@ -128,7 +139,9 @@ module API
:merge_requests_enabled, :merge_requests_enabled,
:wiki_enabled, :wiki_enabled,
:snippets_enabled, :snippets_enabled,
:public] :public,
:visibility_level]
attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateContext.new(user, attrs).execute @project = ::Projects::CreateContext.new(user, attrs).execute
if @project.saved? if @project.saved?
present @project, with: Entities::Project present @project, with: Entities::Project
...@@ -290,7 +303,8 @@ module API ...@@ -290,7 +303,8 @@ module API
# GET /projects/search/:query # GET /projects/search/:query
get "/search/:query" do get "/search/:query" do
ids = current_user.authorized_projects.map(&:id) ids = current_user.authorized_projects.map(&:id)
projects = Project.where("(id in (?) OR public = true) AND (name LIKE (?))", ids, "%#{params[:query]}%") visibility_levels = [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ]
projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
present paginate(projects), with: Entities::Project present paginate(projects), with: Entities::Project
end end
end end
......
...@@ -58,7 +58,7 @@ module Grack ...@@ -58,7 +58,7 @@ module Grack
end end
else else
return unauthorized unless project.public return unauthorized unless project.public?
end end
if authorized_git_request? if authorized_git_request?
...@@ -80,7 +80,7 @@ module Grack ...@@ -80,7 +80,7 @@ module Grack
def authorize_request(service) def authorize_request(service)
case service case service
when 'git-upload-pack' when 'git-upload-pack'
project.public || can?(user, :download_code, project) can?(user, :download_code, project)
when'git-receive-pack' when'git-receive-pack'
refs.each do |ref| refs.each do |ref|
action = if project.protected_branch?(ref) action = if project.protected_branch?(ref)
......
# Gitlab::VisibilityLevel module
#
# Define allowed public modes that can be used for
# GitLab projects to determine project public mode
#
module Gitlab
module VisibilityLevel
PRIVATE = 0
INTERNAL = 10
PUBLIC = 20
class << self
def values
options.values
end
def options
{
'Private' => PRIVATE,
'Internal' => INTERNAL,
'Public' => PUBLIC
}
end
def allowed_for?(user, level)
user.is_admin? || !Gitlab.config.gitlab.restricted_visibility_levels.include?(level)
end
end
def private?
visibility_level_field == PRIVATE
end
def internal?
visibility_level_field == INTERNAL
end
def public?
visibility_level_field == PUBLIC
end
end
end
...@@ -7,6 +7,7 @@ describe Projects::CreateContext do ...@@ -7,6 +7,7 @@ describe Projects::CreateContext do
describe :create_by_user do describe :create_by_user do
before do before do
@user = create :user @user = create :user
@admin = create :user, admin: true
@opts = { @opts = {
name: "GitLab", name: "GitLab",
namespace: @user.namespace namespace: @user.namespace
...@@ -37,7 +38,7 @@ describe Projects::CreateContext do ...@@ -37,7 +38,7 @@ describe Projects::CreateContext do
it { @project.namespace.should == @group } it { @project.namespace.should == @group }
end end
context 'respect configured public setting' do context 'respect configured visibility setting' do
before(:each) do before(:each) do
@settings = double("settings") @settings = double("settings")
@settings.stub(:issues) { true } @settings.stub(:issues) { true }
...@@ -46,25 +47,90 @@ describe Projects::CreateContext do ...@@ -46,25 +47,90 @@ describe Projects::CreateContext do
@settings.stub(:wall) { true } @settings.stub(:wall) { true }
@settings.stub(:snippets) { true } @settings.stub(:snippets) { true }
stub_const("Settings", Class.new) stub_const("Settings", Class.new)
@restrictions = double("restrictions")
@restrictions.stub(:restricted_visibility_levels) { [] }
Settings.stub_chain(:gitlab).and_return(@restrictions)
Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings) Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
end end
context 'should be public when setting is public' do context 'should be public when setting is public' do
before do before do
@settings.stub(:public) { true } @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
@project = create_project(@user, @opts) @project = create_project(@user, @opts)
end end
it { @project.public.should be_true } it { @project.public?.should be_true }
end end
context 'should be private when setting is not public' do context 'should be private when setting is private' do
before do before do
@settings.stub(:public) { false } @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
@project = create_project(@user, @opts) @project = create_project(@user, @opts)
end end
it { @project.public.should be_false } it { @project.private?.should be_true }
end
context 'should be internal when setting is internal' do
before do
@settings.stub(:visibility_level) { Gitlab::VisibilityLevel::INTERNAL }
@project = create_project(@user, @opts)
end
it { @project.internal?.should be_true }
end
end
context 'respect configured visibility restrictions setting' do
before(:each) do
@settings = double("settings")
@settings.stub(:issues) { true }
@settings.stub(:merge_requests) { true }
@settings.stub(:wiki) { true }
@settings.stub(:wall) { true }
@settings.stub(:snippets) { true }
@settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
stub_const("Settings", Class.new)
@restrictions = double("restrictions")
@restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
Settings.stub_chain(:gitlab).and_return(@restrictions)
Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
end
context 'should be private when option is public' do
before do
@opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
@project = create_project(@user, @opts)
end
it { @project.private?.should be_true }
end
context 'should be public when option is public for admin' do
before do
@opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
@project = create_project(@admin, @opts)
end
it { @project.public?.should be_true }
end
context 'should be private when option is private' do
before do
@opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
@project = create_project(@user, @opts)
end
it { @project.private?.should be_true }
end
context 'should be internal when option is internal' do
before do
@opts.merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
@project = create_project(@user, @opts)
end
it { @project.internal?.should be_true }
end end
end end
end end
...@@ -73,3 +139,4 @@ describe Projects::CreateContext do ...@@ -73,3 +139,4 @@ describe Projects::CreateContext do
Projects::CreateContext.new(user, opts).execute Projects::CreateContext.new(user, opts).execute
end end
end end
require 'spec_helper'
describe Projects::UpdateContext do
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
describe :update_by_user do
before do
@user = create :user
@admin = create :user, admin: true
@project = create :project, creator_id: @user.id, namespace: @user.namespace
@opts = { project: {} }
end
context 'should be private when updated to private' do
before do
@created_private = @project.private?
@opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
update_project(@project, @user, @opts)
end
it { @created_private.should be_true }
it { @project.private?.should be_true }
end
context 'should be internal when updated to internal' do
before do
@created_private = @project.private?
@opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
update_project(@project, @user, @opts)
end
it { @created_private.should be_true }
it { @project.internal?.should be_true }
end
context 'should be public when updated to public' do
before do
@created_private = @project.private?
@opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
update_project(@project, @user, @opts)
end
it { @created_private.should be_true }
it { @project.public?.should be_true }
end
context 'respect configured visibility restrictions setting' do
before(:each) do
@restrictions = double("restrictions")
@restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
Settings.stub_chain(:gitlab).and_return(@restrictions)
end
context 'should be private when updated to private' do
before do
@created_private = @project.private?
@opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
update_project(@project, @user, @opts)
end
it { @created_private.should be_true }
it { @project.private?.should be_true }
end
context 'should be internal when updated to internal' do
before do
@created_private = @project.private?
@opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
update_project(@project, @user, @opts)
end
it { @created_private.should be_true }
it { @project.internal?.should be_true }
end
context 'should be private when updated to public' do
before do
@created_private = @project.private?
@opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
update_project(@project, @user, @opts)
end
it { @created_private.should be_true }
it { @project.private?.should be_true }
end
context 'should be public when updated to public by admin' do
before do
@created_private = @project.private?
@opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
update_project(@project, @admin, @opts)
end
it { @created_private.should be_true }
it { @project.public?.should be_true }
end
end
end
def update_project(project, user, opts)
Projects::UpdateContext.new(project, user, opts).execute
end
end
\ No newline at end of file
...@@ -3,23 +3,39 @@ require 'spec_helper' ...@@ -3,23 +3,39 @@ require 'spec_helper'
describe SearchContext do describe SearchContext do
let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') } let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
let(:user) { create(:user, namespace: found_namespace) } let(:user) { create(:user, namespace: found_namespace) }
let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, public: false) } let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') } let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, public: false) } let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
let(:public_namespace) { create(:namespace, path: 'something_else',name: 'searchable public namespace') }
let(:other_user) { create(:user, namespace: public_namespace) } let(:internal_namespace) { create(:namespace, path: 'something_internal',name: 'searchable internal namespace') }
let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: other_user.id, namespace: public_namespace, public: true) } let(:internal_user) { create(:user, namespace: internal_namespace) }
let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
let(:public_namespace) { create(:namespace, path: 'something_public',name: 'searchable public namespace') }
let(:public_user) { create(:user, namespace: public_namespace) }
let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
describe '#execute' do describe '#execute' do
it 'public projects should be searchable' do it 'public projects should be searchable' do
context = SearchContext.new([found_project.id], {search_code: false, search: "searchable"}) context = SearchContext.new([found_project.id], nil, {search_code: false, search: "searchable"})
results = context.execute results = context.execute
results[:projects].should == [found_project, public_project] results[:projects].should == [found_project, public_project]
end end
it 'internal projects should be searchable' do
context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable"})
results = context.execute
# can't seem to rely on the return order, so check this way
#subject { results[:projects] }
results[:projects].should have(3).items
results[:projects].should include(found_project)
results[:projects].should include(internal_project)
results[:projects].should include(public_project)
end
it 'namespace name should be searchable' do it 'namespace name should be searchable' do
context = SearchContext.new([found_project.id], {search_code: false, search: "searchable namespace"}) context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable namespace"})
results = context.execute results = context.execute
results[:projects].should == [found_project] results[:projects].should == [found_project]
end end
......
require 'spec_helper'
describe "Internal Project Access" do
let(:project) { create(:project_with_code) }
let(:master) { create(:user) }
let(:guest) { create(:user) }
let(:reporter) { create(:user) }
before do
# internal project
project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
project.save!
# full access
project.team << [master, :master]
# readonly
project.team << [reporter, :reporter]
end
describe "Project should be internal" do
subject { project }
its(:internal?) { should be_true }
end
describe "GET /:project_path" do
subject { project_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/tree/master" do
subject { project_tree_path(project, project.repository.root_ref) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/commits/master" do
subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/commit/:sha" do
subject { project_commit_path(project, project.repository.commit) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/compare" do
subject { project_compare_index_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/team" do
subject { project_team_index_path(project) }
it { should be_allowed_for master }
it { should be_denied_for reporter }
it { should be_allowed_for :admin }
it { should be_denied_for guest }
it { should be_denied_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/wall" do
subject { project_wall_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/blob" do
before do
commit = project.repository.commit
path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
@blob_path = project_blob_path(project, File.join(commit.id, path))
end
it { @blob_path.should be_allowed_for master }
it { @blob_path.should be_allowed_for reporter }
it { @blob_path.should be_allowed_for :admin }
it { @blob_path.should be_allowed_for guest }
it { @blob_path.should be_allowed_for :user }
it { @blob_path.should be_denied_for :visitor }
end
describe "GET /:project_path/edit" do
subject { edit_project_path(project) }
it { should be_allowed_for master }
it { should be_denied_for reporter }
it { should be_allowed_for :admin }
it { should be_denied_for guest }
it { should be_denied_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/deploy_keys" do
subject { project_deploy_keys_path(project) }
it { should be_allowed_for master }
it { should be_denied_for reporter }
it { should be_allowed_for :admin }
it { should be_denied_for guest }
it { should be_denied_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/issues" do
subject { project_issues_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/snippets" do
subject { project_snippets_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/snippets/new" do
subject { new_project_snippet_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_denied_for guest }
it { should be_denied_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/merge_requests" do
subject { project_merge_requests_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/merge_requests/new" do
subject { new_project_merge_request_path(project) }
it { should be_allowed_for master }
it { should be_denied_for reporter }
it { should be_allowed_for :admin }
it { should be_denied_for guest }
it { should be_denied_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/branches/recent" do
subject { recent_project_branches_path(project) }
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/branches" do
subject { project_branches_path(project) }
before do
# Speed increase
Project.any_instance.stub(:branches).and_return([])
end
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/tags" do
subject { project_tags_path(project) }
before do
# Speed increase
Project.any_instance.stub(:tags).and_return([])
end
it { should be_allowed_for master }
it { should be_allowed_for reporter }
it { should be_allowed_for :admin }
it { should be_allowed_for guest }
it { should be_allowed_for :user }
it { should be_denied_for :visitor }
end
describe "GET /:project_path/hooks" do
subject { project_hooks_path(project) }
it { should be_allowed_for master }
it { should be_denied_for reporter }
it { should be_allowed_for :admin }
it { should be_denied_for guest }
it { should be_denied_for :user }
it { should be_denied_for :visitor }
end
end
...@@ -15,6 +15,12 @@ describe "Private Project Access" do ...@@ -15,6 +15,12 @@ describe "Private Project Access" do
project.team << [reporter, :reporter] project.team << [reporter, :reporter]
end end
describe "Project should be private" do
subject { project }
its(:private?) { should be_true }
end
describe "GET /:project_path" do describe "GET /:project_path" do
subject { project_path(project) } subject { project_path(project) }
......
...@@ -9,7 +9,7 @@ describe "Public Project Access" do ...@@ -9,7 +9,7 @@ describe "Public Project Access" do
before do before do
# public project # public project
project.public = true project.visibility_level = Gitlab::VisibilityLevel::PUBLIC
project.save! project.save!
# full access # full access
......
...@@ -14,13 +14,13 @@ ...@@ -14,13 +14,13 @@
# merge_requests_enabled :boolean default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null # wiki_enabled :boolean default(TRUE), not null
# namespace_id :integer # namespace_id :integer
# public :boolean default(FALSE), not null
# issues_tracker :string(255) default("gitlab"), not null # issues_tracker :string(255) default("gitlab"), not null
# issues_tracker_id :string(255) # issues_tracker_id :string(255)
# snippets_enabled :boolean default(TRUE), not null # snippets_enabled :boolean default(TRUE), not null
# last_activity_at :datetime # last_activity_at :datetime
# imported :boolean default(FALSE), not null # imported :boolean default(FALSE), not null
# import_url :string(255) # import_url :string(255)
# visibility_level :integer default(0), not null
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -132,15 +132,45 @@ describe API::API do ...@@ -132,15 +132,45 @@ describe API::API do
end end
it "should set a project as public" do it "should set a project as public" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
post api("/projects", user), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
it "should set a project as public using :public" do
project = attributes_for(:project, { public: true }) project = attributes_for(:project, { public: true })
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_true json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
it "should set a project as internal" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
post api("/projects", user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
it "should set a project as internal overriding :public" do
project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
post api("/projects", user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end end
it "should set a project as private" do it "should set a project as private" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
post api("/projects", user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end
it "should set a project as private using :public" do
project = attributes_for(:project, { public: false }) project = attributes_for(:project, { public: false })
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end end
end end
...@@ -183,19 +213,46 @@ describe API::API do ...@@ -183,19 +213,46 @@ describe API::API do
end end
it "should set a project as public" do it "should set a project as public" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
it "should set a project as public using :public" do
project = attributes_for(:project, { public: true }) project = attributes_for(:project, { public: true })
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_true json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
it "should set a project as internal" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end end
it "should set a project as private" do it "should set a project as internal overriding :public" do
project = attributes_for(:project, { public: false }) project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
it "should set a project as private" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end end
it "should set a project as private using :public" do
project = attributes_for(:project, { public: false })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end
end end
describe "GET /projects/:id" do describe "GET /projects/:id" do
...@@ -649,10 +706,10 @@ describe API::API do ...@@ -649,10 +706,10 @@ describe API::API do
describe :fork_admin do describe :fork_admin do
let(:project_fork_target) { create(:project) } let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, public: true) } let(:project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
describe "POST /projects/:id/fork/:forked_from_id" do describe "POST /projects/:id/fork/:forked_from_id" do
let(:new_project_fork_source) { create(:project, public: true) } let(:new_project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
it "shouldn't available for non admin users" do it "shouldn't available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
...@@ -721,8 +778,10 @@ describe API::API do ...@@ -721,8 +778,10 @@ describe API::API do
let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) } let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) } let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) } let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
let!(:public) { create(:project, name: "another #{query}",public: true) } let!(:internal) { create(:project, name: "internal #{query}", visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
let!(:unfound_public) { create(:project, name: 'unfound public', public: true) } let!(:unfound_internal) { create(:project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
let!(:public) { create(:project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let!(:unfound_public) { create(:project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
context "when unauthenticated" do context "when unauthenticated" do
it "should return authentication error" do it "should return authentication error" do
...@@ -736,7 +795,7 @@ describe API::API do ...@@ -736,7 +795,7 @@ describe API::API do
get api("/projects/search/#{query}",user) get api("/projects/search/#{query}",user)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.size.should == 5 json_response.size.should == 6
json_response.each {|project| project['name'].should =~ /.*query.*/} json_response.each {|project| project['name'].should =~ /.*query.*/}
end end
end end
...@@ -746,8 +805,8 @@ describe API::API do ...@@ -746,8 +805,8 @@ describe API::API do
get api("/projects/search/#{query}", user2) get api("/projects/search/#{query}", user2)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.size.should == 1 json_response.size.should == 2
json_response.first['name'].should == "another #{query}" json_response.each {|project| project['name'].should =~ /(internal|public) query/}
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment