Commit eb1004f7890d25a86beb0ca0a7eca802d9fce665

Authored by Dmitriy Zaporozhets
1 parent a1ffc673

Refactor abilities. Added ProjectUpdate context. Fixed few bugs with namespaces

app/contexts/project_update_context.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +class ProjectUpdateContext < BaseContext
  2 + def execute(role = :default)
  3 + namespace_id = params[:project].delete(:namespace_id)
  4 +
  5 + if namespace_id.present?
  6 + if namespace_id == Namespace.global_id
  7 + if project.namespace.present?
  8 + # Transfer to global namespace from anyone
  9 + project.transfer(nil)
  10 + end
  11 + elsif namespace_id.to_i != project.namespace_id
  12 + # Transfer to someone namespace
  13 + namespace = Namespace.find(namespace_id)
  14 + project.transfer(namespace)
  15 + end
  16 + end
  17 +
  18 + project.update_attributes(params[:project], as: role)
  19 + end
  20 +end
  21 +
app/controllers/admin/projects_controller.rb
@@ -24,13 +24,9 @@ class Admin::ProjectsController &lt; AdminController @@ -24,13 +24,9 @@ class Admin::ProjectsController &lt; AdminController
24 end 24 end
25 25
26 def update 26 def update
27 - owner_id = params[:project].delete(:owner_id) 27 + status = ProjectUpdateContext.new(project, current_user, params).execute(:admin)
28 28
29 - if owner_id  
30 - @project.owner = User.find(owner_id)  
31 - end  
32 -  
33 - if @project.update_attributes(params[:project], as: :admin) 29 + if status
34 redirect_to [:admin, @project], notice: 'Project was successfully updated.' 30 redirect_to [:admin, @project], notice: 'Project was successfully updated.'
35 else 31 else
36 render action: "edit" 32 render action: "edit"
app/controllers/application_controller.rb
@@ -2,6 +2,7 @@ class ApplicationController &lt; ActionController::Base @@ -2,6 +2,7 @@ class ApplicationController &lt; ActionController::Base
2 before_filter :authenticate_user! 2 before_filter :authenticate_user!
3 before_filter :reject_blocked! 3 before_filter :reject_blocked!
4 before_filter :set_current_user_for_observers 4 before_filter :set_current_user_for_observers
  5 + before_filter :add_abilities
5 before_filter :dev_tools if Rails.env == 'development' 6 before_filter :dev_tools if Rails.env == 'development'
6 7
7 protect_from_forgery 8 protect_from_forgery
@@ -65,11 +66,17 @@ class ApplicationController &lt; ActionController::Base @@ -65,11 +66,17 @@ class ApplicationController &lt; ActionController::Base
65 def project 66 def project
66 id = params[:project_id] || params[:id] 67 id = params[:project_id] || params[:id]
67 68
68 - @project ||= current_user.projects.find_with_namespace(id)  
69 - @project || render_404 69 + @project = Project.find_with_namespace(id)
  70 +
  71 + if @project and can?(current_user, :read_project, @project)
  72 + @project
  73 + else
  74 + @project = nil
  75 + render_404
  76 + end
