diff --git a/extra/svn/OpenProjectAuthentication.pm b/extra/svn/OpenProjectAuthentication.pm
new file mode 100644
index 0000000000..9c8aaafc45
--- /dev/null
+++ b/extra/svn/OpenProjectAuthentication.pm
@@ -0,0 +1,188 @@
+package Apache::Authn::Redmine;
+
+=head1 Apache::Authn::Redmine
+
+Redmine - a mod_perl module to authenticate webdav subversion users
+against an OpenProject web service
+
+=head1 SYNOPSIS
+
+This module allow anonymous users to browse public project and
+registred users to browse and commit their project. Authentication is
+done against an OpenProject web service.
+
+=head1 INSTALLATION
+
+For this to automagically work, you need to have a recent reposman.rb
+(after r860) and if you already use reposman, read the last section to
+migrate.
+
+Sorry ruby users but you need some perl modules, at least mod_perl2.
+
+On debian/ubuntu you must do :
+
+ aptitude install libapache2-mod-perl2
+
+=head1 CONFIGURATION
+
+ ## This module has to be in your perl path
+ ## eg: /usr/lib/perl5/Apache/Authn/OpenProjectAuthentication.pm
+ PerlLoadModule Apache::Authn::OpenProjectAuthentication
+
+ DAV svn
+ SVNParentPath "/var/svn"
+
+ AuthType Basic
+ AuthName OpenProject
+ Require valid-user
+
+ PerlAccessHandler Apache::Authn::Redmine::access_handler
+ PerlAuthenHandler Apache::Authn::Redmine::authen_handler
+
+ RedmineUrl "http://example.com/openproject/"
+ RedmineApiKey ""
+
+
+To be able to browse repository inside redmine, you must add something
+like that :
+
+
+ DAV svn
+ SVNParentPath "/var/svn"
+ Order deny,allow
+ Deny from all
+ # only allow reading orders
+
+ Allow from redmine.server.ip
+
+
+
+and you will have to use this reposman.rb command line to create repository :
+
+ reposman.rb --redmine my.redmine.server --svn-dir /var/svn --owner www-data -u http://svn.server/svn-private/
+
+=head1 MIGRATION FROM OLDER RELEASES
+
+If you use an older reposman.rb (r860 or before), you need to change
+rights on repositories to allow the apache user to read and write
+S
+
+ sudo chown -R www-data /var/svn/*
+ sudo chmod -R u+w /var/svn/*
+
+And you need to upgrade at least reposman.rb (after r860).
+
+=cut
+
+use strict;
+use warnings FATAL => 'all', NONFATAL => 'redefine';
+
+use Digest::SHA1;
+
+use Apache2::Module;
+use Apache2::Access;
+use Apache2::ServerRec qw();
+use Apache2::RequestRec qw();
+use Apache2::RequestUtil qw();
+use Apache2::Const qw(:common :override :cmd_how);
+use APR::Pool ();
+use APR::Table ();
+
+use HTTP::Request::Common qw(POST);
+use LWP::UserAgent;
+
+# use Apache2::Directive qw();
+
+my @directives = (
+ {
+ name => 'RedmineUrl',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ errmsg => 'URL of your (local) OpenProject. (e.g. http://localhost/ or http://www.example.com/openproject/)',
+ },
+ {
+ name => 'RedmineApiKey',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+);
+
+sub RedmineUrl { set_val('RedmineUrl', @_); }
+sub RedmineApiKey { set_val('RedmineApiKey', @_); }
+
+sub trim {
+ my $string = shift;
+ $string =~ s/\s{2,}/ /g;
+ return $string;
+}
+
+sub set_val {
+ my ($key, $self, $parms, $arg) = @_;
+ $self->{$key} = $arg;
+}
+
+Apache2::Module::add(__PACKAGE__, \@directives);
+
+sub access_handler {
+ my $r = shift;
+
+ unless ($r->some_auth_required) {
+ $r->log_reason("No authentication has been configured");
+ return FORBIDDEN;
+ }
+
+ return OK
+}
+
+sub authen_handler {
+ my $r = shift;
+
+ my ($status, $password) = $r->get_basic_auth_pw();
+ my $login = $r->user;
+
+ return $status unless $status == OK;
+
+ my $identifier = get_project_identifier($r);
+ my $method = $r->method;
+
+ if( is_access_allowed( $login, $password, $identifier, $method, $r ) ) {
+ return OK;
+ } else {
+ $r->note_auth_failure();
+ return AUTH_REQUIRED;
+ }
+}
+
+# we send a request to the redmine sys api
+# and use the user's given login and password for basic auth
+# for accessing the redmine sys api an api key is needed
+sub is_access_allowed {
+ my $login = shift;
+ my $password = shift;
+ my $identifier = shift;
+ my $method = shift;
+ my $r = shift;
+
+ my $cfg = Apache2::Module::get_config( __PACKAGE__, $r->server, $r->per_dir_config );
+
+ my $key = $cfg->{RedmineApiKey};
+ my $redmine_url = $cfg->{RedmineUrl} . '/sys/repo_auth';
+
+ my $redmine_req = POST $redmine_url , [ repository => $identifier, key => $key, method => $method ];
+ $redmine_req->authorization_basic( $login, $password );
+
+ my $ua = LWP::UserAgent->new;
+ my $response = $ua->request($redmine_req);
+
+ return $response->is_success();
+}
+
+sub get_project_identifier {
+ my $r = shift;
+
+ my $location = $r->location;
+ my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
+ $identifier;
+}
+
+1;
diff --git a/extra/svn/reposman.rb b/extra/svn/reposman.rb
index cc6e75761d..a56d0aded6 100755
--- a/extra/svn/reposman.rb
+++ b/extra/svn/reposman.rb
@@ -2,7 +2,7 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
-# Copyright (C) 2012-2014 the OpenProject Foundation (OPF)
+# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
@@ -31,8 +31,11 @@
require 'optparse'
require 'find'
require 'etc'
+require 'json'
+require 'net/http'
+require 'uri'
-Version = "1.3"
+Version = "1.4"
SUPPORTED_SCM = %w( Subversion Git Filesystem )
$verbose = 0
@@ -161,28 +164,22 @@ unless File.directory?($repos_base)
log("directory '#{$repos_base}' doesn't exists", :exit => true)
end
-begin
- require 'active_resource'
-rescue LoadError
- log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
-end
-
-class Project < ActiveResource::Base
- self.headers["User-agent"] = "Redmine repository manager/#{Version}"
-end
-
log("querying Redmine for projects...", :level => 1);
$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
$redmine_host.gsub!(/\/$/, '')
-Project.site = "#{$redmine_host}/sys";
+api_uri = URI.parse("#{$redmine_host}/sys")
+http = Net::HTTP.new(api_uri.host, api_uri.port)
+http.use_ssl = (api_uri.scheme == 'https')
+http_headers = {'User-Agent' => "OpenProject-Repository-Manager/#{Version}"}
begin
# Get all active projects that have the Repository module enabled
- projects = Project.find(:all, :params => {:key => $api_key})
+ response = http.get("#{api_uri.path}/projects.json?key=#{$api_key}", http_headers)
+ projects = JSON.parse(response.body)
rescue => e
- log("Unable to connect to #{Project.site}: #{e}", :exit => true)
+ log("Unable to connect to #{$redmine_host}: #{e}", :exit => true)
end
if projects.nil?
@@ -195,8 +192,8 @@ def set_owner_and_rights(project, repos_path, &block)
if mswin?
yield if block_given?
else
- uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid)
- right = project.is_public ? $public_mode : $private_mode
+ uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project['identifier']).gid : Etc.getgrnam($svn_group).gid)
+ right = project['is_public'] ? $public_mode : $private_mode
right = right.to_i(8) & 007777
yield if block_given?
Find.find(repos_path) do |f|
@@ -221,17 +218,17 @@ def mswin?
end
projects.each do |project|
- log("treating project #{project.name}", :level => 1)
+ log("treating project #{project['name']}", :level => 1)
- if project.identifier.empty?
- log("\tno identifier for project #{project.name}")
+ if project['identifier'].empty?
+ log("\tno identifier for project #{project['name']}")
next
- elsif not project.identifier.match(/^[a-z0-9\-_]+$/)
- log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
+ elsif not project['identifier'].match(/^[a-z0-9\-_]+$/)
+ log("\tinvalid identifier for project #{project['name']} : #{project['identifier']}");
next;
end
- repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+ repos_path = File.join($repos_base, project['identifier']).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
if File.directory?(repos_path)
@@ -239,7 +236,7 @@ projects.each do |project|
# rights before leaving
other_read = other_read_right?(repos_path)
owner = owner_name(repos_path)
- next if project.is_public == other_read and owner == $svn_owner
+ next if project['is_public'] == other_read and owner == $svn_owner
if $test
log("\tchange mode on #{repos_path}")
@@ -258,16 +255,16 @@ projects.each do |project|
else
# if repository is already declared in redmine, we don't create
# unless user use -f with reposman
- if $force == false and project.respond_to?(:repository)
- log("\trepository for project #{project.identifier} already exists in Redmine", :level => 1)
+ if $force == false and project.has_key?('repository')
+ log("\trepository for project #{project['identifier']} already exists in Redmine", :level => 1)
next
end
- project.is_public ? File.umask(0002) : File.umask(0007)
+ project['is_public'] ? File.umask(0002) : File.umask(0007)
if $test
log("\tcreate repository #{repos_path}")
- log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url;
+ log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project['identifier']}") if $svn_url;
next
end
@@ -286,8 +283,11 @@ projects.each do |project|
if $svn_url
begin
- project.post(:repository, :vendor => $scm, :repository => {:url => "#{$svn_url}#{project.identifier}"}, :key => $api_key)
- log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
+ http.post("#{api_uri.path}/projects/#{project['identifier']}/repository.json?" +
+ "vendor=#{$scm}&repository[url]=#{$svn_url}#{project['identifier']}&key=#{$api_key}",
+ "", # empty data
+ http_headers)
+ log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project['identifier']}");
rescue => e
log("\trepository #{repos_path} not registered in Redmine: #{e.message}");
end