Merge pull request #2 from eisin/master

Use for content publishing, not for repository access
master
Adirelle 2018-02-12 19:46:40 +01:00 committed by GitHub
commit 0cd05b468a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 115 additions and 13 deletions

View File

@ -61,9 +61,9 @@ Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
# RedmineDbWhereClause "and members.role_id IN (1,2)" # RedmineDbWhereClause "and members.role_id IN (1,2)"
## SCM transport protocol, used to detecte write requests ## SCM transport protocol, used to detecte write requests
## Valid values: dav-svn, git-smart-http ## Valid values: Subversion, Git, None
## Default: dav-svn ## Default: Subversion
# RedmineSCMProtocol dav-svn # RedmineRepositoryType Subversion
## Credentials cache size ## Credentials cache size
## Default: 0 (disabled) ## Default: 0 (disabled)
@ -101,6 +101,10 @@ Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
## Default: On ## Default: On
# RedmineSuperAdmin Off # RedmineSuperAdmin Off
## Sets firstname, lastname, email address to environment variables.
## Default: Off
# RedmineSetUserAttributes Off
</Location> </Location>
To be able to browse repository inside redmine, you must add something To be able to browse repository inside redmine, you must add something
@ -132,4 +136,6 @@ S<them :>
And you need to upgrade at least reposman.rb (after r860). And you need to upgrade at least reposman.rb (after r860).
=cut =cut

View File