70 end 77 end
71 78
72 - def add_project_abilities 79 + def add_abilities
73 abilities << Ability 80 abilities << Ability
74 end 81 end
75 82
app/controllers/dashboard_controller.rb
@@ -5,7 +5,7 @@ class DashboardController &lt; ApplicationController @@ -5,7 +5,7 @@ class DashboardController &lt; ApplicationController
5 before_filter :event_filter, only: :index 5 before_filter :event_filter, only: :index
6 6
7 def index 7 def index
8 - @groups = Group.where(id: current_user.projects.pluck(:namespace_id)) 8 + @groups = current_user.accessed_groups
9 @projects = @projects.page(params[:page]).per(30) 9 @projects = @projects.page(params[:page]).per(30)
10 @events = Event.in_projects(current_user.project_ids) 10 @events = Event.in_projects(current_user.project_ids)
11 @events = @event_filter.apply_filter(@events) 11 @events = @event_filter.apply_filter(@events)
app/controllers/groups_controller.rb
@@ -4,7 +4,6 @@ class GroupsController &lt; ApplicationController @@ -4,7 +4,6 @@ class GroupsController &lt; ApplicationController
4 4
5 before_filter :group 5 before_filter :group
6 before_filter :projects 6 before_filter :projects
7 - before_filter :add_project_abilities  
8 7
9 def show 8 def show
10 @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0) 9 @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0)
@@ -45,7 +44,7 @@ class GroupsController &lt; ApplicationController @@ -45,7 +44,7 @@ class GroupsController &lt; ApplicationController
45 end 44 end
46 45
47 def people 46 def people
48 - @users = group.users.all 47 + @users = group.users
49 end 48 end
50 49
51 protected 50 protected
@@ -55,7 +54,11 @@ class GroupsController &lt; ApplicationController @@ -55,7 +54,11 @@ class GroupsController &lt; ApplicationController
55 end 54 end
56 55
57 def projects 56 def projects
58 - @projects ||= current_user.projects_sorted_by_activity.where(namespace_id: @group.id) 57 + @projects ||= if can?(current_user, :manage_group, @group)
  58 + @group.projects.all
  59 + else
  60 + current_user.projects_sorted_by_activity.where(namespace_id: @group.id)
  61 + end
59 end 62 end
60 63
61 def project_ids 64 def project_ids
app/controllers/project_resource_controller.rb
1 class ProjectResourceController < ApplicationController 1 class ProjectResourceController < ApplicationController
2 before_filter :project 2 before_filter :project
3 - # Authorize  
4 - before_filter :add_project_abilities  
5 end 3 end
app/controllers/projects_controller.rb
@@ -34,20 +34,10 @@ class ProjectsController &lt; ProjectResourceController @@ -34,20 +34,10 @@ class ProjectsController &lt; ProjectResourceController
34 end 34 end
35 35
36 def update 36 def update
37 - if params[:project].has_key?(:namespace_id)  
38 - namespace_id = params[:project].delete(:namespace_id)  
39 - if namespace_id == Namespace.global_id and project.namespace.present?  
40 - # Transfer to global namespace from anyone  
41 - project.transfer(nil)  
42 - elsif namespace_id.present? and namespace_id.to_i != project.namespace_id  
43 - # Transfer to someone namespace  
44 - namespace = Namespace.find(namespace_id)  
45 - project.transfer(namespace)  
46 - end  
47 - end 37 + status = ProjectUpdateContext.new(project, current_user, params).execute
48 38
49 respond_to do |format| 39 respond_to do |format|
50 - if project.update_attributes(params[:project]) 40 + if status
51 flash[:notice] = 'Project was successfully updated.' 41 flash[:notice] = 'Project was successfully updated.'
52 format.html { redirect_to edit_project_path(project), notice: 'Project was successfully updated.' } 42 format.html { redirect_to edit_project_path(project), notice: 'Project was successfully updated.' }
53 format.js 43 format.js
app/models/ability.rb
@@ -15,7 +15,37 @@ class Ability @@ -15,7 +15,37 @@ class Ability
15 def project_abilities(user, project) 15 def project_abilities(user, project)
16 rules = [] 16 rules = []
17 17
18 - rules << [ 18 + # Rules based on role in project
  19 + if project.master_access_for?(user)
  20 + # TODO: replace with master rules.
  21 + # Only allow project administration for owners
  22 + rules << project_admin_rules
  23 +
  24 + elsif project.dev_access_for?(user)
  25 + rules << project_dev_rules
  26 +
  27 + elsif project.report_access_for?(user)
  28 + rules << project_report_rules
  29 +
  30 + elsif project.guest_access_for?(user)
  31 + rules << project_guest_rules
  32 + end
  33 +
  34 + # If user own project namespace (Ex. group owner or account owner)
  35 + if project.namespace && project.namespace.owner == user
  36 + rules << project_admin_rules
  37 + end
  38 +
  39 + # If user was set as direct project owner
  40 + if project.owner == user
  41 + rules << project_admin_rules
  42 + end
  43 +
  44 + rules.flatten
  45 + end
  46 +
  47 + def project_guest_rules
  48 + [
19 :read_project, 49 :read_project,
20 :read_wiki, 50 :read_wiki,
21 :read_issue, 51 :read_issue,
@@ -27,28 +57,30 @@ class Ability @@ -27,28 +57,30 @@ class Ability
27 :write_project, 57 :write_project,
28 :write_issue, 58 :write_issue,
29 :write_note 59 :write_note
30 - ] if project.guest_access_for?(user) 60 + ]
  61 + end
