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