From ec7b471ea8cf6cbec82d0f829b1b0bb1e213388c Mon Sep 17 00:00:00 2001
From: Thomas Bellman <bellman@nsc.liu.se>
Date: Mon, 25 Sep 2023 20:35:25 +0200
Subject: [PATCH] Definition for managing fetch-crl config options.

This adds a definition x509certs::fetchcrl::option for managing
configuration options for the fetch-crl program.  The definition
can set both general options, and options for individual trust
anchors.
---
 manifests/fetchcrl.pp               |   1 +
 manifests/fetchcrl/cfgdir.pp        |  20 ++++
 manifests/fetchcrl/option.pp        | 145 ++++++++++++++++++++++++++++
 templates/fetchcrl_options.conf.erb |  39 ++++++++
 4 files changed, 205 insertions(+)
 create mode 100644 manifests/fetchcrl/cfgdir.pp
 create mode 100644 manifests/fetchcrl/option.pp
 create mode 100644 templates/fetchcrl_options.conf.erb

diff --git a/manifests/fetchcrl.pp b/manifests/fetchcrl.pp
index 8b21724..21a119b 100644
--- a/manifests/fetchcrl.pp
+++ b/manifests/fetchcrl.pp
@@ -11,6 +11,7 @@
 class x509certs::fetchcrl
 {
     contain x509certs::fetchcrl::package
+    contain x509certs::fetchcrl::cfgdir
     contain x509certs::fetchcrl::service
     contain x509certs::fetchcrl::initial
 }
