forked from lix-project/hydra
Merge pull request #1151 from DeterminateSystems/ldap-tests-inc-mapping
ldap: support configurable roles with backwards compatibility
This commit is contained in:
commit
1124230d9f
19 changed files with 725 additions and 119 deletions
|
@ -105,50 +105,116 @@ in the hydra configuration file, as below:
|
||||||
Using LDAP as authentication backend (optional)
|
Using LDAP as authentication backend (optional)
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
Instead of using Hydra\'s built-in user management you can optionally
|
Instead of using Hydra's built-in user management you can optionally
|
||||||
use LDAP to manage roles and users.
|
use LDAP to manage roles and users.
|
||||||
|
|
||||||
The `hydra-server` accepts the environment variable
|
This is configured by defining the `<ldap>` block in the configuration file.
|
||||||
*HYDRA\_LDAP\_CONFIG*. The value of the variable should point to a valid
|
In this block it's possible to configure the authentication plugin in the
|
||||||
YAML file containing the Catalyst LDAP configuration. The format of the
|
`<config>` block. All options are directly passed to `Catalyst::Authentication::Store::LDAP`.
|
||||||
configuration file is describe in the
|
The documentation for the available settings can be found [here]
|
||||||
[*Catalyst::Authentication::Store::LDAP*
|
(https://metacpan.org/pod/Catalyst::Authentication::Store::LDAP#CONFIGURATION-OPTIONS).
|
||||||
documentation](https://metacpan.org/pod/Catalyst::Authentication::Store::LDAP#CONFIGURATION-OPTIONS).
|
|
||||||
An example is given below.
|
|
||||||
|
|
||||||
Roles can be assigned to users based on their LDAP group membership
|
Note that the bind password (if needed) should be supplied as an included file to
|
||||||
(*use\_roles: 1* in the below example). For a user to have the role
|
prevent it from leaking to the Nix store.
|
||||||
*admin* assigned to them they should be in the group *hydra\_admin*. In
|
|
||||||
general any LDAP group of the form *hydra\_some\_role* (notice the
|
|
||||||
*hydra\_* prefix) will work.
|
|
||||||
|
|
||||||
credential:
|
Roles can be assigned to users based on their LDAP group membership. For this
|
||||||
class: Password
|
to work *use\_roles = 1* needs to be defined for the authentication plugin.
|
||||||
password_field: password
|
LDAP groups can then be mapped to Hydra roles using the `<role_mapping>` block.
|
||||||
password_type: self_check
|
|
||||||
store:
|
Example configuration:
|
||||||
class: LDAP
|
```
|
||||||
ldap_server: localhost
|
<ldap>
|
||||||
ldap_server_options.timeout: 30
|
<config>
|
||||||
binddn: "cn=root,dc=example"
|
<credential>
|
||||||
bindpw: notapassword
|
class = Password
|
||||||
start_tls: 0
|
password_field = password
|
||||||
start_tls_options:
|
password_type = self_check
|
||||||
verify: none
|
</credential>
|
||||||
user_basedn: "ou=users,dc=example"
|
<store>
|
||||||
user_filter: "(&(objectClass=inetOrgPerson)(cn=%s))"
|
class = LDAP
|
||||||
user_scope: one
|
ldap_server = localhost
|
||||||
user_field: cn
|
<ldap_server_options>
|
||||||
user_search_options:
|
timeout = 30
|
||||||
deref: always
|
</ldap_server_options>
|
||||||
use_roles: 1
|
binddn = "cn=root,dc=example"
|
||||||
role_basedn: "ou=groups,dc=example"
|
include ldap-password.conf
|
||||||
role_filter: "(&(objectClass=groupOfNames)(member=%s))"
|
start_tls = 0
|
||||||
role_scope: one
|
<start_tls_options>
|
||||||
role_field: cn
|
verify = none
|
||||||
role_value: dn
|
</start_tls_options>
|
||||||
role_search_options:
|
user_basedn = "ou=users,dc=example"
|
||||||
deref: always
|
user_filter = "(&(objectClass=inetOrgPerson)(cn=%s))"
|
||||||
|
user_scope = one
|
||||||
|
user_field = cn
|
||||||
|
<user_search_options>
|
||||||
|
deref = always
|
||||||
|
</user_search_options>
|
||||||
|
# Important for role mappings to work:
|
||||||
|
use_roles = 1
|
||||||
|
role_basedn = "ou=groups,dc=example"
|
||||||
|
role_filter = "(&(objectClass=groupOfNames)(member=%s))"
|
||||||
|
role_scope = one
|
||||||
|
role_field = cn
|
||||||
|
role_value = dn
|
||||||
|
<role_search_options>
|
||||||
|
deref = always
|
||||||
|
</role_search_options>
|
||||||
|
</config>
|
||||||
|
<role_mapping>
|
||||||
|
# Make all users in the hydra_admin group Hydra admins
|
||||||
|
hydra_admin = admin
|
||||||
|
# Allow all users in the dev group to restart jobs and cancel builds
|
||||||
|
dev = restart-jobs
|
||||||
|
dev = cancel-builds
|
||||||
|
</role_mapping>
|
||||||
|
</ldap>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, place the password to your LDAP server in `/var/lib/hydra/ldap-password.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
bindpw = the-ldap-password
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging LDAP
|
||||||
|
|
||||||
|
Set the `debug` parameter under `ldap.config.ldap_server_options.debug`:
|
||||||
|
|
||||||
|
```
|
||||||
|
<ldap>
|
||||||
|
<config>
|
||||||
|
<store>
|
||||||
|
<ldap_server_options>
|
||||||
|
debug = 2
|
||||||
|
</ldap_server_options>
|
||||||
|
</store>
|
||||||
|
</config>
|
||||||
|
</ldap>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Legacy LDAP Configuration
|
||||||
|
|
||||||
|
Hydra used to load the LDAP configuration from a YAML file in the
|
||||||
|
`HYDRA_LDAP_CONFIG` environment variable. This behavior is deperecated
|
||||||
|
and will be removed.
|
||||||
|
|
||||||
|
When Hydra uses the deprecated YAML file, Hydra applies the following
|
||||||
|
default role mapping:
|
||||||
|
|
||||||
|
```
|
||||||
|
<ldap>
|
||||||
|
<role_mapping>
|
||||||
|
hydra_admin = admin
|
||||||
|
hydra_bump-to-front = bump-to-front
|
||||||
|
hydra_cancel-build = cancel-build
|
||||||
|
hydra_create-projects = create-projects
|
||||||
|
hydra_restart-jobs = restart-jobs
|
||||||
|
</role_mapping>
|
||||||
|
</ldap>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that configuring both the LDAP parameters in the hydra.conf and via
|
||||||
|
the environment variable is a fatal error.
|
||||||
|
|
||||||
Embedding Extra HTML
|
Embedding Extra HTML
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
@ -521,8 +521,8 @@
|
||||||
TextDiff
|
TextDiff
|
||||||
TextTable
|
TextTable
|
||||||
UUID4Tiny
|
UUID4Tiny
|
||||||
XMLSimple
|
|
||||||
YAML
|
YAML
|
||||||
|
XMLSimple
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use parent 'Catalyst';
|
||||||
use Moose;
|
use Moose;
|
||||||
use Hydra::Plugin;
|
use Hydra::Plugin;
|
||||||
use Hydra::Model::DB;
|
use Hydra::Model::DB;
|
||||||
|
use Hydra::Config qw(getLDAPConfigAmbient);
|
||||||
use Catalyst::Runtime '5.70';
|
use Catalyst::Runtime '5.70';
|
||||||
use Catalyst qw/ConfigLoader
|
use Catalyst qw/ConfigLoader
|
||||||
Static::Simple
|
Static::Simple
|
||||||
|
@ -19,7 +20,6 @@ use Catalyst qw/ConfigLoader
|
||||||
PrometheusTiny/,
|
PrometheusTiny/,
|
||||||
'-Log=warn,fatal,error';
|
'-Log=warn,fatal,error';
|
||||||
use CatalystX::RoleApplicator;
|
use CatalystX::RoleApplicator;
|
||||||
use YAML qw(LoadFile);
|
|
||||||
use Path::Class 'file';
|
use Path::Class 'file';
|
||||||
|
|
||||||
our $VERSION = '0.01';
|
our $VERSION = '0.01';
|
||||||
|
@ -43,9 +43,7 @@ __PACKAGE__->config(
|
||||||
role_field => "role",
|
role_field => "role",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ldap => $ENV{'HYDRA_LDAP_CONFIG'} ? LoadFile(
|
ldap => getLDAPConfigAmbient()->{'config'}
|
||||||
file($ENV{'HYDRA_LDAP_CONFIG'})
|
|
||||||
) : undef
|
|
||||||
},
|
},
|
||||||
'Plugin::ConfigLoader' => {
|
'Plugin::ConfigLoader' => {
|
||||||
driver => {
|
driver => {
|
||||||
|
|
|
@ -2,7 +2,165 @@ package Hydra::Config;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
use Config::General;
|
||||||
|
use List::SomeUtils qw(none);
|
||||||
|
use YAML qw(LoadFile);
|
||||||
|
|
||||||
|
our @ISA = qw(Exporter);
|
||||||
|
our @EXPORT = qw(
|
||||||
|
getHydraConfig
|
||||||
|
getLDAPConfig
|
||||||
|
getLDAPConfigAmbient
|
||||||
|
);
|
||||||
|
|
||||||
our %configGeneralOpts = (-UseApacheInclude => 1, -IncludeAgain => 1, -IncludeRelative => 1);
|
our %configGeneralOpts = (-UseApacheInclude => 1, -IncludeAgain => 1, -IncludeRelative => 1);
|
||||||
|
|
||||||
|
my $hydraConfigCache;
|
||||||
|
|
||||||
|
sub getHydraConfig {
|
||||||
|
return $hydraConfigCache if defined $hydraConfigCache;
|
||||||
|
|
||||||
|
my $conf;
|
||||||
|
|
||||||
|
if ($ENV{"HYDRA_CONFIG"}) {
|
||||||
|
$conf = $ENV{"HYDRA_CONFIG"};
|
||||||
|
} else {
|
||||||
|
require Hydra::Model::DB;
|
||||||
|
$conf = Hydra::Model::DB::getHydraPath() . "/hydra.conf"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (-f $conf) {
|
||||||
|
$hydraConfigCache = loadConfig($conf);
|
||||||
|
} else {
|
||||||
|
$hydraConfigCache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hydraConfigCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub loadConfig {
|
||||||
|
my ($sourceFile) = @_;
|
||||||
|
|
||||||
|
my %opts = (%configGeneralOpts, -ConfigFile => $sourceFile);
|
||||||
|
|
||||||
|
return { Config::General->new(%opts)->getall };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_ldap_in_legacy_mode {
|
||||||
|
my ($config, %env) = @_;
|
||||||
|
|
||||||
|
my $legacy_defined = defined $env{"HYDRA_LDAP_CONFIG"};
|
||||||
|
|
||||||
|
if (defined $config->{"ldap"}) {
|
||||||
|
if ($legacy_defined) {
|
||||||
|
die "The legacy environment variable HYDRA_LDAP_CONFIG is set, but config is also specified in hydra.conf. Please unset the environment variable.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} elsif ($legacy_defined) {
|
||||||
|
warn "Hydra is configured to use LDAP via the HYDRA_LDAP_CONFIG, a deprecated method. Please see the docs about configuring LDAP in the hydra.conf.";
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub getLDAPConfigAmbient {
|
||||||
|
return getLDAPConfig(getHydraConfig(), %ENV);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub getLDAPConfig {
|
||||||
|
my ($config, %env) = @_;
|
||||||
|
|
||||||
|
my $ldap_config;
|
||||||
|
|
||||||
|
if (is_ldap_in_legacy_mode($config, %env)) {
|
||||||
|
$ldap_config = get_legacy_ldap_config($env{"HYDRA_LDAP_CONFIG"});
|
||||||
|
} else {
|
||||||
|
$ldap_config = $config->{"ldap"};
|
||||||
|
}
|
||||||
|
|
||||||
|
$ldap_config->{"role_mapping"} = normalize_ldap_role_mappings($ldap_config->{"role_mapping"});
|
||||||
|
|
||||||
|
return $ldap_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_legacy_ldap_config {
|
||||||
|
my ($ldap_yaml_file) = @_;
|
||||||
|
|
||||||
|
return {
|
||||||
|
config => LoadFile($ldap_yaml_file),
|
||||||
|
role_mapping => {
|
||||||
|
"hydra_admin" => [ "admin" ],
|
||||||
|
"hydra_bump-to-front" => [ "bump-to-front" ],
|
||||||
|
"hydra_cancel-build" => [ "cancel-build" ],
|
||||||
|
"hydra_create-projects" => [ "create-projects" ],
|
||||||
|
"hydra_restart-jobs" => [ "restart-jobs" ],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub normalize_ldap_role_mappings {
|
||||||
|
my ($input_map) = @_;
|
||||||
|
|
||||||
|
my $mapping = {};
|
||||||
|
|
||||||
|
my @errors;
|
||||||
|
|
||||||
|
for my $group (keys %{$input_map}) {
|
||||||
|
my $input = $input_map->{$group};
|
||||||
|
|
||||||
|
if (ref $input eq "ARRAY") {
|
||||||
|
$mapping->{$group} = $input;
|
||||||
|
} elsif (ref $input eq "") {
|
||||||
|
$mapping->{$group} = [ $input ];
|
||||||
|
} else {
|
||||||
|
push @errors, "On group '$group': the value is of type ${\ref $input}. Only strings and lists are acceptable.";
|
||||||
|
$mapping->{$group} = [ ];
|
||||||
|
}
|
||||||
|
|
||||||
|
eval {
|
||||||
|
validate_roles($mapping->{$group});
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
push @errors, "On group '$group': $@";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@errors) {
|
||||||
|
die "Failed to normalize LDAP role mappings:\n" . (join "\n", @errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validate_roles {
|
||||||
|
my ($roles) = @_;
|
||||||
|
|
||||||
|
my @invalid;
|
||||||
|
my $valid = valid_roles();
|
||||||
|
|
||||||
|
for my $role (@$roles) {
|
||||||
|
if (none { $_ eq $role } @$valid) {
|
||||||
|
push @invalid, "'$role'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@invalid) {
|
||||||
|
die "Invalid roles: ${\join ', ', @invalid}. Valid roles are: ${\join ', ', @$valid}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub valid_roles {
|
||||||
|
return [
|
||||||
|
"admin",
|
||||||
|
"bump-to-front",
|
||||||
|
"cancel-build",
|
||||||
|
"create-projects",
|
||||||
|
"restart-jobs",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -7,6 +7,7 @@ use base 'Hydra::Base::Controller::REST';
|
||||||
use File::Slurper qw(read_text);
|
use File::Slurper qw(read_text);
|
||||||
use Crypt::RandPasswd;
|
use Crypt::RandPasswd;
|
||||||
use Digest::SHA1 qw(sha1_hex);
|
use Digest::SHA1 qw(sha1_hex);
|
||||||
|
use Hydra::Config qw(getLDAPConfigAmbient);
|
||||||
use Hydra::Helper::Nix;
|
use Hydra::Helper::Nix;
|
||||||
use Hydra::Helper::CatalystUtils;
|
use Hydra::Helper::CatalystUtils;
|
||||||
use Hydra::Helper::Email;
|
use Hydra::Helper::Email;
|
||||||
|
@ -56,10 +57,10 @@ sub logout_POST {
|
||||||
|
|
||||||
sub doLDAPLogin {
|
sub doLDAPLogin {
|
||||||
my ($self, $c, $username) = @_;
|
my ($self, $c, $username) = @_;
|
||||||
|
|
||||||
my $user = $c->find_user({ username => $username });
|
my $user = $c->find_user({ username => $username });
|
||||||
my $LDAPUser = $c->find_user({ username => $username }, 'ldap');
|
my $LDAPUser = $c->find_user({ username => $username }, 'ldap');
|
||||||
my @LDAPRoles = grep { (substr $_, 0, 6) eq "hydra_" } $LDAPUser->roles;
|
my @LDAPRoles = $LDAPUser->roles;
|
||||||
|
my $role_mapping = getLDAPConfigAmbient()->{"role_mapping"};
|
||||||
|
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
$c->model('DB::Users')->create(
|
$c->model('DB::Users')->create(
|
||||||
|
@ -79,8 +80,13 @@ sub doLDAPLogin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$user->userroles->delete;
|
$user->userroles->delete;
|
||||||
if (@LDAPRoles) {
|
foreach my $ldap_role (@LDAPRoles) {
|
||||||
$user->userroles->create({ role => (substr $_, 6) }) for @LDAPRoles;
|
if (defined($role_mapping->{$ldap_role})) {
|
||||||
|
my $roles = $role_mapping->{$ldap_role};
|
||||||
|
for my $mapped_role (@$roles) {
|
||||||
|
$user->userroles->create({ role => $mapped_role });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$c->set_authenticated($user);
|
$c->set_authenticated($user);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ use warnings;
|
||||||
use Exporter;
|
use Exporter;
|
||||||
use File::Path;
|
use File::Path;
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
use Config::General;
|
|
||||||
use Hydra::Config;
|
use Hydra::Config;
|
||||||
use Hydra::Helper::CatalystUtils;
|
use Hydra::Helper::CatalystUtils;
|
||||||
use Hydra::Model::DB;
|
use Hydra::Model::DB;
|
||||||
|
@ -49,24 +48,6 @@ sub getHydraHome {
|
||||||
return $dir;
|
return $dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
my $hydraConfig;
|
|
||||||
|
|
||||||
sub getHydraConfig {
|
|
||||||
return $hydraConfig if defined $hydraConfig;
|
|
||||||
my $conf = $ENV{"HYDRA_CONFIG"} || (Hydra::Model::DB::getHydraPath . "/hydra.conf");
|
|
||||||
my %opts = (%Hydra::Config::configGeneralOpts, -ConfigFile => $conf);
|
|
||||||
if (-f $conf) {
|
|
||||||
my %h = Config::General->new(%opts)->getall;
|
|
||||||
|
|
||||||
$hydraConfig = \%h;
|
|
||||||
} else {
|
|
||||||
$hydraConfig = {};
|
|
||||||
}
|
|
||||||
return $hydraConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Return hash of statsd configuration of the following shape:
|
# Return hash of statsd configuration of the following shape:
|
||||||
# (
|
# (
|
||||||
# host => string,
|
# host => string,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use Data::Dump qw(dump);
|
||||||
use Digest::SHA qw(sha256_hex);
|
use Digest::SHA qw(sha256_hex);
|
||||||
use Encode;
|
use Encode;
|
||||||
use File::Slurper qw(read_text);
|
use File::Slurper qw(read_text);
|
||||||
|
use Hydra::Config;
|
||||||
use Hydra::Helper::AddBuilds;
|
use Hydra::Helper::AddBuilds;
|
||||||
use Hydra::Helper::CatalystUtils;
|
use Hydra::Helper::CatalystUtils;
|
||||||
use Hydra::Helper::Email;
|
use Hydra::Helper::Email;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use utf8;
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
use Time::HiRes qw( gettimeofday tv_interval );
|
use Time::HiRes qw( gettimeofday tv_interval );
|
||||||
use HTTP::Server::PSGI;
|
use HTTP::Server::PSGI;
|
||||||
|
use Hydra::Config;
|
||||||
use Hydra::Event;
|
use Hydra::Event;
|
||||||
use Hydra::Event::BuildFinished;
|
use Hydra::Event::BuildFinished;
|
||||||
use Hydra::Helper::AddBuilds;
|
use Hydra::Helper::AddBuilds;
|
||||||
|
|
|
@ -9,6 +9,7 @@ use Net::Amazon::S3;
|
||||||
use Net::Amazon::S3::Client;
|
use Net::Amazon::S3::Client;
|
||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
use Nix::Store;
|
use Nix::Store;
|
||||||
|
use Hydra::Config;
|
||||||
use Hydra::Model::DB;
|
use Hydra::Model::DB;
|
||||||
use Hydra::Helper::Nix;
|
use Hydra::Helper::Nix;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
use Hydra::Config;
|
||||||
use Hydra::Helper::Nix;
|
use Hydra::Helper::Nix;
|
||||||
use Net::Statsd;
|
use Net::Statsd;
|
||||||
use File::Slurper qw(read_text);
|
use File::Slurper qw(read_text);
|
||||||
|
|
|
@ -6,6 +6,7 @@ use File::Path;
|
||||||
use File::stat;
|
use File::stat;
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
use Nix::Store;
|
use Nix::Store;
|
||||||
|
use Hydra::Config;
|
||||||
use Hydra::Schema;
|
use Hydra::Schema;
|
||||||
use Hydra::Helper::Nix;
|
use Hydra::Helper::Nix;
|
||||||
use Hydra::Model::DB;
|
use Hydra::Model::DB;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Setup;
|
use Setup;
|
||||||
|
use Hydra::Config;
|
||||||
|
|
||||||
my %ctx = test_init(hydra_config => q|
|
my %ctx = test_init(hydra_config => q|
|
||||||
<hydra_notify>
|
<hydra_notify>
|
||||||
|
@ -14,7 +15,7 @@ my %ctx = test_init(hydra_config => q|
|
||||||
require Hydra::Helper::Nix;
|
require Hydra::Helper::Nix;
|
||||||
use Test2::V0;
|
use Test2::V0;
|
||||||
|
|
||||||
is(Hydra::Helper::Nix::getHydraNotifyPrometheusConfig(Hydra::Helper::Nix::getHydraConfig()), {
|
is(Hydra::Helper::Nix::getHydraNotifyPrometheusConfig(getHydraConfig()), {
|
||||||
'listen_address' => "127.0.0.1",
|
'listen_address' => "127.0.0.1",
|
||||||
'port' => 9199
|
'port' => 9199
|
||||||
}, "Reading specific configuration from the hydra.conf works");
|
}, "Reading specific configuration from the hydra.conf works");
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Setup;
|
use Setup;
|
||||||
|
use Hydra::Config;
|
||||||
|
use Test2::V0;
|
||||||
|
|
||||||
my %ctx = test_init(
|
my %ctx = test_init(
|
||||||
use_external_destination_store => 0,
|
use_external_destination_store => 0,
|
||||||
|
@ -17,10 +19,7 @@ write_file($ctx{'tmpdir'} . "/bar.conf", q|
|
||||||
bar = baz
|
bar = baz
|
||||||
|);
|
|);
|
||||||
|
|
||||||
require Hydra::Helper::Nix;
|
is(getHydraConfig(), {
|
||||||
use Test2::V0;
|
|
||||||
|
|
||||||
is(Hydra::Helper::Nix::getHydraConfig(), {
|
|
||||||
foo => { bar => "baz" }
|
foo => { bar => "baz" }
|
||||||
}, "Nested includes work.");
|
}, "Nested includes work.");
|
||||||
|
|
||||||
|
|
242
t/Hydra/Config/ldap_role_map.t
Normal file
242
t/Hydra/Config/ldap_role_map.t
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Setup;
|
||||||
|
use Hydra::Config;
|
||||||
|
use Test2::V0;
|
||||||
|
|
||||||
|
my $tmpdir = File::Temp->newdir();
|
||||||
|
my $cfgfile = "$tmpdir/conf";
|
||||||
|
my $scratchCfgFile = "$tmpdir/hydra.scratch.conf";
|
||||||
|
|
||||||
|
my $ldapInHydraConfFile = "$tmpdir/hydra.empty.conf";
|
||||||
|
write_file($ldapInHydraConfFile, <<CONF);
|
||||||
|
<ldap>
|
||||||
|
<config>
|
||||||
|
<credential>
|
||||||
|
class = Password
|
||||||
|
</credential>
|
||||||
|
</config>
|
||||||
|
<role_mapping>
|
||||||
|
hydra_admin = admin
|
||||||
|
hydra_one_group_many_roles = create-projects
|
||||||
|
hydra_one_group_many_roles = cancel-build
|
||||||
|
</role_mapping>
|
||||||
|
</ldap>
|
||||||
|
CONF
|
||||||
|
my $ldapInHydraConf = Hydra::Config::loadConfig($ldapInHydraConfFile);
|
||||||
|
|
||||||
|
my $emptyHydraConfFile = "$tmpdir/hydra.empty.conf";
|
||||||
|
write_file($emptyHydraConfFile, "");
|
||||||
|
my $emptyHydraConf = Hydra::Config::loadConfig($emptyHydraConfFile);
|
||||||
|
|
||||||
|
my $ldapYamlFile = "$tmpdir/ldap.yaml";
|
||||||
|
write_file($ldapYamlFile, <<YAML);
|
||||||
|
credential:
|
||||||
|
class: Password
|
||||||
|
YAML
|
||||||
|
|
||||||
|
subtest "getLDAPConfig" => sub {
|
||||||
|
subtest "No ldap section and an env var gets us legacy data" => sub {
|
||||||
|
like(
|
||||||
|
warning {
|
||||||
|
is(
|
||||||
|
Hydra::Config::getLDAPConfig(
|
||||||
|
$emptyHydraConf,
|
||||||
|
( HYDRA_LDAP_CONFIG => $ldapYamlFile )
|
||||||
|
),
|
||||||
|
{
|
||||||
|
config => {
|
||||||
|
credential => {
|
||||||
|
class => "Password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
role_mapping => {
|
||||||
|
"hydra_admin" => [ "admin" ],
|
||||||
|
"hydra_bump-to-front" => [ "bump-to-front" ],
|
||||||
|
"hydra_cancel-build" => [ "cancel-build" ],
|
||||||
|
"hydra_create-projects" => [ "create-projects" ],
|
||||||
|
"hydra_restart-jobs" => [ "restart-jobs" ],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"The empty file and set env var make legacy mode active."
|
||||||
|
);
|
||||||
|
},
|
||||||
|
qr/configured to use LDAP via the HYDRA_LDAP_CONFIG/,
|
||||||
|
"Having the environment variable set warns."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "An ldap section and no env var gets us normalized data" => sub {
|
||||||
|
is(
|
||||||
|
warns {
|
||||||
|
is(
|
||||||
|
Hydra::Config::getLDAPConfig(
|
||||||
|
$ldapInHydraConf,
|
||||||
|
()
|
||||||
|
),
|
||||||
|
{
|
||||||
|
config => {
|
||||||
|
credential => {
|
||||||
|
class => "Password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
role_mapping => {
|
||||||
|
"hydra_admin" => [ "admin" ],
|
||||||
|
"hydra_one_group_many_roles" => [ "create-projects", "cancel-build" ],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"The empty file and set env var make legacy mode active."
|
||||||
|
);
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
"No warnings are issued for non-legacy LDAP support."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
subtest "is_ldap_in_legacy_mode" => sub {
|
||||||
|
subtest "With the environment variable set and an empty hydra.conf" => sub {
|
||||||
|
like(
|
||||||
|
warning {
|
||||||
|
is(
|
||||||
|
Hydra::Config::is_ldap_in_legacy_mode(
|
||||||
|
$emptyHydraConf,
|
||||||
|
( HYDRA_LDAP_CONFIG => $ldapYamlFile )
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
"The empty file and set env var make legacy mode active."
|
||||||
|
);
|
||||||
|
},
|
||||||
|
qr/configured to use LDAP via the HYDRA_LDAP_CONFIG/,
|
||||||
|
"Having the environment variable set warns."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "With the environment variable set and LDAP specified in hydra.conf" => sub {
|
||||||
|
like(
|
||||||
|
dies {
|
||||||
|
Hydra::Config::is_ldap_in_legacy_mode(
|
||||||
|
$ldapInHydraConf,
|
||||||
|
( HYDRA_LDAP_CONFIG => $ldapYamlFile )
|
||||||
|
);
|
||||||
|
},
|
||||||
|
qr/HYDRA_LDAP_CONFIG is set, but config is also specified in hydra\.conf/,
|
||||||
|
"Having the environment variable set dies to avoid misconfiguration."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "Without the environment variable set and an empty hydra.conf" => sub {
|
||||||
|
is(
|
||||||
|
warns {
|
||||||
|
is(
|
||||||
|
Hydra::Config::is_ldap_in_legacy_mode(
|
||||||
|
$emptyHydraConf,
|
||||||
|
()
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
"The empty file and unset env var means non-legacy."
|
||||||
|
);
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
"We should receive zero warnings."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "Without the environment variable set and LDAP specified in hydra.conf" => sub {
|
||||||
|
is(
|
||||||
|
warns {
|
||||||
|
is(
|
||||||
|
Hydra::Config::is_ldap_in_legacy_mode(
|
||||||
|
$ldapInHydraConf,
|
||||||
|
()
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
"The empty file and unset env var means non-legacy."
|
||||||
|
);
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
"We should receive zero warnings."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "get_legacy_ldap_config" => sub {
|
||||||
|
is(
|
||||||
|
Hydra::Config::get_legacy_ldap_config($ldapYamlFile),
|
||||||
|
{
|
||||||
|
config => {
|
||||||
|
credential => {
|
||||||
|
class => "Password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
role_mapping => {
|
||||||
|
"hydra_admin" => [ "admin" ],
|
||||||
|
"hydra_bump-to-front" => [ "bump-to-front" ],
|
||||||
|
"hydra_cancel-build" => [ "cancel-build" ],
|
||||||
|
"hydra_create-projects" => [ "create-projects" ],
|
||||||
|
"hydra_restart-jobs" => [ "restart-jobs" ],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Legacy, default role maps are applied."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "validate_roles" => sub {
|
||||||
|
ok(Hydra::Config::validate_roles([]), "An empty list is valid");
|
||||||
|
ok(Hydra::Config::validate_roles(Hydra::Config::valid_roles()), "All current roles are valid.");
|
||||||
|
like(
|
||||||
|
dies { Hydra::Config::validate_roles([""]) },
|
||||||
|
qr/Invalid roles: ''./,
|
||||||
|
"Invalid roles are failing"
|
||||||
|
);
|
||||||
|
like(
|
||||||
|
dies { Hydra::Config::validate_roles(["foo", "bar"]) },
|
||||||
|
qr/Invalid roles: 'foo', 'bar'./,
|
||||||
|
"All the invalid roles are present in the error"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "normalize_ldap_role_mappings" => sub {
|
||||||
|
is(
|
||||||
|
Hydra::Config::normalize_ldap_role_mappings({}),
|
||||||
|
{},
|
||||||
|
"An empty input map is an empty output map."
|
||||||
|
);
|
||||||
|
|
||||||
|
is(
|
||||||
|
Hydra::Config::normalize_ldap_role_mappings({
|
||||||
|
hydra_admin => "admin",
|
||||||
|
hydra_one_group_many_roles => [ "create-projects", "bump-to-front" ],
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
hydra_admin => [ "admin" ],
|
||||||
|
hydra_one_group_many_roles => [ "create-projects", "bump-to-front" ],
|
||||||
|
},
|
||||||
|
"Lists and plain strings normalize to lists"
|
||||||
|
);
|
||||||
|
|
||||||
|
like(
|
||||||
|
dies{
|
||||||
|
Hydra::Config::normalize_ldap_role_mappings({
|
||||||
|
"group" => "invalid-role",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
qr/Failed to normalize.*Invalid roles.*invalid-role/s,
|
||||||
|
"Invalid roles fail to normalize."
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
like(
|
||||||
|
dies{
|
||||||
|
Hydra::Config::normalize_ldap_role_mappings({
|
||||||
|
"group" => { "nested" => "data" },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
qr/On group 'group':.* Only strings/,
|
||||||
|
"Invalid nesting fail to normalize."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -1,6 +1,7 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Setup;
|
use Setup;
|
||||||
|
use Hydra::Config;
|
||||||
|
|
||||||
my %ctx = test_init(hydra_config => q|
|
my %ctx = test_init(hydra_config => q|
|
||||||
<statsd>
|
<statsd>
|
||||||
|
@ -12,7 +13,7 @@ my %ctx = test_init(hydra_config => q|
|
||||||
require Hydra::Helper::Nix;
|
require Hydra::Helper::Nix;
|
||||||
use Test2::V0;
|
use Test2::V0;
|
||||||
|
|
||||||
is(Hydra::Helper::Nix::getStatsdConfig(Hydra::Helper::Nix::getHydraConfig()), {
|
is(Hydra::Helper::Nix::getStatsdConfig(getHydraConfig()), {
|
||||||
'host' => "foo.bar",
|
'host' => "foo.bar",
|
||||||
'port' => 18125
|
'port' => 18125
|
||||||
}, "Reading specific configuration from the hydra.conf works");
|
}, "Reading specific configuration from the hydra.conf works");
|
||||||
|
|
107
t/Hydra/Controller/User/ldap-legacy.t
Normal file
107
t/Hydra/Controller/User/ldap-legacy.t
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Setup;
|
||||||
|
use LDAPContext;
|
||||||
|
use Test2::V0;
|
||||||
|
use Catalyst::Test ();
|
||||||
|
use HTTP::Request::Common;
|
||||||
|
use JSON::MaybeXS;
|
||||||
|
|
||||||
|
my $ldap = LDAPContext->new(
|
||||||
|
root_password => "the-root-password",
|
||||||
|
);
|
||||||
|
my $users = {
|
||||||
|
unrelated => $ldap->add_user("unrelated_user"),
|
||||||
|
admin => $ldap->add_user("admin_user"),
|
||||||
|
not_admin => $ldap->add_user("not_admin_user"),
|
||||||
|
many_roles => $ldap->add_user("many_roles"),
|
||||||
|
};
|
||||||
|
|
||||||
|
$ldap->add_group("hydra_admin", $users->{"admin"}->{"username"});
|
||||||
|
$ldap->add_group("hydra-admin", $users->{"not_admin"}->{"username"});
|
||||||
|
|
||||||
|
$ldap->add_group("hydra_create-projects", $users->{"many_roles"}->{"username"});
|
||||||
|
$ldap->add_group("hydra_restart-jobs", $users->{"many_roles"}->{"username"});
|
||||||
|
$ldap->add_group("hydra_bump-to-front", $users->{"many_roles"}->{"username"});
|
||||||
|
$ldap->add_group("hydra_cancel-build", $users->{"many_roles"}->{"username"});
|
||||||
|
|
||||||
|
my $hydra_ldap_config = "${\$ldap->tmpdir()}/hydra_ldap_config.yaml";
|
||||||
|
LDAPContext::write_file($hydra_ldap_config, <<YAML);
|
||||||
|
credential:
|
||||||
|
class: Password
|
||||||
|
password_field: password
|
||||||
|
password_type: self_check
|
||||||
|
store:
|
||||||
|
class: LDAP
|
||||||
|
ldap_server: "${\$ldap->server_url()}"
|
||||||
|
ldap_server_options:
|
||||||
|
timeout: 30
|
||||||
|
debug: 0
|
||||||
|
binddn: "cn=root,dc=example"
|
||||||
|
bindpw: the-root-password
|
||||||
|
start_tls: 0
|
||||||
|
start_tls_options:
|
||||||
|
verify: none
|
||||||
|
user_basedn: "ou=users,dc=example"
|
||||||
|
user_filter: "(&(objectClass=inetOrgPerson)(cn=%s))"
|
||||||
|
user_scope: one
|
||||||
|
user_field: cn
|
||||||
|
user_search_options:
|
||||||
|
deref: always
|
||||||
|
use_roles: 1
|
||||||
|
role_basedn: "ou=groups,dc=example"
|
||||||
|
role_filter: "(&(objectClass=groupOfNames)(member=%s))"
|
||||||
|
role_scope: one
|
||||||
|
role_field: cn
|
||||||
|
role_value: dn
|
||||||
|
role_search_options:
|
||||||
|
deref: always
|
||||||
|
YAML
|
||||||
|
|
||||||
|
$ENV{'HYDRA_LDAP_CONFIG'} = $hydra_ldap_config;
|
||||||
|
my $ctx = test_context();
|
||||||
|
|
||||||
|
Catalyst::Test->import('Hydra');
|
||||||
|
|
||||||
|
subtest "Valid login attempts" => sub {
|
||||||
|
my %users_to_roles = (
|
||||||
|
unrelated => [],
|
||||||
|
admin => ["admin"],
|
||||||
|
not_admin => [],
|
||||||
|
many_roles => [ "create-projects", "restart-jobs", "bump-to-front", "cancel-build" ],
|
||||||
|
);
|
||||||
|
for my $username (keys %users_to_roles) {
|
||||||
|
my $user = $users->{$username};
|
||||||
|
my $roles = $users_to_roles{$username};
|
||||||
|
|
||||||
|
subtest "Verifying $username" => sub {
|
||||||
|
my $req = request(POST '/login',
|
||||||
|
Referer => 'http://localhost/',
|
||||||
|
Accept => 'application/json',
|
||||||
|
Content => {
|
||||||
|
username => $user->{"username"},
|
||||||
|
password => $user->{"password"}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
is($req->code, 302, "The login redirects");
|
||||||
|
my $data = decode_json($req->content());
|
||||||
|
is($data->{"username"}, $user->{"username"}, "Username matches");
|
||||||
|
is($data->{"emailaddress"}, $user->{"email"}, "Email matches");
|
||||||
|
is([sort @{$data->{"userroles"}}], [sort @$roles], "Roles match");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
# Logging in with an invalid user is rejected
|
||||||
|
is(request(POST '/login',
|
||||||
|
Referer => 'http://localhost/',
|
||||||
|
Content => {
|
||||||
|
username => 'alice',
|
||||||
|
password => 'foobar'
|
||||||
|
}
|
||||||
|
)->code, 403, "Logging in with invalid credentials does not work");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -13,51 +13,77 @@ my $users = {
|
||||||
admin => $ldap->add_user("admin_user"),
|
admin => $ldap->add_user("admin_user"),
|
||||||
not_admin => $ldap->add_user("not_admin_user"),
|
not_admin => $ldap->add_user("not_admin_user"),
|
||||||
many_roles => $ldap->add_user("many_roles"),
|
many_roles => $ldap->add_user("many_roles"),
|
||||||
|
many_roles_one_group => $ldap->add_user("many_roles_one_group"),
|
||||||
};
|
};
|
||||||
|
|
||||||
$ldap->add_group("hydra_admin", $users->{"admin"}->{"username"});
|
$ldap->add_group("hydra_admin", $users->{"admin"}->{"username"});
|
||||||
$ldap->add_group("hydra-admin", $users->{"not_admin"}->{"username"});
|
$ldap->add_group("hydra-admin", $users->{"not_admin"}->{"username"});
|
||||||
|
$ldap->add_group("hydra_one_group_many_roles", $users->{"many_roles_one_group"}->{"username"});
|
||||||
|
|
||||||
$ldap->add_group("hydra_create-projects", $users->{"many_roles"}->{"username"});
|
$ldap->add_group("hydra_create-projects", $users->{"many_roles"}->{"username"});
|
||||||
$ldap->add_group("hydra_restart-jobs", $users->{"many_roles"}->{"username"});
|
$ldap->add_group("hydra_restart-jobs", $users->{"many_roles"}->{"username"});
|
||||||
$ldap->add_group("hydra_bump-to-front", $users->{"many_roles"}->{"username"});
|
$ldap->add_group("hydra_bump-to-front", $users->{"many_roles"}->{"username"});
|
||||||
$ldap->add_group("hydra_cancel-build", $users->{"many_roles"}->{"username"});
|
$ldap->add_group("hydra_cancel-build", $users->{"many_roles"}->{"username"});
|
||||||
|
|
||||||
my $hydra_ldap_config = "${\$ldap->tmpdir()}/hydra_ldap_config.yaml";
|
|
||||||
LDAPContext::write_file($hydra_ldap_config, <<YAML);
|
|
||||||
credential:
|
|
||||||
class: Password
|
|
||||||
password_field: password
|
|
||||||
password_type: self_check
|
|
||||||
store:
|
|
||||||
class: LDAP
|
|
||||||
ldap_server: "${\$ldap->server_url()}"
|
|
||||||
ldap_server_options:
|
|
||||||
timeout: 30
|
|
||||||
debug: 0
|
|
||||||
binddn: "cn=root,dc=example"
|
|
||||||
bindpw: notapassword
|
|
||||||
start_tls: 0
|
|
||||||
start_tls_options:
|
|
||||||
verify: none
|
|
||||||
user_basedn: "ou=users,dc=example"
|
|
||||||
user_filter: "(&(objectClass=inetOrgPerson)(cn=%s))"
|
|
||||||
user_scope: one
|
|
||||||
user_field: cn
|
|
||||||
user_search_options:
|
|
||||||
deref: always
|
|
||||||
use_roles: 1
|
|
||||||
role_basedn: "ou=groups,dc=example"
|
|
||||||
role_filter: "(&(objectClass=groupOfNames)(member=%s))"
|
|
||||||
role_scope: one
|
|
||||||
role_field: cn
|
|
||||||
role_value: dn
|
|
||||||
role_search_options:
|
|
||||||
deref: always
|
|
||||||
YAML
|
|
||||||
|
|
||||||
$ENV{'HYDRA_LDAP_CONFIG'} = $hydra_ldap_config;
|
my $ctx = test_context(
|
||||||
my $ctx = test_context();
|
before_init => sub {
|
||||||
|
my ($ctx) = @_;
|
||||||
|
write_file($ctx->{"tmpdir"} . "/password.conf", "bindpw = ${\$ldap->{'root_password'}}");
|
||||||
|
},
|
||||||
|
hydra_config => <<CFG
|
||||||
|
<ldap>
|
||||||
|
<config>
|
||||||
|
<credential>
|
||||||
|
class = Password
|
||||||
|
password_field = password
|
||||||
|
password_type = self_check
|
||||||
|
</credential>
|
||||||
|
<store>
|
||||||
|
class = LDAP
|
||||||
|
ldap_server = ${\$ldap->server_url()}
|
||||||
|
<ldap_server_options>
|
||||||
|
timeout = 30
|
||||||
|
debug = 0
|
||||||
|
</ldap_server_options>
|
||||||
|
binddn = "cn=root,dc=example"
|
||||||
|
include password.conf
|
||||||
|
start_tls = 0
|
||||||
|
<start_tls_options>
|
||||||
|
verify = none
|
||||||
|
</start_tls_options>
|
||||||
|
user_basedn = "ou=users,dc=example"
|
||||||
|
user_filter = "(&(objectClass=inetOrgPerson)(cn=%s))"
|
||||||
|
user_scope = one
|
||||||
|
user_field = cn
|
||||||
|
<user_search_options>
|
||||||
|
deref = always
|
||||||
|
</user_search_options>
|
||||||
|
use_roles = 1
|
||||||
|
role_basedn = "ou=groups,dc=example"
|
||||||
|
role_filter = "(&(objectClass=groupOfNames)(member=%s))"
|
||||||
|
role_scope = one
|
||||||
|
role_field = cn
|
||||||
|
role_value = dn
|
||||||
|
<role_search_options>
|
||||||
|
deref = always
|
||||||
|
</role_search_options>
|
||||||
|
</store>
|
||||||
|
</config>
|
||||||
|
<role_mapping>
|
||||||
|
hydra_admin = admin
|
||||||
|
hydra_create-projects = create-projects
|
||||||
|
hydra_cancel-build = cancel-build
|
||||||
|
hydra_bump-to-front = bump-to-front
|
||||||
|
hydra_restart-jobs = restart-jobs
|
||||||
|
|
||||||
|
hydra_one_group_many_roles = create-projects
|
||||||
|
hydra_one_group_many_roles = cancel-build
|
||||||
|
hydra_one_group_many_roles = bump-to-front
|
||||||
|
</role_mapping>
|
||||||
|
</ldap>
|
||||||
|
CFG
|
||||||
|
);
|
||||||
|
|
||||||
Catalyst::Test->import('Hydra');
|
Catalyst::Test->import('Hydra');
|
||||||
|
|
||||||
|
@ -67,6 +93,7 @@ subtest "Valid login attempts" => sub {
|
||||||
admin => ["admin"],
|
admin => ["admin"],
|
||||||
not_admin => [],
|
not_admin => [],
|
||||||
many_roles => [ "create-projects", "restart-jobs", "bump-to-front", "cancel-build" ],
|
many_roles => [ "create-projects", "restart-jobs", "bump-to-front", "cancel-build" ],
|
||||||
|
many_roles_one_group => [ "create-projects", "bump-to-front", "cancel-build" ],
|
||||||
);
|
);
|
||||||
for my $username (keys %users_to_roles) {
|
for my $username (keys %users_to_roles) {
|
||||||
my $user = $users->{$username};
|
my $user = $users->{$username};
|
||||||
|
|
|
@ -6,6 +6,7 @@ use File::Path qw(make_path);
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
use Cwd qw(abs_path getcwd);
|
use Cwd qw(abs_path getcwd);
|
||||||
use CliRunners;
|
use CliRunners;
|
||||||
|
use Hydra::Helper::Exec;
|
||||||
|
|
||||||
# Set up the environment for running tests.
|
# Set up the environment for running tests.
|
||||||
#
|
#
|
||||||
|
@ -16,6 +17,9 @@ use CliRunners;
|
||||||
# * use_external_destination_store: Boolean indicating whether hydra should
|
# * use_external_destination_store: Boolean indicating whether hydra should
|
||||||
# use a destination store different from the evaluation store.
|
# use a destination store different from the evaluation store.
|
||||||
# True by default.
|
# True by default.
|
||||||
|
# * before_init: a sub which is called after the database is up, but before
|
||||||
|
# hydra-init is executed. It receives the HydraTestContext object as
|
||||||
|
# its argument.
|
||||||
#
|
#
|
||||||
# This clears several environment variables and sets them to ephemeral
|
# This clears several environment variables and sets them to ephemeral
|
||||||
# values: a temporary database, temporary Nix store, temporary Hydra
|
# values: a temporary database, temporary Nix store, temporary Hydra
|
||||||
|
@ -63,24 +67,28 @@ sub new {
|
||||||
extra_initdb_args => "--locale C.UTF-8"
|
extra_initdb_args => "--locale C.UTF-8"
|
||||||
);
|
);
|
||||||
$ENV{'HYDRA_DBI'} = $pgsql->dsn;
|
$ENV{'HYDRA_DBI'} = $pgsql->dsn;
|
||||||
system("hydra-init") == 0 or die;
|
|
||||||
|
|
||||||
my $self = {
|
my $self = bless {
|
||||||
_db => undef,
|
_db => undef,
|
||||||
db_handle => $pgsql,
|
db_handle => $pgsql,
|
||||||
tmpdir => $dir,
|
tmpdir => $dir,
|
||||||
nix_state_dir => "$dir/nix/var/nix",
|
nix_state_dir => "$dir/nix/var/nix",
|
||||||
testdir => abs_path(dirname(__FILE__) . "/.."),
|
testdir => abs_path(dirname(__FILE__) . "/.."),
|
||||||
jobsdir => abs_path(dirname(__FILE__) . "/../jobs")
|
jobsdir => abs_path(dirname(__FILE__) . "/../jobs")
|
||||||
};
|
}, $class;
|
||||||
|
|
||||||
return bless $self, $class;
|
if ($opts{'before_init'}) {
|
||||||
|
$opts{'before_init'}->($self);
|
||||||
|
}
|
||||||
|
|
||||||
|
expectOkay(5, ("hydra-init"));
|
||||||
|
|
||||||
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub db {
|
sub db {
|
||||||
my ($self, $setup) = @_;
|
my ($self, $setup) = @_;
|
||||||
|
|
||||||
|
|
||||||
if (!defined $self->{_db}) {
|
if (!defined $self->{_db}) {
|
||||||
require Hydra::Schema;
|
require Hydra::Schema;
|
||||||
require Hydra::Model::DB;
|
require Hydra::Model::DB;
|
||||||
|
|
|
@ -12,9 +12,15 @@ use Hydra::Helper::Exec;
|
||||||
# It creates a top level organization and structure, and provides
|
# It creates a top level organization and structure, and provides
|
||||||
# add_user and add_group.
|
# add_user and add_group.
|
||||||
#
|
#
|
||||||
|
# Hash Parameters:
|
||||||
|
#
|
||||||
|
# * root_password: The clear text password required for connecting to the LDAP server
|
||||||
|
#
|
||||||
# The server is automatically terminated when the class is dropped.
|
# The server is automatically terminated when the class is dropped.
|
||||||
sub new {
|
sub new {
|
||||||
my ($class) = @_;
|
my ($class, %opts) = @_;
|
||||||
|
|
||||||
|
my $rootPassword = $opts{'root_password'} // rand_chars();
|
||||||
|
|
||||||
my $root = File::Temp->newdir();
|
my $root = File::Temp->newdir();
|
||||||
mkdir $root;
|
mkdir $root;
|
||||||
|
@ -30,12 +36,13 @@ sub new {
|
||||||
my $socket = "$root/slapd.socket";
|
my $socket = "$root/slapd.socket";
|
||||||
|
|
||||||
my $self = {
|
my $self = {
|
||||||
_tmpdir => $root,
|
|
||||||
_db_dir => $db_dir,
|
_db_dir => $db_dir,
|
||||||
_openldap_source => $ENV{"OPENLDAP_ROOT"},
|
_openldap_source => $ENV{"OPENLDAP_ROOT"},
|
||||||
_pid_file => $pid_file,
|
_pid_file => $pid_file,
|
||||||
_slapd_dir => $slapd_dir,
|
_slapd_dir => $slapd_dir,
|
||||||
_socket => $socket,
|
_socket => $socket,
|
||||||
|
_tmpdir => $root,
|
||||||
|
root_password => $rootPassword,
|
||||||
};
|
};
|
||||||
|
|
||||||
my $blessed = bless $self, $class;
|
my $blessed = bless $self, $class;
|
||||||
|
@ -128,7 +135,7 @@ objectClass: olcMdbConfig
|
||||||
olcDatabase: {1}mdb
|
olcDatabase: {1}mdb
|
||||||
olcDbDirectory: ${\$self->{"_db_dir"}}
|
olcDbDirectory: ${\$self->{"_db_dir"}}
|
||||||
olcRootDN: cn=root,dc=example
|
olcRootDN: cn=root,dc=example
|
||||||
olcRootPW: notapassword
|
olcRootPW: ${\$self->{"root_password"}}
|
||||||
olcSuffix: dc=example
|
olcSuffix: dc=example
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue