##// END OF EJS Templates
Merged nbc branch @ r1812 (commit access permission and reposman improvements)....
Jean-Philippe Lang -
r1812:cc643ce932b2
parent child
Show More
@@ -0,0 +1,14
1 class AddCommitAccessPermission < ActiveRecord::Migration
2
3 def self.up
4 Role.find(:all).select { |r| not r.builtin? }.each do |r|
5 r.add_permission!(:commit_access)
6 end
7 end
8
9 def self.down
10 Role.find(:all).select { |r| not r.builtin? }.each do |r|
11 r.remove_permission!(:commit_access)
12 end
13 end
14 end
@@ -15,11 +15,19
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AWSProjectWithRepository < ActionWebService::Struct
19 member :id, :int
20 member :identifier, :string
21 member :name, :string
22 member :is_public, :bool
23 member :repository, Repository
24 end
25
18 class SysApi < ActionWebService::API::Base
26 class SysApi < ActionWebService::API::Base
19 api_method :projects,
27 api_method :projects_with_repository_enabled,
20 :expects => [],
28 :expects => [],
21 :returns => [[Project]]
29 :returns => [[AWSProjectWithRepository]]
22 api_method :repository_created,
30 api_method :repository_created,
23 :expects => [:string, :string],
31 :expects => [:string, :string, :string],
24 :returns => [:int]
32 :returns => [:int]
25 end
33 end
@@ -23,18 +23,17 class SysController < ActionController::Base
23 before_invocation :check_enabled
23 before_invocation :check_enabled
24
24
25 # Returns the projects list, with their repositories
25 # Returns the projects list, with their repositories
26 def projects
26 def projects_with_repository_enabled
27 Project.find(:all, :include => :repository)
27 Project.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
28 end
28 end
29
29
30 # Registers a repository for the given project identifier
30 # Registers a repository for the given project identifier
31 # (Subversion specific)
31 def repository_created(identifier, vendor, url)
32 def repository_created(identifier, url)
33 project = Project.find_by_identifier(identifier)
32 project = Project.find_by_identifier(identifier)
34 # Do not create the repository if the project has already one
33 # Do not create the repository if the project has already one
35 return 0 unless project && project.repository.nil?
34 return 0 unless project && project.repository.nil?
36 logger.debug "Repository for #{project.name} was created"
35 logger.debug "Repository for #{project.name} was created"
37 repository = Repository.factory('Subversion', :project => project, :url => url)
36 repository = Repository.factory(vendor, :project => project, :url => url)
38 repository.save
37 repository.save
39 repository.id || 0
38 repository.id || 0
40 end
39 end
@@ -63,6 +63,8 class Project < ActiveRecord::Base
63
63
64 before_destroy :delete_all_members
64 before_destroy :delete_all_members
65
65
66 named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } }
67
66 def identifier=(identifier)
68 def identifier=(identifier)
67 super unless identifier_frozen?
69 super unless identifier_frozen?
68 end
70 end
@@ -20,6 +20,11 class Role < ActiveRecord::Base
20 BUILTIN_NON_MEMBER = 1
20 BUILTIN_NON_MEMBER = 1
21 BUILTIN_ANONYMOUS = 2
21 BUILTIN_ANONYMOUS = 2
22
22
23 named_scope :builtin, lambda { |*args|
24 compare = 'not' if args.first == true
25 { :conditions => "#{compare} builtin = 0" }
26 }
27
23 before_destroy :check_deletable
28 before_destroy :check_deletable
24 has_many :workflows, :dependent => :delete_all do
29 has_many :workflows, :dependent => :delete_all do
25 def copy(role)
30 def copy(role)
@@ -36,7 +41,7 class Role < ActiveRecord::Base
36 has_many :members
41 has_many :members
37 acts_as_list
42 acts_as_list
38
43
39 serialize :permissions
44 serialize :permissions, Array
40 attr_protected :builtin
45 attr_protected :builtin
41
46
42 validates_presence_of :name
47 validates_presence_of :name
@@ -49,10 +54,28 class Role < ActiveRecord::Base
49 end
54 end
50
55
51 def permissions=(perms)
56 def permissions=(perms)
52 perms = perms.collect {|p| p.to_sym unless p.blank? }.compact if perms
57 perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
53 write_attribute(:permissions, perms)
58 write_attribute(:permissions, perms)
54 end
59 end
55
60
61 def add_permission!(*perms)
62 self.permissions = [] unless permissions.is_a?(Array)
63
64 permissions_will_change!
65 perms.each do |p|
66 p = p.to_sym
67 permissions << p unless permissions.include?(p)
68 end
69 save!
70 end
71
72 def remove_permission!(*perms)
73 return unless permissions.is_a?(Array)
74 permissions_will_change!
75 perms.each { |p| permissions.delete(p.to_sym) }
76 save!
77 end
78
56 def <=>(role)
79 def <=>(role)
57 position <=> role.position
80 position <=> role.position
58 end
81 end
@@ -148,11 +148,12 sub RedmineDSN {
148 my ($self, $parms, $arg) = @_;
148 my ($self, $parms, $arg) = @_;
149 $self->{RedmineDSN} = $arg;
149 $self->{RedmineDSN} = $arg;
150 my $query = "SELECT
150 my $query = "SELECT
151 hashed_password, auth_source_id
151 hashed_password, auth_source_id, permissions
152 FROM members, projects, users
152 FROM members, projects, users, roles
153 WHERE
153 WHERE
154 projects.id=members.project_id
154 projects.id=members.project_id
155 AND users.id=members.user_id
155 AND users.id=members.user_id
156 AND roles.id=members.role_id
156 AND users.status=1
157 AND users.status=1
157 AND login=?
158 AND login=?
158 AND identifier=? ";
159 AND identifier=? ";
@@ -277,9 +278,11 sub is_member {
277 $sth->execute($redmine_user, $project_id);
278 $sth->execute($redmine_user, $project_id);
278
279
279 my $ret;
280 my $ret;
280 while (my @row = $sth->fetchrow_array) {
281 while (my ($hashed_password, $auth_source_id, $permissions) = $sth->fetchrow_array) {
281 unless ($row[1]) {
282
282 if ($row[0] eq $pass_digest) {
283 unless ($auth_source_id) {
284 my $method = $r->method;
285 if ($hashed_password eq $pass_digest && (defined $read_only_methods{$method} || $permissions =~ /:commit_access/) ) {
283 $ret = 1;
286 $ret = 1;
284 last;
287 last;
285 }
288 }
@@ -287,7 +290,7 sub is_member {
287 my $sthldap = $dbh->prepare(
290 my $sthldap = $dbh->prepare(
288 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
291 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
289 );
292 );
290 $sthldap->execute($row[1]);
293 $sthldap->execute($auth_source_id);
291 while (my @rowldap = $sthldap->fetchrow_array) {
294 while (my @rowldap = $sthldap->fetchrow_array) {
292 my $ldap = Authen::Simple::LDAP->new(
295 my $ldap = Authen::Simple::LDAP->new(
293 host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
296 host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
@@ -6,52 +6,49
6 #
6 #
7 # == Usage
7 # == Usage
8 #
8 #
9 # reposman [ -h | --help ] [ -v | --verbose ] [ -V | --version ] [ -q | --quiet ] -s /var/svn -r redmine.host.org
9 # reposman [OPTIONS...] -s [DIR] -r [HOST]
10 # example: reposman --svn-dir=/var/svn --redmine-host=redmine.mydomain.foo
11 # reposman -s /var/svn -r redmine.mydomain.foo
12 #
10 #
13 # == Arguments (mandatory)
11 # Examples:
12 # reposman --svn-dir=/var/svn --redmine-host=redmine.example.net
13 # reposman -s /var/svn -r redmine.example.net -u http://svn.example.net
14 #
14 #
15 # -s, --svn-dir=DIR
15 # == Arguments (mandatory)
16 # use DIR as base directory for svn repositories
17 #
16 #
18 # -r, --redmine-host=HOST
17 # -s, --svn-dir=DIR use DIR as base directory for svn repositories
19 # assume Redmine is hosted on HOST.
18 # -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples:
20 # you can use :
19 # -r redmine.example.net
21 # * -r redmine.mydomain.foo (will add http://)
20 # -r http://redmine.example.net
22 # * -r http://redmine.mydomain.foo
21 # -r https://example.net/redmine
23 # * -r https://mydomain.foo/redmine
24 #
22 #
25 # == Options
23 # == Options
26 #
24 #
27 # -o, --owner=OWNER
25 # -o, --owner=OWNER owner of the repository. using the rails login
28 # owner of the repository. using the rails login allow user to browse
26 # allow user to browse the repository within
29 # the repository in Redmine even for private project
27 # Redmine even for private project
30 #
28 # -u, --url=URL the base url Redmine will use to access your
31 # -u, --url=URL
29 # repositories. This option is used to automatically
32 # the base url Redmine will use to access your repositories. This
30 # register the repositories in Redmine. The project
33 # will be used to register the repository in Redmine so that user
31 # identifier will be appended to this url. Examples:
34 # doesn't need to do anything. reposman will add the identifier to this url :
32 # -u https://example.net/svn
35 #
33 # -u file:///var/svn/
36 # -u https://my.svn.server/my/reposity/root # if the repository can be access by http
34 # if this option isn't set, reposman won't register
37 # -u file:///var/svn/ # if the repository is local
35 # the repositories in Redmine
38 # if this option isn't set, reposman won't register the repository
36 # -c, --command=COMMAND use this command instead of "svnadmin create" to
39 #
37 # create a repository. This option can be used to
40 # -t, --test
38 # create non-subversion repositories
41 # only show what should be done
39 # --scm SCM vendor used to register the repository in
42 #
40 # Redmine (default: Subversion). Can be one of the
43 # -h, --help:
41 # other supported SCM: Bazaar, Darcs, Filesystem,
44 # show help and exit
42 # Git, Mercurial (case sensitive).
45 #
43 # This option should be used when both options --url
46 # -v, --verbose
44 # and --command are used.
47 # verbose
45 # -f, --force force repository creation even if the project
48 #
46 # repository is already declared in Redmine
49 # -V, --version
47 # -t, --test only show what should be done
50 # print version and exit
48 # -h, --help show help and exit
51 #
49 # -v, --verbose verbose
52 # -q, --quiet
50 # -V, --version print version and exit
53 # no log
51 # -q, --quiet no log
54 #
55
52
56 require 'getoptlong'
53 require 'getoptlong'
57 require 'rdoc/usage'
54 require 'rdoc/usage'
@@ -59,14 +56,18 require 'soap/wsdlDriver'
59 require 'find'
56 require 'find'
60 require 'etc'
57 require 'etc'
61
58
62 Version = "1.0"
59 Version = "1.1"
60 SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
63
61
64 opts = GetoptLong.new(
62 opts = GetoptLong.new(
65 ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
63 ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
66 ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
64 ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
67 ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT],
65 ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT],
68 ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
66 ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
67 ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
68 ['--scm', GetoptLong::REQUIRED_ARGUMENT],
69 ['--test', '-t', GetoptLong::NO_ARGUMENT],
69 ['--test', '-t', GetoptLong::NO_ARGUMENT],
70 ['--force', '-f', GetoptLong::NO_ARGUMENT],
70 ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
71 ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
71 ['--version', '-V', GetoptLong::NO_ARGUMENT],
72 ['--version', '-V', GetoptLong::NO_ARGUMENT],
72 ['--help' , '-h', GetoptLong::NO_ARGUMENT],
73 ['--help' , '-h', GetoptLong::NO_ARGUMENT],
@@ -81,6 +82,9 $svn_owner = 'root'
81 $use_groupid = true
82 $use_groupid = true
82 $svn_url = false
83 $svn_url = false
83 $test = false
84 $test = false
85 $command = "svnadmin create"
86 $force = false
87 $scm = 'Subversion'
84
88
85 def log(text,level=0, exit=false)
89 def log(text,level=0, exit=false)
86 return if $quiet or level > $verbose
90 return if $quiet or level > $verbose
@@ -95,8 +99,11 begin
95 when '--redmine-host'; $redmine_host = arg.dup
99 when '--redmine-host'; $redmine_host = arg.dup
96 when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
100 when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
97 when '--url'; $svn_url = arg.dup
101 when '--url'; $svn_url = arg.dup
102 when '--scm'; $scm = arg.dup; log("Invalid SCM: #{$scm}", 0, true) unless SUPPORTED_SCM.include?($scm)
103 when '--command'; $command = arg.dup
98 when '--verbose'; $verbose += 1
104 when '--verbose'; $verbose += 1
99 when '--test'; $test = true
105 when '--test'; $test = true
106 when '--force'; $force = true
100 when '--version'; puts Version; exit
107 when '--version'; puts Version; exit
101 when '--help'; RDoc::usage
108 when '--help'; RDoc::usage
102 when '--quiet'; $quiet = true
109 when '--quiet'; $quiet = true
@@ -110,6 +117,12 if $test
110 log("running in test mode")
117 log("running in test mode")
111 end
118 end
112
119
120 # Make sure command is overridden if SCM vendor is not Subversion
121 if $scm != 'Subversion' && $command == 'svnadmin create'
122 log("Please use --command option to specify how to create a #{$scm} repository.", 0, true)
123 end
124
125
113 $svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
126 $svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
114
127
115 if ($redmine_host.empty? or $repos_base.empty?)
128 if ($redmine_host.empty? or $repos_base.empty?)
@@ -133,7 +146,7 rescue => e
133 log("Unable to connect to #{wsdl_url} : #{e}", 0, true)
146 log("Unable to connect to #{wsdl_url} : #{e}", 0, true)
134 end
147 end
135
148
136 projects = soap.Projects
149 projects = soap.ProjectsWithRepositoryEnabled
137
150
138 if projects.nil?
151 if projects.nil?
139 log('no project found, perhaps you forgot to "Enable WS for repository management"', 0, true)
152 log('no project found, perhaps you forgot to "Enable WS for repository management"', 0, true)
@@ -201,6 +214,13 projects.each do |project|
201 log("\tmode change on #{repos_path}");
214 log("\tmode change on #{repos_path}");
202
215
203 else
216 else
217 # if repository is already declared in redmine, we don't create
218 # unless user use -f with reposman
219 if $force == false and not project.repository.nil?
220 log("\trepository for project #{project.identifier} already exists in Redmine", 1)
221 next
222 end
223
204 project.is_public ? File.umask(0002) : File.umask(0007)
224 project.is_public ? File.umask(0002) : File.umask(0007)
205
225
206 if $test
226 if $test
@@ -211,7 +231,8 projects.each do |project|
211
231
212 begin
232 begin
213 set_owner_and_rights(project, repos_path) do
233 set_owner_and_rights(project, repos_path) do
214 raise "svnadmin create #{repos_path} failed" unless system("svnadmin", "create", repos_path)
234 command = "#{$command} #{repos_path}"
235 raise "#{command} failed" unless system( command )
215 end
236 end
216 rescue => e
237 rescue => e
217 log("\tunable to create #{repos_path} : #{e}\n")
238 log("\tunable to create #{repos_path} : #{e}\n")
@@ -219,7 +240,7 projects.each do |project|
219 end
240 end
220
241
221 if $svn_url
242 if $svn_url
222 ret = soap.RepositoryCreated project.identifier, "#{$svn_url}#{project.identifier}"
243 ret = soap.RepositoryCreated project.identifier, $scm, "#{$svn_url}#{project.identifier}"
223 if ret > 0
244 if ret > 0
224 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
245 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
225 else
246 else
@@ -88,6 +88,7 Redmine::AccessControl.map do |map|
88 map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
88 map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
89 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
89 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
90 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
90 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
91 map.permission :commit_access, {}
91 end
92 end
92
93
93 map.project_module :boards do |map|
94 map.project_module :boards do |map|
@@ -67,7 +67,8 module Redmine
67 :view_files,
67 :view_files,
68 :manage_files,
68 :manage_files,
69 :browse_repository,
69 :browse_repository,
70 :view_changesets]
70 :view_changesets,
71 :commit_access]
71
72
72 reporter = Role.create! :name => l(:default_role_reporter),
73 reporter = Role.create! :name => l(:default_role_reporter),
73 :position => 3,
74 :position => 3,
@@ -5,7 +5,7 require 'sys_controller'
5 class SysController; def rescue_action(e) raise e end; end
5 class SysController; def rescue_action(e) raise e end; end
6
6
7 class SysControllerTest < Test::Unit::TestCase
7 class SysControllerTest < Test::Unit::TestCase
8 fixtures :projects, :repositories
8 fixtures :projects, :enabled_modules, :repositories
9
9
10 def setup
10 def setup
11 @controller = SysController.new
11 @controller = SysController.new
@@ -15,17 +15,36 class SysControllerTest < Test::Unit::TestCase
15 Setting.sys_api_enabled = 1
15 Setting.sys_api_enabled = 1
16 end
16 end
17
17
18 def test_projects
18 def test_projects_with_repository_enabled
19 result = invoke :projects
19 result = invoke :projects_with_repository_enabled
20 assert_equal Project.count, result.size
20 assert_equal EnabledModule.count(:all, :conditions => {:name => 'repository'}), result.size
21 assert result.first.is_a?(Project)
21
22 project = result.first
23 assert project.is_a?(AWSProjectWithRepository)
24
25 assert project.respond_to?(:id)
26 assert_equal 1, project.id
27
28 assert project.respond_to?(:identifier)
29 assert_equal 'ecookbook', project.identifier
30
31 assert project.respond_to?(:name)
32 assert_equal 'eCookbook', project.name
33
34 assert project.respond_to?(:is_public)
35 assert project.is_public
36
37 assert project.respond_to?(:repository)
38 assert project.repository.is_a?(Repository)
22 end
39 end
23
40
24 def test_repository_created
41 def test_repository_created
25 project = Project.find(3)
42 project = Project.find(3)
26 assert_nil project.repository
43 assert_nil project.repository
27 assert invoke(:repository_created, project.identifier, 'http://localhost/svn')
44 assert invoke(:repository_created, project.identifier, 'Subversion', 'http://localhost/svn')
28 project.reload
45 project.reload
29 assert_not_nil project.repository
46 assert_not_nil project.repository
47 assert project.repository.is_a?(Repository::Subversion)
48 assert_equal 'http://localhost/svn', project.repository.url
30 end
49 end
31 end
50 end
@@ -30,4 +30,24 class RoleTest < Test::Unit::TestCase
30 target.reload
30 target.reload
31 assert_equal 90, target.workflows.size
31 assert_equal 90, target.workflows.size
32 end
32 end
33
34 def test_add_permission
35 role = Role.find(1)
36 size = role.permissions.size
37 role.add_permission!("apermission", "anotherpermission")
38 role.reload
39 assert role.permissions.include?(:anotherpermission)
40 assert_equal size + 2, role.permissions.size
41 end
42
43 def test_remove_permission
44 role = Role.find(1)
45 size = role.permissions.size
46 perm = role.permissions[0..1]
47 role.remove_permission!(*perm)
48 role.reload
49 assert ! role.permissions.include?(perm[0])
50 assert_equal size - 2, role.permissions.size
51 end
52
33 end
53 end
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now