diff --git a/manifests/fetchcrl/cfgdir.pp b/manifests/fetchcrl/cfgdir.pp
new file mode 100644
index 0000000..1645dfa
--- /dev/null
+++ b/manifests/fetchcrl/cfgdir.pp
@@ -0,0 +1,20 @@
+# Copyright © 2023      National Supercomputer Centre,
+#                       Linköping University, Sweden
+# Licensed under the GNU LGPL v3+; see the README file for more information.
+
+
+# Internal helper for x509certs::fetchcrl::option definition.
+#
+class x509certs::fetchcrl::cfgdir
+{
+    $cfgdir = '/etc/fetch-crl.d'
+
+    file {
+	$cfgdir:
+	    ensure => directory,
+	    owner => 'root', group => 'root', mode => '0755',
+	    recurse => true, purge => true, force => true, backup => false,
+	    require => Class['x509certs::fetchcrl::package'],
+	    notify => Class['x509certs::fetchcrl::initial'];
+    }
+}
diff --git a/manifests/fetchcrl/option.pp b/manifests/fetchcrl/option.pp
new file mode 100644
index 0000000..0f31292
--- /dev/null
+++ b/manifests/fetchcrl/option.pp
@@ -0,0 +1,145 @@
+# Copyright © 2023      National Supercomputer Centre,
+#                       Linköping University, Sweden
+# Licensed under the GNU LGPL v3+; see the README file for more information.
+
+
+/*
+ * Manage fetch-crl configuration options.
+ *
+ * The name parameter specifies the option to set, and/or which trust
+ * anchor section to set the option in.  There are four basic cases:
+ *
+ *  - "[" trust-anchor-name "]" option-name
+ *    E.g. "[EGI-Example-CA] agingtolerance".
+ *    This sets the named option within the specified trust anchor
+ *    section.
+ *
+ *  - option-name  ||
+ *    "[" "]" option-name
+ *    E.g. "formats" or "[] formats".  This sets the named option as a
+ *    general, not trust anchor-specific, option.
+ *
+ *  - "[" trust-anchor-name "]"
+ *    The value must be a hash of option names and values, which will
+ *    all be set in the specified trust anchor section.
+ *
+ *  - "[" "]"
+ *    As with the previous format, the value must be a hash of option
+ *    names and values, but they will be set as general options.
+ *
+ * In all cases, whitespace is allowed around inside of the brackets
+ * (around the trust anchor name), and between the closing bracket of
+ * the trust anchor specifier and the option name.  I.e, the following
+ * are equivalent:
+ *
+ *  -  "[foo]bar"  ===  "[ foo ]bar"  ===  "[foo] bar"  ===  "[ foo ] bar"
+ *  -  "[]fie"  ===  "[ ]fie"  ===  "[] fie"  ===  "[ ] fie"
+ *  -  "[]"  ===  "[ ]"
+ *
+ *
+ * Each x509certs::fetchcrl::option resource will generate its own file
+ * in the /etc/fetch-crl.d directory.  This will override any setting of
+ * the same option in /etc/fetch-crl.conf.  Any files in that directory
+ * that are not explicitly managed using this definition will be removed
+ * automatically.
+ *
+ *
+ * Some examples:
+ *
+ *     x509certs::fetchcrl::option {
+ *         'agingtolerance':
+ *             value => 4711;
+ *         '[] nocache':
+ *             value => true;
+ *         '[]':
+ *             value => { 'foo' => 17, 'fie' => true, };
+ *         '[EGI-Example-CA] statedir':
+ *             value => '';
+ *         '[EGI-Example-CA]':
+ *             value => { 'fum' => '', };
+ *     }
+ *
+ * WARNING: It may be possible to set the same option twice, by using
+ * one resource on the "[trustanchor]option" format, and one resource
+ * on the "[trustanchor]" format with a hash value, e.g:
+ *
+ *     x509certs::fetchcrl::option {
+ *         '[] agingtolerance': value => 4711;
+ *         '[]': value => { 'agingtolerance' => 11174, };
+ *     }
+ *
+ * This is a limitation of the x509certs::fetchcrl::option definition.
+ * It is not well-defined which of the two values fetch-crl will use.
+ */
+define x509certs::fetchcrl::option(
+	# Whether the option should be set (the default) or unset.  The
+	# values 'present' and 'absent' are equivalent to 'set' and
+	# 'unset', respectively.
+	#
+	$ensure = 'set',
+
+	# The value for the option.  As a special case, if set to the
+	# boolean value true, a "valueless" option will be set in the
+	# config file, useful for e.g. the 'nocache' or 'nowarning'
+	# options.  (In practice, fetch-crl treats this as equivalent
+	# to setting the option to 1.)
+	#
+	# If no option name is given in the name parameter, this must
+	# be a hash of options for the specified trust anchor.
+	#
+	$value = undef,
+
+	# Comment lines to add before the option in the generated config
+	# snippet file.
+	#
+	$comment = [],
+)
+{
+    $pattern = '^(\[\s*([^\]\s]*)\s*\])?\s*([^\s]*)$'
+    $sectionspec = regsubst($name, $pattern, '\1')
+    $section = regsubst($name, $pattern, '\2')
+    $option  = regsubst($name, $pattern, '\3')
+    if ( $section =~ /::|\s/  or  $option =~ /::|\s/  or
+	 ( $sectionspec == '' and $option == '' ) )
+    {
+	fail("X509certs::Fetchcrl::Option[${title}]:",
+	     "Bad option specifier, ``${name}''")
+    }
+
+    include x509certs::fetchcrl::cfgdir
+
+    if $sectionspec != '' and $section == '' and $option == '' {
+	$cfgfile_name = '::GLOBAL::'
+    } elsif $section == '' {
+	$cfgfile_name = $option
+    } else {
+	$cfgfile_name = "${section}::${option}"
+    }
+    $cfgfile = "${x509certs::fetchcrl::cfgdir::cfgdir}/${cfgfile_name}.conf"
+
+    if ( $ensure == 'present' or $ensure == 'set' )
+    {
+	if ( $value == undef or $value == false ) {
+	    fail("X509certs::Fetchcrl::Option[${title}]:",
+	         "Value must be given when ensure=${ensure}")
+	}
+	file {
+	    $cfgfile:
+		ensure => file,
+		content => template('x509certs/fetchcrl_options.conf.erb'),
+		owner => 'root', group => 'root', mode => '0444',
+		require => Class['x509certs::fetchcrl::package'],
+		notify => Class['x509certs::fetchcrl::initial'];
+	}
+    } elsif ( $ensure == 'absent' or $ensure == 'unset' ) {
+	file {
+	    $cfgfile:
+		ensure => absent,
+		require => Class['x509certs::fetchcrl::package'],
+		notify => Class['x509certs::fetchcrl::initial'];
+	}
+    } else {
+	fail("X509certs::Fetchcrl::Option[${title}]:",
+	     "Bad ensure parameter, ``${ensure}''")
+    }
+}
diff --git a/templates/fetchcrl_options.conf.erb b/templates/fetchcrl_options.conf.erb
new file mode 100644
index 0000000..78c8ac4
--- /dev/null
+++ b/templates/fetchcrl_options.conf.erb
@@ -0,0 +1,39 @@
+<% # Copyright © 2023      National Supercomputer Centre,
+   #                       Linköping University, Sweden
+   # Licensed under the GNU LGPL v3+; see the README file for more information.
+-%>
+<% [@comment].flatten.each do |c| -%>
+# <%= c %>
+<% end -%>
+<% if @section != "" -%>
+[<%= @section %>]
+<% end -%>
+<% if @option != ""
+      # Single option
+      if @value.respond_to? :each_pair
+	 raise(Puppet::ParseError,
+	       ("X509certs::Fetchcrl::Option['#{@title}']:" +
+		" Value is hash when single option name given"))
+      end
+      option_hash = { @option => @value }
+   elsif not @value.respond_to? :each_pair
+      raise(Puppet::ParseError,
+	    ("X509certs::Fetchcrl::Option['#{@title}']:" +
+	     " Value not hash when no option name given"))
+   else
+      option_hash = @value
+   end
+-%>
+<% option_hash.sort.each do |optname,optvalue| -%>
+<%    next if optname =~ /^#/ -%>
+<%    if option_hash.has_key?("#"+optname) -%>
+<%       [option_hash["#"+optname]].flatten.each do |c| -%>
+# <%= c %>
+<%       end -%>
+<%    end -%>
+<%    if optvalue == true -%>
+<%= optname %>
+<%    else -%>
+<%= optname %> = <%= optvalue.to_s %>
+<%    end -%>
+<% end -%>
-- 
GitLab