Project

General

Profile

Feature #2647 » redmine-1.3-9291_repository_auth.patch

Updated Apache authz patch for stable-1.3. - Robert Rath, 2012-04-01 13:04

View differences:

app/helpers/repositories_helper.rb (working copy)
157 157
  end
158 158

  
159 159
  def subversion_field_tags(form, repository)
160

  
161
      login_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
162
      login_options << [l("repository_login_usernamepassword"), 0] 
163
      login_options << [l("repository_login_current_username"), 1] 
164
      login_options << [l("repository_login_current_role"), 2] 
165

  
160 166
      content_tag('p', form.text_field(:url, :size => 60, :required => true,
161 167
                       :disabled => (repository && !repository.root_url.blank?)) +
162 168
                       '<br />'.html_safe +
163 169
                       '(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
170
      content_tag('p', form.text_field(:root_url, :size => 60) +
171
                       '<br />(this needs to be set to the to the highest level in the SVN tree allowed if not the URL)') +
172
      content_tag('p', form.select(:login_method, login_options)) +
164 173
      content_tag('p', form.text_field(:login, :size => 30)) +
165 174
      content_tag('p', form.password_field(
166 175
                            :password, :size => 30, :name => 'ignore',
167 176
                            :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
168 177
                            :onfocus => "this.value=''; this.name='repository[password]';",
169
                            :onchange => "this.name='repository[password]';"))
178
                            :onchange => "this.name='repository[password]';")) +
179
      content_tag('p', form.password_field(:security_token, :size => 30, :name => 'ignore',
180
                            :value => ((repository.new_record? || repository.security_token.blank?) ? '' : ('x'*15)),
181
                            :onfocus => "this.value=''; this.name='repository[security_token]';",
182
                            :onchange => "this.name='repository[security_token]';"))
170 183
  end
171 184

  
185
#      select_tag('login_method', options_for_select(login_method, repository.class.name.demodulize),
186
#               :onchange => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)") ) +
187

  
172 188
  def darcs_field_tags(form, repository)
