LDAP support: include BC support for the YAML based loading

Includes a refactoring of the configuration loader.
This commit is contained in:
Graham Christensen
2022-02-09 21:06:28 -05:00
parent 61d74a7194
commit f07fb7d279
16 changed files with 475 additions and 41 deletions

View File

@ -6,7 +6,7 @@ use parent 'Catalyst';
use Moose;
use Hydra::Plugin;
use Hydra::Model::DB;
use Hydra::Helper::Nix qw(getHydraConfig);
use Hydra::Config qw(getLDAPConfigAmbient);
use Catalyst::Runtime '5.70';
use Catalyst qw/ConfigLoader
Static::Simple
@ -43,7 +43,7 @@ __PACKAGE__->config(
role_field => "role",
},
},
ldap => Hydra::Helper::Nix::getHydraConfig->{'ldap'}->{'config'}
ldap => getLDAPConfigAmbient()->{'config'}
},
'Plugin::ConfigLoader' => {
driver => {

View File

@ -2,7 +2,165 @@ package Hydra::Config;
use strict;
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);
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 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;

View File

@ -7,6 +7,7 @@ use base 'Hydra::Base::Controller::REST';
use File::Slurper qw(read_text);
use Crypt::RandPasswd;
use Digest::SHA1 qw(sha1_hex);
use Hydra::Config qw(getLDAPConfigAmbient);
use Hydra::Helper::Nix;
use Hydra::Helper::CatalystUtils;
use Hydra::Helper::Email;
@ -56,12 +57,10 @@ sub logout_POST {
sub doLDAPLogin {
my ($self, $c, $username) = @_;
my $user = $c->find_user({ username => $username });
my $LDAPUser = $c->find_user({ username => $username }, 'ldap');
my @LDAPRoles = $LDAPUser->roles;
my %ldap_config = %{Hydra::Helper::Nix::getHydraConfig->{'ldap'}};
my %role_mapping = $ldap_config{'role_mapping'} ? %{$ldap_config{'role_mapping'}} : ();
my $role_mapping = getLDAPConfigAmbient()->{"role_mapping"};
if (!$user) {
$c->model('DB::Users')->create(
@ -82,8 +81,11 @@ sub doLDAPLogin {
}
$user->userroles->delete;
foreach my $ldap_role (@LDAPRoles) {
if (%role_mapping{$ldap_role}) {
$user->userroles->create({ role => $role_mapping{$ldap_role} });
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);

View File

@ -5,7 +5,6 @@ use warnings;
use Exporter;
use File::Path;
use File::Basename;
use Config::General;
use Hydra::Config;
use Hydra::Helper::CatalystUtils;
use Hydra::Model::DB;
@ -49,24 +48,6 @@ sub getHydraHome {
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:
# (
# host => string,

View File

@ -8,6 +8,7 @@ use Data::Dump qw(dump);
use Digest::SHA qw(sha256_hex);
use Encode;
use File::Slurper qw(read_text);
use Hydra::Config;
use Hydra::Helper::AddBuilds;
use Hydra::Helper::CatalystUtils;
use Hydra::Helper::Email;

View File

@ -6,6 +6,7 @@ use utf8;
use Getopt::Long;
use Time::HiRes qw( gettimeofday tv_interval );
use HTTP::Server::PSGI;
use Hydra::Config;
use Hydra::Event;
use Hydra::Event::BuildFinished;
use Hydra::Helper::AddBuilds;

View File

@ -9,6 +9,7 @@ use Net::Amazon::S3;
use Net::Amazon::S3::Client;
use Nix::Config;
use Nix::Store;
use Hydra::Config;
use Hydra::Model::DB;
use Hydra::Helper::Nix;

View File

@ -3,6 +3,7 @@
use strict;
use warnings;
use utf8;
use Hydra::Config;
use Hydra::Helper::Nix;
use Net::Statsd;
use File::Slurper qw(read_text);

View File

@ -6,6 +6,7 @@ use File::Path;
use File::stat;
use File::Basename;
use Nix::Store;
use Hydra::Config;
use Hydra::Schema;
use Hydra::Helper::Nix;
use Hydra::Model::DB;