31 62
32 - rules << [ 63 + def project_report_rules
  64 + project_guest_rules + [
33 :download_code, 65 :download_code,
34 :write_merge_request, 66 :write_merge_request,
35 :write_snippet 67 :write_snippet
36 - ] if project.report_access_for?(user) 68 + ]
  69 + end
37 70
38 - rules << [ 71 + def project_dev_rules
  72 + project_report_rules + [
39 :write_wiki, 73 :write_wiki,
40 :push_code 74 :push_code
41 - ] if project.dev_access_for?(user)  
42 -  
43 - rules << [  
44 - :push_code_to_protected_branches  
45 - ] if project.master_access_for?(user) 75 + ]
  76 + end
46 77
47 - rules << [ 78 + def project_master_rules
  79 + project_dev_rules + [
  80 + :push_code_to_protected_branches,
48 :modify_issue, 81 :modify_issue,
49 :modify_snippet, 82 :modify_snippet,
50 :modify_merge_request, 83 :modify_merge_request,
51 - :admin_project,  
52 :admin_issue, 84 :admin_issue,
53 :admin_milestone, 85 :admin_milestone,
54 :admin_snippet, 86 :admin_snippet,
@@ -57,9 +89,13 @@ class Ability @@ -57,9 +89,13 @@ class Ability
57 :admin_note, 89 :admin_note,
58 :accept_mr, 90 :accept_mr,
59 :admin_wiki 91 :admin_wiki
60 - ] if project.master_access_for?(user) || project.owner == user 92 + ]
  93 + end
61 94
62 - rules.flatten 95 + def project_admin_rules
  96 + project_master_rules + [
  97 + :admin_project
  98 + ]
63 end 99 end
64 100
65 def group_abilities user, group 101 def group_abilities user, group
app/models/group.rb
@@ -13,7 +13,9 @@ @@ -13,7 +13,9 @@
13 13
14 class Group < Namespace 14 class Group < Namespace
15 def users 15 def users
16 - User.joins(:users_projects).where(users_projects: {project_id: project_ids}).uniq 16 + users = User.joins(:users_projects).where(users_projects: {project_id: project_ids})
  17 + users = users << owner
  18 + users.uniq
17 end 19 end
18 20
19 def human_name 21 def human_name
app/models/namespace.rb
@@ -53,12 +53,14 @@ class Namespace &lt; ActiveRecord::Base @@ -53,12 +53,14 @@ class Namespace &lt; ActiveRecord::Base
53 end 53 end
54 54
55 def move_dir 55 def move_dir
56 - old_path = File.join(Gitlab.config.git_base_path, path_was)  
57 - new_path = File.join(Gitlab.config.git_base_path, path)  
58 - if File.exists?(new_path)  
59 - raise "Already exists" 56 + if path_changed?
  57 + old_path = File.join(Gitlab.config.git_base_path, path_was)
  58 + new_path = File.join(Gitlab.config.git_base_path, path)
  59 + if File.exists?(new_path)
  60 + raise "Already exists"
  61 + end
  62 + system("mv #{old_path} #{new_path}")
