Project

General

Profile

« Previous | Next » 

Revision 1814

Merged nbc branch @ r1812 (commit access permission and reposman improvements).

View differences:

trunk/app/apis/sys_api.rb
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
trunk/app/controllers/sys_controller.rb
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
trunk/app/models/project.rb
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?
trunk/app/models/role.rb
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 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 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
trunk/db/migrate/096_add_commit_access_permission.rb
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
0 15

  
trunk/extra/svn/reposman.pl
1
#!/usr/bin/perl
2
#
3
# redMine is free software; you can redistribute it and/or
4
# modify it under the terms of the GNU General Public License
5
# as published by the Free Software Foundation; either version 2
6
# of the License, or (at your option) any later version.
7
# 
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
# 
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16

  
17
use strict;
18
use SOAP::Lite;
19
use Getopt::Long;
20
Getopt::Long::Configure ("bundling", "no_auto_abbrev", "no_ignore_case");
21
use Pod::Usage;
22
use vars qw/$VERSION/;
23

  
24
$VERSION = "1.0";
25

  
26
my $warning = "This program is now deprecated. Use the reposman.rb for new features";
27
print STDERR "*" x length($warning), "\n",
28
    $warning, "\n",
29
    "*" x length($warning), "\n\n";
30

  
31
my %opts = (verbose => 0);
32
GetOptions(\%opts, 'verbose|v+', 'version|V', 'help|h', 'man|m', 'quiet|q', 'svn-dir|s=s', 'redmine-host|r=s') or pod2usage(2);
33

  
34
die "$VERSION\n"           if $opts{version};
35
pod2usage(1)               if $opts{help};
36
pod2usage( -verbose => 2 ) if $opts{man};
37

  
38
my $repos_base = $opts{'svn-dir'};
39
my $redmine_host = $opts{'redmine-host'};
40

  
41
pod2usage(2) unless $repos_base and $redmine_host;
42

  
43
unless (-d $repos_base) {
44
    Log(text => "$repos_base doesn't exist", exit => 1);
45
}
46

  
47
Log(level => 1, text => "querying redMine for projects...");
48
my $wdsl = "http://$redmine_host/sys/service.wsdl";
49
my $service = SOAP::Lite->service($wdsl);
50

  
51
my $projects = $service->Projects('');
52
my $project_count = @{$projects};
53
Log(level => 1, text => "retrieved $project_count projects");
54

  
55
foreach my $project (@{$projects}) {
56
    Log(level => 1, text => "treating project $project->{name}");
57
    my $repos_name = $project->{identifier};
58

  
59
    if ($repos_name eq "") {
60
        Log(text => "\tno identifier for project $project->{name}");
61
        next;
62
    }
63

  
64
    unless ($repos_name =~ /^[a-z0-9\-]+$/) {
65
        Log(text => "\tinvalid identifier for project $project->{name}");
66
        next;
67
    }
68

  
69
    my $repos_path = "$repos_base/$repos_name";
70

  
71
    if (-e $repos_path) {
72
    	# check unix right and change them if needed
73
    	my $other_read = (stat($repos_path))[2] & 00007;
74
	    my $right;
75

  
76
	    if ($project->{is_public} and not $other_read) {
77
	        $right = "0775";
78
	    } elsif (not $project->{is_public} and $other_read) {
79
	        $right = "0770";
80
	    } else {
81
	        next;
82
	    }
83
		
84
		# change mode	
85
	    system('chmod', '-R', $right, $repos_path) == 0 or
86
	        warn("\tunable to change mode on $repos_path : $?\n"), next;
87
	
88
	    Log(text => "\tmode change on $repos_path");
89
    	    
90
    } else {    
91
	    # change umask to suit the repository's privacy
92
	    $project->{is_public} ? umask 0002 : umask 0007;
93
	    
94
		# create the repository
95
	    system('svnadmin', 'create', $repos_path) == 0 or
96
	        warn("\tsystem svnadmin failed unable to create $repos_path\n"), next;
97
	        
98
		# set the group owner
99
	    system('chown', '-R', "root:$repos_name", $repos_path) == 0 or
100
	        warn("\tunable to create $repos_path : $?\n"), next;
101

  
102
	    Log(text => "\trepository $repos_path created");
103
	}
104
}
105

  
106

  
107
sub Log {
108
    my %args = (level => 0, text => '', @_);
109

  
110
    my $level = delete $args{level};
111
    my $text  = delete $args{text};
112
    return unless $level <= $opts{verbose};
113
    return if $opts{quiet};
114
    print "$text\n";
115

  
116
    exit $args{exit}
117
        if defined $args{exit};
118
}
119

  
120

  
121
__END__
122

  
123
=head1 NAME
124

  
125
 reposman - manages your svn repositories with redMine
126

  
127
=head1 SYNOPSIS
128

  
129
 reposman [options] arguments
130
 example: reposman --svn-dir=/var/svn --redmine-host=redmine.mydomain.foo
131
          reposman -s /var/svn -r redmine.mydomain.foo
132

  
133
=head1 ARGUMENTS
134

  
135
 -s, --svn-dir=DIR        use DIR as base directory for svn repositories
136
 -r, --redmine-host=HOST  assume redMine is hosted on HOST
137

  
138
=head1 OPTIONS
139

  
140
 -v                       verbose
141
 -V                       print version and exit
142

  
143 0

  
trunk/extra/svn/Redmine.pm
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 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 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],
trunk/extra/svn/reposman.rb
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 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 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 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 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 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 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 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 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
trunk/lib/redmine/default_data/loader.rb
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,
trunk/lib/redmine.rb
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|
trunk/test/functional/sys_api_test.rb
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 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
trunk/test/unit/role_test.rb
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

Also available in: Unified diff