User groups branch added.

git-svn-id: http://redmine.rubyforge.org/svn/branches/work@1372 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang
2008-04-28 14:51:16 +00:00
parent 4dfa4bbc77
commit 4783d3d7c5
1095 changed files with 99809 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
== Sample plugin
This is a sample plugin for Redmine
== Installation
=== Adding plugin support to Redmine
To add plugin support to Redmine, install engines plugin:
See: http://rails-engines.org/
=== Plugin installation
1. Copy the plugin directory into the vendor/plugins directory
2. Migrate plugin:
rake db:migrate_plugins
3. Start Redmine
Installed plugins are listed on 'Admin -> Information' screen.
@@ -0,0 +1,19 @@
# Sample plugin controller
class ExampleController < ApplicationController
unloadable
layout 'base'
before_filter :find_project, :authorize
def say_hello
@value = Setting.plugin_sample_plugin['sample_setting']
end
def say_goodbye
end
private
def find_project
@project=Project.find(params[:id])
end
end
@@ -0,0 +1,5 @@
<p class="icon icon-example-works"><%= l(:text_say_goodbye) %></p>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "example.css", :plugin => "sample_plugin", :media => "screen" %>
<% end %>
@@ -0,0 +1,9 @@
<p class="icon icon-example-works"><%= l(:text_say_hello) %></p>
<p><label>Example setting</label>: <%= @value %></p>
<%= link_to_if_authorized 'Good bye', :action => 'say_goodbye', :id => @project %>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "example.css", :plugin => "sample_plugin", :media => "screen" %>
<% end %>
@@ -0,0 +1,3 @@
<p><label>Example setting</label><%= text_field_tag 'settings[sample_setting]', @settings['sample_setting'] %></p>
<p><label>Foo</label><%= text_field_tag 'settings[foo]', @settings['foo'] %></p>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@@ -0,0 +1 @@
.icon-example-works { background-image: url(../images/it_works.png); }
@@ -0,0 +1,13 @@
# Sample plugin migration
# Use rake db:migrate_plugins to migrate installed plugins
class CreateSomeModels < ActiveRecord::Migration
def self.up
create_table :example_plugin_model, :force => true do |t|
t.column "example_attribute", :integer
end
end
def self.down
drop_table :example_plugin_model
end
end
+25
View File
@@ -0,0 +1,25 @@
# Redmine sample plugin
require 'redmine'
RAILS_DEFAULT_LOGGER.info 'Starting Example plugin for RedMine'
Redmine::Plugin.register :sample_plugin do
name 'Example plugin'
author 'Author name'
description 'This is a sample plugin for Redmine'
version '0.0.1'
settings :default => {'sample_setting' => 'value', 'foo'=>'bar'}, :partial => 'settings/settings'
# This plugin adds a project module
# It can be enabled/disabled at project level (Project settings -> Modules)
project_module :example_module do
# A public action
permission :example_say_hello, {:example => [:say_hello]}, :public => true
# This permission has to be explicitly given
# It will be listed on the permissions screen
permission :example_say_goodbye, {:example => [:say_goodbye]}
end
# A new item is added to the project menu
menu :project_menu, :sample_plugin, { :controller => 'example', :action => 'say_hello' }, :caption => 'Sample'
end
+4
View File
@@ -0,0 +1,4 @@
# Sample plugin
label_plugin_example: Sample Plugin
text_say_hello: Plugin say 'Hello'
text_say_goodbye: Plugin say 'Good bye'
+4
View File
@@ -0,0 +1,4 @@
# Sample plugin
label_plugin_example: Plugin exemple
text_say_hello: Plugin dit 'Bonjour'
text_say_goodbye: Plugin dit 'Au revoir'
+237
View File
@@ -0,0 +1,237 @@
package Apache::Authn::Redmine;
=head1 Apache::Authn::Redmine
Redmine - a mod_perl module to authenticate webdav subversion users
against redmine database
=head1 SYNOPSIS
This module allow anonymous users to browse public project and
registred users to browse and commit their project. Authentication is
done against the redmine database or the LDAP configured in redmine.
This method is far simpler than the one with pam_* and works with all
database without an hassle but you need to have apache/mod_perl on the
svn server.
=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,
DBI and DBD::mysql (or the DBD driver for you database as it should
work on allmost all databases).
On debian/ubuntu you must do :
aptitude install libapache-dbi-perl libapache2-mod-perl2 libdbd-mysql-perl
If your Redmine users use LDAP authentication, you will also need
Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
aptitude install libauthen-simple-ldap-perl libio-socket-ssl-perl
=head1 CONFIGURATION
## if the module isn't in your perl path
PerlRequire /usr/local/apache/Redmine.pm
## else
# PerlModule Apache::Authn::Redmine
<Location /svn>
DAV svn
SVNParentPath "/var/svn"
AuthType Basic
AuthName redmine
Require valid-user
PerlAccessHandler Apache::Authn::Redmine::access_handler
PerlAuthenHandler Apache::Authn::Redmine::authen_handler
## for mysql
PerlSetVar dsn DBI:mysql:database=databasename;host=my.db.server
## for postgres
# PerlSetVar dsn DBI:Pg:dbname=databasename;host=my.db.server
PerlSetVar db_user redmine
PerlSetVar db_pass password
</Location>
To be able to browse repository inside redmine, you must add something
like that :
<Location /svn-private>
DAV svn
SVNParentPath "/var/svn"
Order deny,allow
Deny from all
# only allow reading orders
<Limit GET PROPFIND OPTIONS REPORT>
Allow from redmine.server.ip
</Limit>
</Location>
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<them :>
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 DBI;
use Digest::SHA1;
# optional module for LDAP authentication
my $CanUseLDAPAuth = eval("use Authen::Simple::LDAP; 1");
use Apache2::Module;
use Apache2::Access;
use Apache2::ServerRec qw();
use Apache2::RequestRec qw();
use Apache2::RequestUtil qw();
use Apache2::Const qw(:common);
# use Apache2::Directive qw();
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
sub access_handler {
my $r = shift;
unless ($r->some_auth_required) {
$r->log_reason("No authentication has been configured");
return FORBIDDEN;
}
my $method = $r->method;
return OK unless 1 == $read_only_methods{$method};
my $project_id = get_project_identifier($r);
$r->set_handlers(PerlAuthenHandler => [\&OK])
if is_public_project($project_id, $r);
return OK
}
sub authen_handler {
my $r = shift;
my ($res, $redmine_pass) = $r->get_basic_auth_pw();
return $res unless $res == OK;
if (is_member($r->user, $redmine_pass, $r)) {
return OK;
} else {
$r->note_auth_failure();
return AUTH_REQUIRED;
}
}
sub is_public_project {
my $project_id = shift;
my $r = shift;
my $dbh = connect_database($r);
my $sth = $dbh->prepare(
"SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;"
);
$sth->execute($project_id);
my $ret = $sth->fetchrow_array ? 1 : 0;
$dbh->disconnect();
$ret;
}
# perhaps we should use repository right (other read right) to check public access.
# it could be faster BUT it doesn't work for the moment.
# sub is_public_project_by_file {
# my $project_id = shift;
# my $r = shift;
# my $tree = Apache2::Directive::conftree();
# my $node = $tree->lookup('Location', $r->location);
# my $hash = $node->as_hash;
# my $svnparentpath = $hash->{SVNParentPath};
# my $repos_path = $svnparentpath . "/" . $project_id;
# return 1 if (stat($repos_path))[2] & 00007;
# }
sub is_member {
my $redmine_user = shift;
my $redmine_pass = shift;
my $r = shift;
my $dbh = connect_database($r);
my $project_id = get_project_identifier($r);
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
my $sth = $dbh->prepare(
"SELECT hashed_password, auth_source_id FROM members, projects, users WHERE projects.id=members.project_id AND users.id=members.user_id AND users.status=1 AND login=? AND identifier=?;"
);
$sth->execute($redmine_user, $project_id);
my $ret;
while (my @row = $sth->fetchrow_array) {
unless ($row[1]) {
if ($row[0] eq $pass_digest) {
$ret = 1;
last;
}
} elsif ($CanUseLDAPAuth) {
my $sthldap = $dbh->prepare(
"SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
);
$sthldap->execute($row[1]);
while (my @rowldap = $sthldap->fetchrow_array) {
my $ldap = Authen::Simple::LDAP->new(
host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
port => $rowldap[1],
basedn => $rowldap[5],
binddn => $rowldap[3] ? $rowldap[3] : "",
bindpw => $rowldap[4] ? $rowldap[4] : "",
filter => "(".$rowldap[6]."=%s)"
);
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass));
}
$sthldap->finish();
}
}
$sth->finish();
$dbh->disconnect();
$ret;
}
sub get_project_identifier {
my $r = shift;
my $location = $r->location;
my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
$identifier;
}
sub connect_database {
my $r = shift;
my ($dsn, $db_user, $db_pass) = map { $r->dir_config($_) } qw/dsn db_user db_pass/;
return DBI->connect($dsn, $db_user, $db_pass);
}
1;
+24
View File
@@ -0,0 +1,24 @@
/* ssh views */
CREATE OR REPLACE VIEW ssh_users as
select login as username, hashed_password as password
from users
where status = 1;
/* nss views */
CREATE OR REPLACE VIEW nss_groups AS
select identifier AS name, (id + 5000) AS gid, 'x' AS password
from projects;
CREATE OR REPLACE VIEW nss_users AS
select login AS username, CONCAT_WS(' ', firstname, lastname) as realname, (id + 5000) AS uid, 'x' AS password
from users
where status = 1;
CREATE OR REPLACE VIEW nss_grouplist AS
select (members.project_id + 5000) AS gid, users.login AS username
from users, members
where users.id = members.user_id
and users.status = 1;
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/perl
#
# redMine is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
use strict;
use SOAP::Lite;
use Getopt::Long;
Getopt::Long::Configure ("bundling", "no_auto_abbrev", "no_ignore_case");
use Pod::Usage;
use vars qw/$VERSION/;
$VERSION = "1.0";
my $warning = "This program is now deprecated. Use the reposman.rb for new features";
print STDERR "*" x length($warning), "\n",
$warning, "\n",
"*" x length($warning), "\n\n";
my %opts = (verbose => 0);
GetOptions(\%opts, 'verbose|v+', 'version|V', 'help|h', 'man|m', 'quiet|q', 'svn-dir|s=s', 'redmine-host|r=s') or pod2usage(2);
die "$VERSION\n" if $opts{version};
pod2usage(1) if $opts{help};
pod2usage( -verbose => 2 ) if $opts{man};
my $repos_base = $opts{'svn-dir'};
my $redmine_host = $opts{'redmine-host'};
pod2usage(2) unless $repos_base and $redmine_host;
unless (-d $repos_base) {
Log(text => "$repos_base doesn't exist", exit => 1);
}
Log(level => 1, text => "querying redMine for projects...");
my $wdsl = "http://$redmine_host/sys/service.wsdl";
my $service = SOAP::Lite->service($wdsl);
my $projects = $service->Projects('');
my $project_count = @{$projects};
Log(level => 1, text => "retrieved $project_count projects");
foreach my $project (@{$projects}) {
Log(level => 1, text => "treating project $project->{name}");
my $repos_name = $project->{identifier};
if ($repos_name eq "") {
Log(text => "\tno identifier for project $project->{name}");
next;
}
unless ($repos_name =~ /^[a-z0-9\-]+$/) {
Log(text => "\tinvalid identifier for project $project->{name}");
next;
}
my $repos_path = "$repos_base/$repos_name";
if (-e $repos_path) {
# check unix right and change them if needed
my $other_read = (stat($repos_path))[2] & 00007;
my $right;
if ($project->{is_public} and not $other_read) {
$right = "0775";
} elsif (not $project->{is_public} and $other_read) {
$right = "0770";
} else {
next;
}
# change mode
system('chmod', '-R', $right, $repos_path) == 0 or
warn("\tunable to change mode on $repos_path : $?\n"), next;
Log(text => "\tmode change on $repos_path");
} else {
# change umask to suit the repository's privacy
$project->{is_public} ? umask 0002 : umask 0007;
# create the repository
system('svnadmin', 'create', $repos_path) == 0 or
warn("\tsystem svnadmin failed unable to create $repos_path\n"), next;
# set the group owner
system('chown', '-R', "root:$repos_name", $repos_path) == 0 or
warn("\tunable to create $repos_path : $?\n"), next;
Log(text => "\trepository $repos_path created");
}
}
sub Log {
my %args = (level => 0, text => '', @_);
my $level = delete $args{level};
my $text = delete $args{text};
return unless $level <= $opts{verbose};
return if $opts{quiet};
print "$text\n";
exit $args{exit}
if defined $args{exit};
}
__END__
=head1 NAME
reposman - manages your svn repositories with redMine
=head1 SYNOPSIS
reposman [options] arguments
example: reposman --svn-dir=/var/svn --redmine-host=redmine.mydomain.foo
reposman -s /var/svn -r redmine.mydomain.foo
=head1 ARGUMENTS
-s, --svn-dir=DIR use DIR as base directory for svn repositories
-r, --redmine-host=HOST assume redMine is hosted on HOST
=head1 OPTIONS
-v verbose
-V print version and exit
+234
View File
@@ -0,0 +1,234 @@
#!/usr/bin/ruby
# == Synopsis
#
# reposman: manages your svn repositories with Redmine
#
# == Usage
#
# reposman [ -h | --help ] [ -v | --verbose ] [ -V | --version ] [ -q | --quiet ] -s /var/svn -r redmine.host.org
# example: reposman --svn-dir=/var/svn --redmine-host=redmine.mydomain.foo
# reposman -s /var/svn -r redmine.mydomain.foo
#
# == Arguments (mandatory)
#
# -s, --svn-dir=DIR
# use DIR as base directory for svn repositories
#
# -r, --redmine-host=HOST
# assume Redmine is hosted on HOST.
# you can use :
# * -r redmine.mydomain.foo (will add http://)
# * -r http://redmine.mydomain.foo
# * -r https://mydomain.foo/redmine
#
# == Options
#
# -o, --owner=OWNER
# owner of the repository. using the rails login allow user to browse
# the repository in Redmine even for private project
#
# -u, --url=URL
# the base url Redmine will use to access your repositories. This
# will be used to register the repository in Redmine so that user
# doesn't need to do anything. reposman will add the identifier to this url :
#
# -u https://my.svn.server/my/reposity/root # if the repository can be access by http
# -u file:///var/svn/ # if the repository is local
# if this option isn't set, reposman won't register the repository
#
# -t, --test
# only show what should be done
#
# -h, --help:
# show help and exit
#
# -v, --verbose
# verbose
#
# -V, --version
# print version and exit
#
# -q, --quiet
# no log
#
require 'getoptlong'
require 'rdoc/usage'
require 'soap/wsdlDriver'
require 'find'
require 'etc'
Version = "1.0"
opts = GetoptLong.new(
['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT],
['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
['--test', '-t', GetoptLong::NO_ARGUMENT],
['--verbose', '-v', GetoptLong::NO_ARGUMENT],
['--version', '-V', GetoptLong::NO_ARGUMENT],
['--help' , '-h', GetoptLong::NO_ARGUMENT],
['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
)
$verbose = 0
$quiet = false
$redmine_host = ''
$repos_base = ''
$svn_owner = 'root'
$use_groupid = true
$svn_url = false
$test = false
def log(text,level=0, exit=false)
return if $quiet or level > $verbose
puts text
exit 1 if exit
end
begin
opts.each do |opt, arg|
case opt
when '--svn-dir'; $repos_base = arg.dup
when '--redmine-host'; $redmine_host = arg.dup
when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
when '--url'; $svn_url = arg.dup
when '--verbose'; $verbose += 1
when '--test'; $test = true
when '--version'; puts Version; exit
when '--help'; RDoc::usage
when '--quiet'; $quiet = true
end
end
rescue
exit 1
end
if $test
log("running in test mode")
end
$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
if ($redmine_host.empty? or $repos_base.empty?)
RDoc::usage
end
unless File.directory?($repos_base)
log("directory '#{$repos_base}' doesn't exists", 0, true)
end
log("querying Redmine for projects...", 1);
$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
$redmine_host.gsub!(/\/$/, '')
wsdl_url = "#{$redmine_host}/sys/service.wsdl";
begin
soap = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver
rescue => e
log("Unable to connect to #{wsdl_url} : #{e}", 0, true)
end
projects = soap.Projects
if projects.nil?
log('no project found, perhaps you forgot to "Enable WS for repository management"', 0, true)
end
log("retrieved #{projects.size} projects", 1)
def set_owner_and_rights(project, repos_path, &block)
if RUBY_PLATFORM =~ /mswin/
yield if block_given?
else
uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : 0)
right = project.is_public ? 0775 : 0770
yield if block_given?
Find.find(repos_path) do |f|
File.chmod right, f
File.chown uid, gid, f
end
end
end
def other_read_right?(file)
(File.stat(file).mode & 0007).zero? ? false : true
end
def owner_name(file)
RUBY_PLATFORM =~ /mswin/ ?
$svn_owner :
Etc.getpwuid( File.stat(file).uid ).name
end
projects.each do |project|
log("treating project #{project.name}", 1)
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}");
next;
end
repos_path = $repos_base + "/" + project.identifier
if File.directory?(repos_path)
# we must verify that repository has the good owner and the good
# 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
if $test
log("\tchange mode on #{repos_path}")
next
end
begin
set_owner_and_rights(project, repos_path)
rescue Errno::EPERM => e
log("\tunable to change mode on #{repos_path} : #{e}\n")
next
end
log("\tmode change on #{repos_path}");
else
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;
next
end
begin
set_owner_and_rights(project, repos_path) do
raise "svnadmin create #{repos_path} failed" unless system("svnadmin", "create", repos_path)
end
rescue => e
log("\tunable to create #{repos_path} : #{e}\n")
next
end
if $svn_url
ret = soap.RepositoryCreated project.identifier, "#{$svn_url}#{project.identifier}"
if ret > 0
log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
else
log("\trepository #{repos_path} not registered in Redmine. Look in your log to find why.");
end
end
log("\trepository #{repos_path} created");
end
end
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/perl
#
# redMine is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# modify to suit your repository base
my $repos_base = '/var/svn';
my $path = '/usr/bin/';
my %kwown_commands = map { $_ => 1 } qw/svnserve/;
umask 0002;
exec ('/usr/bin/svnserve', '-r', $repos_base, '-t');