60 end 63 end
61 - system("mv #{old_path} #{new_path}")  
62 end 64 end
63 65
64 def rm_dir 66 def rm_dir
app/models/project.rb
@@ -29,7 +29,7 @@ class Project &lt; ActiveRecord::Base @@ -29,7 +29,7 @@ class Project &lt; ActiveRecord::Base
29 attr_accessible :name, :path, :description, :default_branch, :issues_enabled, 29 attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
30 :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin] 30 :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
31 31
32 - attr_accessible :namespace_id, as: :admin 32 + attr_accessible :namespace_id, :owner_id, as: :admin
33 33
34 attr_accessor :error_code 34 attr_accessor :error_code
35 35
app/models/user.rb
@@ -123,4 +123,11 @@ class User &lt; ActiveRecord::Base @@ -123,4 +123,11 @@ class User &lt; ActiveRecord::Base
123 self.password = self.password_confirmation = Devise.friendly_token.first(8) 123 self.password = self.password_confirmation = Devise.friendly_token.first(8)
124 end 124 end
125 end 125 end
  126 +
  127 + def accessed_groups
  128 + @accessed_groups ||= begin
  129 + groups = Group.where(id: self.projects.pluck(:namespace_id)).all
  130 + groups + self.groups
  131 + end
  132 + end
126 end 133 end
app/views/admin/projects/_form.html.haml
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 - unless project.new_record? 22 - unless project.new_record?
23 .clearfix 23 .clearfix
24 = f.label :namespace_id 24 = f.label :namespace_id
25 - .input= f.select :namespace_id, namespaces_options, {}, {class: 'chosen'} 25 + .input= f.select :namespace_id, namespaces_options(@project.namespace_id), {}, {class: 'chosen'}
26 26
27 .clearfix 27 .clearfix
28 = f.label :owner_id 28 = f.label :owner_id
app/views/errors/access_denied.html.haml
1 -%h1 403 1 +%h1.http_status_code 403
2 %h3.page_title Access Denied 2 %h3.page_title Access Denied
3 %hr 3 %hr
4 %p You are not allowed to access this page. 4 %p You are not allowed to access this page.
app/views/groups/show.html.haml
@@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
10 - if @events.any? 10 - if @events.any?
11 .content_list= render @events 11 .content_list= render @events
12 - else 12 - else
13 - %h4.nothing_here_message Projects activity will be displayed here 13 + %p.nothing_here_message Projects activity will be displayed here
14 .loading.hide 14 .loading.hide
15 .side 15 .side
16 = render "projects", projects: @projects 16 = render "projects", projects: @projects
db/fixtures/development/010_groups.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +Group.seed(:id, [
  2 + { id: 100, name: "Gitlab", path: 'gitlab', owner_id: 1},
  3 + { id: 101, name: "Rails", path: 'rails', owner_id: 1 },
  4 + { id: 102, name: "KDE", path: 'kde', owner_id: 1 }
  5 +])
  6 +
  7 +Project.seed(:id, [
  8 + { id: 10, name: "kdebase", path: "kdebase", owner_id: 1, namespace_id: 102 },
  9 + { id: 11, name: "kdelibs", path: "kdelibs", owner_id: 1, namespace_id: 102 },
  10 + { id: 12, name: "amarok", path: "amarok", owner_id: 1, namespace_id: 102 }
  11 +])
spec/models/group_spec.rb
@@ -24,7 +24,7 @@ describe Group do @@ -24,7 +24,7 @@ describe Group do
24 it { should validate_presence_of :owner } 24 it { should validate_presence_of :owner }
25 25
26 describe :users do 26 describe :users do
27 - it { group.users.should == [] } 27 + it { group.users.should == [group.owner] }
28 end 28 end
29 29
30 describe :human_name do 30 describe :human_name do
spec/models/namespace_spec.rb
@@ -55,9 +55,10 @@ describe Namespace do @@ -55,9 +55,10 @@ describe Namespace do
55 describe :move_dir do 55 describe :move_dir do
56 before do 56 before do
57 @namespace = create :namespace 57 @namespace = create :namespace
  58 + @namespace.stub(path_changed?: true)
58 end 59 end
59 60
60 - it "should raise error when called directly" do 61 + it "should raise error when dirtory exists" do
61 expect { @namespace.move_dir }.to raise_error("Already exists") 62 expect { @namespace.move_dir }.to raise_error("Already exists")
62 end 63 end
63 64