173 189
    content_tag('p', form.text_field(
174 190
                     :url, :label => l(:field_path_to_repository),
app/models/repository.rb (working copy)
71 71
  end
72 72

  
73 73
  def scm
74
    @scm ||= self.scm_adapter.new(url, root_url,
75
                                  login, password, path_encoding)
74
    @scm ||= self.scm_adapter.new(url, root_url, login, password, path_encoding, login_method, security_token, project)
76 75
    update_attribute(:root_url, @scm.root_url) if root_url.blank?
77 76
    @scm
78 77
  end
app/models/repository/subversion.rb (working copy)
18 18
require 'redmine/scm/adapters/subversion_adapter'
19 19

  
20 20
class Repository::Subversion < Repository
21
  attr_protected :root_url
21
  #attr_protected :root_url
22 22
  validates_presence_of :url
23 23
  validates_format_of :url, :with => /^(http|https|svn(\+[^\s:\/\\]+)?|file):\/\/.+/i
24 24

  
extra/svn/Redmine.pm (working copy)
1
package Apache::Authn::Redmine;
1
package Apache::Redmine;
2 2

  
3
=head1 Apache::Authn::Redmine
3
=head1 Apache::Redmine
4 4

  
5 5
Redmine - a mod_perl module to authenticate webdav subversion users
6 6
against redmine database
7 7

  
8
In addition this module allows to authenticate a redmine server
9
for webdav subversion access, bypassing full authentication, still a
10
llowing to apply repository based security (e.g. .authz files)
11

  
12

  
8 13
=head1 SYNOPSIS
9 14

  
10 15
This module allow anonymous users to browse public project and
......
37 42
=head1 CONFIGURATION
38 43

  
39 44
   ## This module has to be in your perl path
40
   ## eg:  /usr/lib/perl5/Apache/Authn/Redmine.pm
41
   PerlLoadModule Apache::Authn::Redmine
45
   ## eg:  /usr/lib/perl5/Apache/Redmine.pm
46
   PerlLoadModule Apache::Redmine
42 47
   <Location /svn>
43 48
     DAV svn
44 49
     SVNParentPath "/var/svn"
......
47 52
     AuthName redmine
48 53
     Require valid-user
49 54

  
50
     PerlAccessHandler Apache::Authn::Redmine::access_handler
51
     PerlAuthenHandler Apache::Authn::Redmine::authen_handler
52

  
55
     PerlAccessHandler Apache::Redmine::access_handler
56
     PerlAuthenHandler Apache::Redmine::authen_handler
57
  
53 58
     ## for mysql
54 59
     RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
55 60
     ## for postgres
......
73 78
     Order deny,allow
74 79
     Deny from all
75 80
     # only allow reading orders
81

  
82
     AuthType Basic
83
     AuthName redmine
84
     Require valid-user
85

  
86
     PerlAccessHandler Apache::Redmine::redmine_access_handler
87
     PerlAuthenHandler Apache::Redmine::redmine_authen_handler
88

  
89
     RedmineSecurityToken "redmine"
90

  
76 91
     <Limit GET PROPFIND OPTIONS REPORT>
77 92
       Allow from redmine.server.ip
78 93
     </Limit>
......
142 157
    args_how => TAKE1,
143 158
    errmsg => 'RedmineCacheCredsMax must be decimal number',
144 159
  },
160
  {
161
    name => 'RedmineSecurityToken',
162
    req_override => OR_AUTHCFG,
163
    args_how => TAKE1,
164
    errmsg => 'RedmineSecurityToken additional authentication token',
165
  },
145 166
);
146 167

  
147
sub RedmineDSN {
168
sub RedmineDSN { 
148 169
  my ($self, $parms, $arg) = @_;
149 170
  $self->{RedmineDSN} = $arg;
150 171
  my $query = "SELECT 
......
164 185

  
165 186
sub RedmineDbUser { set_val('RedmineDbUser', @_); }
166 187
sub RedmineDbPass { set_val('RedmineDbPass', @_); }
167
sub RedmineDbWhereClause {
188
sub RedmineSecurityToken { set_val('RedmineSecurityToken', @_); }
189
sub RedmineDbWhereClause { 
168 190
  my ($self, $parms, $arg) = @_;
169 191
  $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
170 192
}
171 193

  
172
sub RedmineCacheCredsMax {
194
sub RedmineCacheCredsMax { 
173 195
  my ($self, $parms, $arg) = @_;
174 196
  if ($arg) {
175 197
    $self->{RedmineCachePool} = APR::Pool->new;
......
199 221
  my $r = shift;
200 222

  
201 223
  unless ($r->some_auth_required) {
202
      $r->log_reason("No authentication has been configured");
224
      $r->log_reason("Apache::Redmine - No authentication has been configured in vhost (access_handler)");
203 225
      return FORBIDDEN;
204 226
  }
205 227

  
......
216 238

  
217 239
sub authen_handler {
218 240
  my $r = shift;
219

  
241
  
220 242
  my ($res, $redmine_pass) =  $r->get_basic_auth_pw();
221 243
  return $res unless $res == OK;
222

  
244
  
223 245
  if (is_member($r->user, $redmine_pass, $r)) {
224 246
      return OK;
225 247
  } else {
......
246 268
  }
247 269
  $sth->finish();
248 270
  undef $sth;
249

  
271
  
250 272
  $dbh->disconnect();
251 273
  undef $dbh;
252 274

  
......
256 278
sub is_public_project {
257 279
    my $project_id = shift;
258 280
    my $r = shift;
259

  
281
    
260 282
    if (is_authentication_forced($r)) {
261 283
      return 0;
262 284
    }
......
283 305

  
284 306
sub anonymous_role_allows_browse_repository {
285 307
  my $r = shift;
286

  
308
  
287 309
  my $dbh = connect_database($r);
288 310
  my $sth = $dbh->prepare(
289 311
      "SELECT permissions FROM roles WHERE builtin = 2;"
290 312
  );
291

  
313
  
292 314
  $sth->execute();
293 315
  my $ret = 0;
294 316
  if (my @row = $sth->fetchrow_array) {
......
300 322
  undef $sth;
301 323
  $dbh->disconnect();
302 324
  undef $dbh;
303

  
325
  
304 326
  $ret;
305 327
}
306 328

  
......
328 350
  my $project_id  = get_project_identifier($r);
329 351

  
330 352
  my $pass_digest = Digest::SHA::sha1_hex($redmine_pass);
331

  
353
  
332 354
  my $access_mode = defined $read_only_methods{$r->method} ? "R" : "W";
333 355

  
334 356
  my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
......
397 419

  
398 420
sub get_project_identifier {
399 421
    my $r = shift;
400

  
422
    
401 423
    my $location = $r->location;
402 424
    my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
403 425
    $identifier;
......
405 427

  
406 428
sub connect_database {
407 429
    my $r = shift;
408

  
430
    
409 431
    my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
410 432
    return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
411 433
}
412 434

  
435
sub redmine_access_handler {
436
  my $r = shift;
437

  
438
  unless ($r->some_auth_required) {
439
      $r->log_reason("Apache::Redmine - No auth has been configured in vhost conf (redmine_access_handler)");
440
      return FORBIDDEN;
441
  }
442

  
443
  my $method = $r->method;
444
  return OK unless defined $read_only_methods{$method};
445

  
446
  my $project_id = get_project_identifier($r);
447

  
448
  $r->set_handlers(PerlAuthenHandler => [\&OK])
449
      if is_public_project($project_id, $r);
450

  
451
  return OK
452
}
453

  
454
sub redmine_authen_handler {
455
  my $r = shift;
456

  
457
  my ($res, $redmine_pass) =  $r->get_basic_auth_pw();
458
  return $res unless $res == OK;
459

  
460
  my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
461

  
462
  if ($cfg->{RedmineSecurityToken}) {
463
    my $securityToken = $cfg->{RedmineSecurityToken};
464

  
465
    if ($securityToken ne $redmine_pass) {
466
      $r->log_error("Apache::Redmine - Provided SecurityToken did not match (redmine_authen_handler)");
467
      $r->note_auth_failure();
468
      return AUTH_REQUIRED; 
469
    }    
470
  }  
471
  return OK;
472
}
473

  
413 474
1;
config/locales/en-GB.yml (working copy)
312 312
  field_text: Text field
313 313
  field_visible: Visible
314 314
  field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"
315
  
315
  field_login_method: Authentication method
316
  field_security_token: Security token
317
  field_root_url: Root URL
318

  
319
  repository_login_usernamepassword: Provided credentials
320
  repository_login_current_username: Name of current user
321
  repository_login_current_role: Role of current user
322
 
316 323
  setting_app_title: Application title
317 324
  setting_app_subtitle: Application subtitle
318 325
  setting_welcome_text: Welcome text
config/locales/en.yml (working copy)
317 317
  field_root_directory: Root directory
318 318
  field_cvsroot: CVSROOT
319 319
  field_cvs_module: Module
320
  field_login_method: Authentication method
321
  field_security_token: Security token
322
  field_root_url: Root URL
320 323

  
324
  repository_login_usernamepassword: Provided credentials
325
  repository_login_current_username: Name of current user
326
  repository_login_current_role: Role of current user
327

  
321 328
  setting_app_title: Application title
322 329
  setting_app_subtitle: Application subtitle
323 330
  setting_welcome_text: Welcome text
lib/redmine/scm/adapters/subversion_adapter.rb (working copy)
257 257
        private
258 258

  
259 259
        def credentials_string
260
          str = ''
261
          str << " --username #{shell_quote(@login)}" unless @login.blank?
262
          str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
260
          if !(User.current.is_a?(AnonymousUser))
261

  
262
            if (@login_method == 1)
263
              str = ''
264
              str << " --username #{shell_quote(User.current.login)}"
265
              str << " --password #{shell_quote( (@security_token.blank? ? "foo" : @security_token) )}"   
266
            end
267

  
268
            if (@login_method == 2) && (!@project.nil?)
269
              role = User.current.role_for_project(@project)
270

  
271
              if !role.blank?
272
                str = ''
273
                str << " --username #{shell_quote(role.name)}"
274
                str << " --password #{shell_quote( (@security_token.blank? ? "foo" : @security_token) )}"   
275
              end
276
            end
277

  
278
          end
279

  
280
          if (str.blank?)
281
            str = ''
282
            str << " --username #{shell_quote(@login)}" unless @login.blank?
283
            str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
284
          end
285

  
263 286
          str << " --no-auth-cache --non-interactive"
264 287
          str
265 288
        end
lib/redmine/scm/adapters/abstract_adapter.rb (working copy)
75 75
          end
76 76
        end
77 77

  
78
        def initialize(url, root_url=nil, login=nil, password=nil,
79
                       path_encoding=nil)
78
        def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil, login_method=nil, security_token=nil, project=nil)
80 79
          @url = url
81 80
          @login = login if login && !login.empty?
82 81
          @password = (password || "") if @login
83 82
          @root_url = root_url.blank? ? retrieve_root_url : root_url
83
          @login_method = login_method.blank? ? 0 : login_method
84
          @security_token = security_token.blank? ? "" : security_token
85
          @project = project
84 86
        end
85 87

  
86 88
        def adapter_name
......
95 97
          respond_to?('annotate')
96 98
        end
97 99

  
100
        def use_current_user
101
          @use_current_user
102
        end
103

  
98 104
        def root_url
99 105
          @root_url
100 106
        end
(3-3/6)