@ -63,7 +63,7 @@ Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
# RedmineDbWhereClause "and members.role_id IN (1,2)" # RedmineDbWhereClause "and members.role_id IN (1,2)"
## SCM transport protocol, used to detecte write requests ## SCM transport protocol, used to detecte write requests
## Valid values: Subversion, Git ## Valid values: Subversion, Git, None
## Default: Subversion ## Default: Subversion
# RedmineRepositoryType Subversion # RedmineRepositoryType Subversion
@ -237,7 +237,13 @@ my @directives = (
name => 'RedmineRepositoryType', name => 'RedmineRepositoryType',
req_override => OR_AUTHCFG, req_override => OR_AUTHCFG,
args_how => TAKE1, args_how => TAKE1,
errmsg => 'Indicate the type of Repository (Subversion or Git). This is used to properly detected write requests. Defaults to Subversion.', errmsg => 'Indicate the type of Repository (Subversion or Git or None). This is used to properly detected write requests. Defaults to Subversion.',
},
{
name => 'RedmineSetUserAttributes',
req_override => OR_AUTHCFG,
args_how => FLAG,
errmsg => 'Sets firstname, lastname, email address to environment variables. Defaults to no.',
}, },
); );
@ -263,6 +269,8 @@ sub DIR_CREATE {
DenyAnonymous => 0, DenyAnonymous => 0,
DenyNonMember => 0, DenyNonMember => 0,
SuperAdmin => 1, SuperAdmin => 1,
SetUserAttributes => 0,
AttributesCacheCredsCount => 0,
}, $class; }, $class;
} }
@ -278,6 +286,7 @@ sub RedmineCacheCredsMaxAge { set_val('CacheCredsMaxAge', @_); }
sub RedmineDenyAnonymous { set_val('DenyAnonymous', @_); } sub RedmineDenyAnonymous { set_val('DenyAnonymous', @_); }
sub RedmineDenyNonMember { set_val('DenyNonMember', @_); } sub RedmineDenyNonMember { set_val('DenyNonMember', @_); }
sub RedmineSuperAdmin { set_val('SuperAdmin', @_); } sub RedmineSuperAdmin { set_val('SuperAdmin', @_); }
sub RedmineSetUserAttributes { set_val('SetUserAttributes', @_); }
sub RedmineDbWhereClause { sub RedmineDbWhereClause {
my ($cfg, $parms, $arg) = @_; my ($cfg, $parms, $arg) = @_;
@ -291,8 +300,10 @@ sub RedmineRepositoryType {
$arg = trim($arg); $arg = trim($arg);
if($arg eq 'Subversion' || $arg eq 'Git') { if($arg eq 'Subversion' || $arg eq 'Git') {
$cfg->{RepositoryType} = 'Repository::'.$arg; $cfg->{RepositoryType} = 'Repository::'.$arg;
} elsif($arg eq 'None') {
$cfg->{RepositoryType} = $arg;
} else { } else {
die "Invalid RedmineRepositoryType value: $arg, choose either Subversion or Git !"; die "Invalid RedmineRepositoryType value: $arg, choose either Subversion or Git or None !";
} }
} }
@ -335,16 +346,21 @@ sub authen_handler {
# Used cached credentials if possible # Used cached credentials if possible
my $cache_key = get_cache_key($r, $password); my $cache_key = get_cache_key($r, $password);
if(defined $cache_key && cache_get($r, $cache_key)) { my $cfg = get_config($r);
if(defined $cache_key && !$cfg->{SetUserAttributes} && cache_get($r, $cache_key)) {
$r->log->debug("reusing cached credentials for user '", $r->user, "'"); $r->log->debug("reusing cached credentials for user '", $r->user, "'");
$r->set_handlers(PerlAuthzHandler => undef); $r->set_handlers(PerlAuthzHandler => undef);
} elsif(defined $cache_key && $cfg->{SetUserAttributes} && cache_get($r, $cache_key) && attributes_cache_get($r, $cache_key)) {
$r->log->debug("reusing cached credentials for user '", $r->user, "' including attributes");
$r->set_handlers(PerlAuthzHandler => undef);
} else { } else {
# Else check them # Else check them
my $dbh = connect_database($r) my $dbh = connect_database($r)
or return SERVER_ERROR; or return SERVER_ERROR;
($res, $reason) = check_login($r, $dbh, $password); ($res, $reason) = check_login($r, $dbh, $password, $cfg);
$dbh->disconnect(); $dbh->disconnect();
# Store the cache key for latter use # Store the cache key for latter use
@ -376,10 +392,14 @@ sub authen_handler {
sub check_login { sub check_login {
my ($r, $dbh, $password) = @_; my ($r, $dbh, $password, $cfg) = @_;
my $user = $r->user; my $user = $r->user;
my ($hashed_password, $status, $auth_source_id, $salt) = $dbh->selectrow_array('SELECT hashed_password, status, auth_source_id, salt FROM users WHERE login = ?', undef, $user) my ($hashed_password, $status, $auth_source_id, $salt, $id, $firstname, $lastname, $email_address) =
$dbh->selectrow_array('SELECT users.hashed_password, users.status, users.auth_source_id, users.salt, users.id, users.firstname, users.lastname, email_addresses.address
FROM users
LEFT JOIN email_addresses on (email_addresses.user_id=users.id and email_addresses.is_default=1)
WHERE users.login = ?', undef, $user)
or return (AUTH_REQUIRED, "unknown user '$user'"); or return (AUTH_REQUIRED, "unknown user '$user'");
# Check password # Check password
@ -422,7 +442,16 @@ sub check_login {
# Password is ok, check if account if locked # Password is ok, check if account if locked
return (FORBIDDEN, "inactive account: '$user'") unless $status == 1; return (FORBIDDEN, "inactive account: '$user'") unless $status == 1;
$r->log->debug("successfully authenticated as active redmine user '$user'"); if ($cfg->{SetUserAttributes}) {
if (defined $email_address) {
$r->subprocess_env->set("REDMINE_DEFAULT_EMAIL_ADDRESS" => $email_address);
} else {
$r->subprocess_env->set("REDMINE_DEFAULT_EMAIL_ADDRESS" => "");
}
$r->subprocess_env->set("REDMINE_FIRSTNAME" => $firstname);
$r->subprocess_env->set("REDMINE_LASTNAME" => $lastname);
$r->log->debug("successfully authenticated as active redmine user '$user'");
}
# Everything's ok # Everything's ok
return OK; return OK;
@ -449,7 +478,23 @@ sub authz_handler {
my ($identifier, $project_id, $is_public, $status); my ($identifier, $project_id, $is_public, $status);
if($identifier = $cfg->{Project}) { if($cfg->{RepositoryType} eq 'None') {
$identifier = $cfg->{Project};
if(!$cfg->{Project}) {
return FORBIDDEN;
}
($project_id, $is_public, $status) = $dbh->selectrow_array(
"SELECT p.id, p.is_public, p.status
FROM projects p
WHERE p.identifier = ?",
undef, $identifier
);
unless(defined $project_id) {
$r->log_reason("No matching project for ${identifier}");
return NOT_FOUND;
}
} elsif($identifier = $cfg->{Project}) {
($project_id, $is_public, $status) = $dbh->selectrow_array( ($project_id, $is_public, $status) = $dbh->selectrow_array(
"SELECT p.id, p.is_public, p.status "SELECT p.id, p.is_public, p.status
FROM projects p JOIN repositories r ON (p.id = r.project_id) FROM projects p JOIN repositories r ON (p.id = r.project_id)
@ -530,6 +575,7 @@ sub authz_handler {
# Put successful credentials in cache # Put successful credentials in cache
if(my $cache_key = $r->pnotes("RedmineCacheKey")) { if(my $cache_key = $r->pnotes("RedmineCacheKey")) {
cache_set($r, $cache_key); cache_set($r, $cache_key);
attributes_cache_set($r, $cache_key);
} }
} else { } else {
@ -603,7 +649,10 @@ sub connect_database {
my $r = shift; my $r = shift;
my $cfg = get_config($r); my $cfg = get_config($r);
my $dbh = DBI->connect($cfg->{DSN}, $cfg->{DbUser}, $cfg->{DbPass}) my $dbh = DBI->connect($cfg->{DSN}, $cfg->{DbUser}, $cfg->{DbPass}, {
pg_enable_utf8 => 1,
mysql_enable_utf8 => 1,
})
or $r->log->error("Connection to database failed: $DBI::errstr."); or $r->log->error("Connection to database failed: $DBI::errstr.");
return $dbh; return $dbh;
@ -641,6 +690,29 @@ sub cache_get {
return 1; return 1;
} }
sub attributes_cache_get {
my($r, $key) = @_;
my $cfg = get_config($r);
return unless $cfg->{CacheCredsMax} && $cfg->{AttributesCacheCreds};
my $cache_text = $cfg->{AttributesCacheCreds}->get($key)
or return 0;
my($time, $email_address, $firstname, $lastname) = split(":", $cache_text);
if($cfg->{CacheCredsMaxAge} && ($r->request_time - $time) > $cfg->{CacheCredsMaxAge}) {
$cfg->{AttributesCacheCreds}->unset($key);
$cfg->{AttributesCacheCredsCount}--;
return 0;
}
$r->subprocess_env->set("REDMINE_DEFAULT_EMAIL_ADDRESS", $email_address . "");
$r->subprocess_env->set("REDMINE_FIRSTNAME", $firstname . "");
$r->subprocess_env->set("REDMINE_LASTNAME", $lastname . "");
return 1;
}
# put credentials in cache # put credentials in cache
sub cache_set { sub cache_set {
my($r, $key) = @_; my($r, $key) = @_;
@ -661,6 +733,30 @@ sub cache_set {
$cfg->{CacheCredsCount}++; $cfg->{CacheCredsCount}++;
} }
sub attributes_cache_set {
my($r, $key) = @_;
my $cfg = get_config($r);
return unless $cfg->{CacheCredsMax};
unless($cfg->{AttributesCacheCreds}) {
$cfg->{AttributesCachePool} = APR::Pool->new;
$cfg->{AttributesCacheCreds} = APR::Table::make($cfg->{AttributesCachePool}, $cfg->{CacheCredsMax});
}
if($cfg->{AttributesCacheCredsCount} >= $cfg->{CacheCredsMax}) {
$cfg->{AttributesCacheCreds}->clear;
$cfg->{AttributesCacheCredsCount} = 0;
}
my $cache_text = join(":", $r->request_time,
$r->subprocess_env->get("REDMINE_DEFAULT_EMAIL_ADDRESS"),
$r->subprocess_env->get("REDMINE_FIRSTNAME"),
$r->subprocess_env->get("REDMINE_LASTNAME"),
);
$cfg->{AttributesCacheCreds}->set($key, $cache_text);
$cfg->{AttributesCacheCredsCount}++;
}
1; 1;