From 673a936e2d3b1a28cd2a683fe8f8e0b4891e27d1 Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Thu, 11 Aug 2022 16:46:11 +0200 Subject: [PATCH 1/4] Add merge subcommand to lemonldap-ng-cli (#2780) --- lemonldap-ng-common/scripts/lemonldap-ng-cli | 8 ++- .../lib/Lemonldap/NG/Manager/Cli.pm | 72 ++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/lemonldap-ng-common/scripts/lemonldap-ng-cli b/lemonldap-ng-common/scripts/lemonldap-ng-cli index 4fec31b377..4fbc071663 100755 --- a/lemonldap-ng-common/scripts/lemonldap-ng-cli +++ b/lemonldap-ng-common/scripts/lemonldap-ng-cli @@ -32,7 +32,7 @@ for ( my $i = 0 ; $i < @ARGV ; $i++ ) { $action ||= "usage"; if ( $action =~ - /^(?:[gs]et|del|(?:add|del)Key|(?:add|del)PostVars|save|restore|rollback)$/ +/^(?:[gs]et|del|(?:add|del)Key|(?:add|del)PostVars|merge|save|restore|rollback)$/ ) { eval { require Lemonldap::NG::Manager::Cli; }; @@ -80,6 +80,7 @@ Available actions: delKey KEY SUBKEY : delete subkey of a parameter addPostVars HOST URI KEY VALUE : add post vars for form replay delPostVars HOST URI KEY : delete post vars for form replay + merge FILE [FILE ...] : merge JSON/YAML files with existing configuration save : export configuration to STDOUT restore - : import configuration from STDIN restore FILE : import configuration from file @@ -204,6 +205,11 @@ This action lets you add a new POST var in a form replay configuration. This action lets you delete a new POST var in a form replay configuration. +=item B [I ...]> + +This action iterates through the given JSON or YAML files and merges the +existing configuration with the keys contained in those files + =item B Dump the entire LemonLDAP::NG configuration to standard output, in JSON format. diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm index 0bac4df006..cb2abd0f77 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm @@ -5,6 +5,7 @@ use Crypt::URandom; use Mouse; use Data::Dumper; use JSON; +use Hash::Merge::Simple; use Lemonldap::NG::Common::Conf::ReConstants; our $VERSION = '2.0.12'; @@ -138,6 +139,73 @@ sub del { return $self->_save($new); } +sub merge { + my ( $self, @files ) = @_; + die 'merge requires at least one file' unless (@files); + require Clone; + my $old = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf ); + my $new; + my @valid_files; + + foreach my $file (@files) { + + my $merging; + if ( $file =~ /\.ya?ml/ ) { + eval { + require YAML; + $merging = YAML::LoadFile($file); + }; + if ($@) { + print STDERR "Skipping invalid YAML file $file: " . $@; + } + } + else { + eval { + local $/ = undef; + open my $fh, '<', $file or die $!; + $merging = from_json(<$fh>); + }; + if ($@) { + print STDERR "Skipping invalid JSON file $file: " . $@; + } + } + if ( ref($merging) eq "HASH" ) { + $new = Hash::Merge::Simple::merge( $new, $merging ); + push @valid_files, $file; + } + } + die "Nothing to do" unless ref($new) eq "HASH"; + + unless ( $self->yes ) { + print "Merge configuration changes from " + . join( " ", @valid_files ) . " \n"; + print Dumper($new); + print "Confirm (N/y)? "; + my $c = ; + unless ( $c =~ /^y(?:es)?$/ ) { + die "Aborting"; + } + } + + $new = Hash::Merge::Simple::merge( $old, $new ); + _clean_hash_undef($new); + return $self->_save($new); +} + +# Remove key => undef from hashes +# This allows you to remove keys from the config +sub _clean_hash_undef { + my ($hash) = @_; + for my $key ( keys %$hash ) { + if ( !defined( $hash->{$key} ) ) { + delete $hash->{$key}; + } + if ( ref( $hash->{$key} ) eq "HASH" ) { + _clean_hash_undef( $hash->{$key} ); + } + } +} + sub addKey { my $self = shift; unless ( @_ % 3 == 0 ) { @@ -512,11 +580,11 @@ sub run { } my $action = shift; unless ( $action =~ -/^(?:get|set|del|addKey|delKey|addPostVars|delPostVars|save|restore|rollback)$/ +/^(?:get|set|del|addKey|delKey|addPostVars|delPostVars|merge|save|restore|rollback)$/ ) { die -"Unknown action $action. Only get, set, del, addKey, delKey, addPostVars, delPostVars, save, restore, rollback allowed"; +"Unknown action $action. Only get, set, del, addKey, delKey, addPostVars, delPostVars, merge, save, restore, rollback allowed"; } unless ( $action eq "restore" ) { -- GitLab From c4112c7f74d5961755bc5f4fc6f6f72a8ab713d4 Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Wed, 10 Aug 2022 17:00:38 +0200 Subject: [PATCH 2/4] Unit test for #2780 --- lemonldap-ng-manager/t/16-cli.t | 12 ++++++++++-- lemonldap-ng-manager/t/test-merge.json | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 lemonldap-ng-manager/t/test-merge.json diff --git a/lemonldap-ng-manager/t/16-cli.t b/lemonldap-ng-manager/t/16-cli.t index a8ea735142..fa4e2c1b25 100644 --- a/lemonldap-ng-manager/t/16-cli.t +++ b/lemonldap-ng-manager/t/16-cli.t @@ -4,7 +4,7 @@ use JSON; use strict; require 't/test-lib.pm'; -my $tests = 17; +my $tests = 18; use_ok('Lemonldap::NG::Common::Cli'); use_ok('Lemonldap::NG::Manager::Cli'); @@ -93,9 +93,17 @@ combined_like( sub { llcommonClient->run(@cmd) }, combined_like( sub { llclient->run(@cmd) }, qr/Configuration \d+ has been rolled back/, - '"Author IP" OK' + 'Configuration rollback OK' +); + +@cmd = qw(-yes 1 merge t/test-merge.json); +combined_like( + sub { llclient->run(@cmd) }, + qr/Saved under number \d+/, + 'Configuration merge OK' ); + count($tests); done_testing( count() ); &cleanConfFiles; diff --git a/lemonldap-ng-manager/t/test-merge.json b/lemonldap-ng-manager/t/test-merge.json new file mode 100644 index 0000000000..0ae2771b30 --- /dev/null +++ b/lemonldap-ng-manager/t/test-merge.json @@ -0,0 +1,3 @@ +{ + "notification": 0 +} -- GitLab From aa5c9271401448552e679ec7aff8fd14dd93b61e Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Thu, 11 Aug 2022 16:47:28 +0200 Subject: [PATCH 3/4] Update package dependencies (#2780) --- debian/control | 2 ++ rpm/lemonldap-ng.spec | 1 + 2 files changed, 3 insertions(+) diff --git a/debian/control b/debian/control index 3f5038be63..5d742eeed3 100644 --- a/debian/control +++ b/debian/control @@ -28,6 +28,7 @@ Build-Depends-Indep: gsfonts , libgd-securityimage-perl , libglib-perl , libgssapi-perl , + libhash-merge-simple-perl , libhtml-template-perl , libimage-magick-perl , libio-string-perl , @@ -267,6 +268,7 @@ Depends: ${misc:Depends}, libcrypt-openssl-rsa-perl, libemail-date-format-perl, liblemonldap-ng-handler-perl (= ${binary:Version}), + libhash-merge-simple-perl, lemonldap-ng-fastcgi-server (= ${binary:Version}) | lemonldap-ng-uwsgi-app (= ${binary:Version}) | apache2 | httpd-cgi Recommends: lemonldap-ng-doc (= ${binary:Version}), libxml-libxml-perl, diff --git a/rpm/lemonldap-ng.spec b/rpm/lemonldap-ng.spec index 21431c7320..a5328d5869 100644 --- a/rpm/lemonldap-ng.spec +++ b/rpm/lemonldap-ng.spec @@ -107,6 +107,7 @@ BuildRequires: perl(GD::SecurityImage) BuildRequires: perl(Getopt::Long) BuildRequires: perl(Getopt::Std) BuildRequires: perl(GSSAPI) +BuildRequires: perl(Hash::Merge::Simple) BuildRequires: perl(HTML::Template) BuildRequires: perl(HTTP::Headers) BuildRequires: perl(HTTP::Message) -- GitLab From 6467aa79ea6e819883d5d3af4da957d601f62bc7 Mon Sep 17 00:00:00 2001 From: Maxime Besson Date: Thu, 11 Aug 2022 16:44:59 +0200 Subject: [PATCH 4/4] Documentation for #2780 --- doc/sources/admin/cli_examples.rst | 74 +++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/doc/sources/admin/cli_examples.rst b/doc/sources/admin/cli_examples.rst index 4762fa7a3f..b8dc8d3f8f 100644 --- a/doc/sources/admin/cli_examples.rst +++ b/doc/sources/admin/cli_examples.rst @@ -494,6 +494,78 @@ To update the master encryption key: key 'xxxxxxxxxxxxxxx' +Bulk configuration changes +-------------------------- + +.. versionadded:: 2.0.15 + +The ``merge`` subcommand can be used to inject multiple configuration keys and +variables at once. It reads a list of JSON or YAML formatted files and combines +them with the current config. This allows you to script common configuration +changes in the form of snippets. + +Example (JSON): + +.. code:: json + + { + "https": 1, + "securedCookie": 1, + "sameSite": "None", + "macros": { + "UA": null, + "_whatToTrace": "uid" + } + } + + +Example (YAML) : + +.. code:: yaml + + # YAML files can be commented + https: 1 + securedCookie: 1 + sameSite: "None" + + # override some default macros + macros: + + # Remove UA + UA: ~ + + # Update _whatToTrace + _whatToTrace: uid + + +Importing the changes: + +.. code:: shell + + # Import a JSON snippet + /usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 merge example.json + + # Import a YAML snippet + /usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 merge example.yaml + + # Import several snippets + /usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 merge my_config/*.yaml + +.. note:: You may need to install the `YAML `__ + Perl module to be able to import + YAML configuration snippets + +.. warning:: + + * The config files will be read as the webserver (``apache``/``www-data``) + user. Make sure they have the correct permissions before running the + command + * Do not use booleans in JSON/YAML files, LemonLDAP only understands 0/1 + values for boolean configuration keys + * Due to limitations in the Perl YAML parser, you need to set a key to ``~`` + instead of ``null`` to remove it + + .. _cli-sessions: Sessions Management @@ -546,7 +618,7 @@ OIDC Consents management .. versionadded:: 2.0.9 List consents of a user :: - + lemonldap-ng-sessions consents get dwho Revoke consents on OIDC provider 'test' for a user:: -- GitLab