freeipa

FreeIPA is an integrated Identity and Authentication solution for Linux/UNIX networked environments.  |  http://www.freeipa.org/

Commit afd7c05 csrgen: Use data_sources option to define which fields are rendered

13 files Authored by benlipton a year ago , Committed by jcholast a year ago ,
csrgen: Use data_sources option to define which fields are rendered

This removes the ipa.syntaxrule and ipa.datarule macros in favor of
simple 'if' statements based on the data referenced in the rules. The
'if' statement for a syntax rule is generated based on the data rules it
contains.

The Subject DN should not be generated unless all data rules are in
place, so the ability to override the logical operator that combines
data_sources (from 'or' to 'and') is added.

https://fedorahosted.org/freeipa/ticket/4899

Reviewed-By: Jan Cholasta <jcholast@redhat.com>

    
 1 @@ -1,42 +0,0 @@
 2 - {% set rendersyntax = {} %}
 3 - 
 4 - {% set renderdata = {} %}
 5 - 
 6 - {# Wrapper for syntax rules. We render the contents of the rule into a
 7 - variable, so that if we find that none of the contained data rules rendered we
 8 - can suppress the whole syntax rule. That is, a syntax rule is rendered either
 9 - if no data rules are specified (unusual) or if at least one of the data rules
10 - rendered successfully. #}
11 - {% macro syntaxrule() -%}
12 - {% do rendersyntax.update(none=true, any=false) -%}
13 - {% set contents -%}
14 - {{ caller() -}}
15 - {% endset -%}
16 - {% if rendersyntax['none'] or rendersyntax['any'] -%}
17 - {{ contents -}}
18 - {% endif -%}
19 - {% endmacro %}
20 - 
21 - {# Wrapper for data rules. A data rule is rendered only when all of the data
22 - fields it contains have data available. #}
23 - {% macro datarule() -%}
24 - {% do rendersyntax.update(none=false) -%}
25 - {% do renderdata.update(all=true) -%}
26 - {% set contents -%}
27 - {{ caller() -}}
28 - {% endset -%}
29 - {% if renderdata['all'] -%}
30 - {% do rendersyntax.update(any=true) -%}
31 - {{ contents -}}
32 - {% endif -%}
33 - {% endmacro %}
34 - 
35 - {# Wrapper for fields in data rules. If any value wrapped by this macro
36 - produces an empty string, the entire data rule will be suppressed. #}
37 - {% macro datafield(value) -%}
38 - {% if value -%}
39 - {{ value -}}
40 - {% else -%}
41 - {% do renderdata.update(all=false) -%}
42 - {% endif -%}
43 - {% endmacro %}
 1 @@ -12,6 +12,7 @@
 2   »       rules/dataEmail.json»       »       \
 3   »       rules/dataHostCN.json»       »       \
 4   »       rules/dataUsernameCN.json»       \
 5 + »       rules/dataSubjectBase.json»       \
 6   »       rules/syntaxSAN.json»       »       \
 7   »       rules/syntaxSubject.json»       \
 8   »       $(NULL)
 9 @@ -21,7 +22,6 @@
10   »       templates/certutil_base.tmpl»       \
11   »       templates/openssl_base.tmpl»       \
12   »       templates/openssl_macros.tmpl»       \
13 - »       templates/ipa_macros.tmpl»       \
14   »       $(NULL)
15   
16   EXTRA_DIST =»       »       »       »       \
 1 @@ -2,7 +2,8 @@
 2       {
 3           "syntax": "syntaxSubject",
 4           "data": [
 5 -             "dataHostCN"
 6 +             "dataHostCN",
 7 +             "dataSubjectBase"
 8           ]
 9       },
10       {
 1 @@ -2,7 +2,8 @@
 2       {
 3           "syntax": "syntaxSubject",
 4           "data": [
 5 -             "dataUsernameCN"
 6 +             "dataUsernameCN",
 7 +             "dataSubjectBase"
 8           ]
 9       },
10       {
 1 @@ -2,11 +2,14 @@
 2     "rules": [
 3       {
 4         "helper": "openssl",
 5 -       "template": "DNS = {{ipa.datafield(subject.krbprincipalname.0.partition('/')[2].partition('@')[0])}}"
 6 +       "template": "DNS = {{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
 7       },
 8       {
 9         "helper": "certutil",
10 -       "template": "dns:{{ipa.datafield(subject.krbprincipalname.0.partition('/')[2].partition('@')[0])|quote}}"
11 +       "template": "dns:{{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]|quote}}"
12       }
13 -   ]
14 +   ],
15 +   "options": {
16 +     "data_source": "subject.krbprincipalname.0.partition('/')[2].partition('@')[0]"
17 +   }
18   }
 1 @@ -2,11 +2,14 @@
 2     "rules": [
 3       {
 4         "helper": "openssl",
 5 -       "template": "email = {{ipa.datafield(subject.mail.0)}}"
 6 +       "template": "email = {{subject.mail.0}}"
 7       },
 8       {
 9         "helper": "certutil",
10 -       "template": "email:{{ipa.datafield(subject.mail.0)|quote}}"
11 +       "template": "email:{{subject.mail.0|quote}}"
12       }
13 -   ]
14 +   ],
15 +   "options": {
16 +     "data_source": "subject.mail.0"
17 +   }
18   }
 1 @@ -2,11 +2,14 @@
 2     "rules": [
 3       {
 4         "helper": "openssl",
 5 -       "template": "{{ipa.datafield(config.ipacertificatesubjectbase.0)}}\nCN={{ipa.datafield(subject.krbprincipalname.0.partition('/')[2].partition('@')[0])}}"
 6 +       "template": "CN={{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
 7       },
 8       {
 9         "helper": "certutil",
10 -       "template": "CN={{ipa.datafield(subject.krbprincipalname.0.partition('/')[2].partition('@')[0])|quote}},{{ipa.datafield(config.ipacertificatesubjectbase.0)|quote}}"
11 +       "template": "CN={{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]|quote}}"
12       }
13 -   ]
14 +   ],
15 +   "options": {
16 +     "data_source": "subject.krbprincipalname.0.partition('/')[2].partition('@')[0]"
17 +   }
18   }
 1 @@ -0,0 +1,15 @@
 2 + {
 3 +   "rules": [
 4 +     {
 5 +       "helper": "openssl",
 6 +       "template": "{{config.ipacertificatesubjectbase.0}}"
 7 +     },
 8 +     {
 9 +       "helper": "certutil",
10 +       "template": "{{config.ipacertificatesubjectbase.0|quote}}"
11 +     }
12 +   ],
13 +   "options": {
14 +     "data_source": "config.ipacertificatesubjectbase.0"
15 +   }
16 + }
 1 @@ -2,11 +2,14 @@
 2     "rules": [
 3       {
 4         "helper": "openssl",
 5 -       "template": "{{ipa.datafield(config.ipacertificatesubjectbase.0)}}\nCN={{ipa.datafield(subject.uid.0)}}"
 6 +       "template": "CN={{subject.uid.0}}"
 7       },
 8       {
 9         "helper": "certutil",
10 -       "template": "CN={{ipa.datafield(subject.uid.0)|quote}},{{ipa.datafield(config.ipacertificatesubjectbase.0)|quote}}"
11 +       "template": "CN={{subject.uid.0|quote}}"
12       }
13 -   ]
14 +   ],
15 +   "options": {
16 +     "data_source": "subject.uid.0"
17 +   }
18   }
 1 @@ -2,14 +2,15 @@
 2     "rules": [
 3       {
 4         "helper": "openssl",
 5 -       "template": "distinguished_name = {% call openssl.section() %}{{ datarules|first }}{% endcall %}"
 6 +       "template": "distinguished_name = {% call openssl.section() %}{{ datarules|reverse|join('\n') }}{% endcall %}"
 7       },
 8       {
 9         "helper": "certutil",
10 -       "template": "-s {{ datarules|first }}"
11 +       "template": "-s {{ datarules|join(',') }}"
12       }
13     ],
14     "options": {
15 -     "required": true
16 +     "required": true,
17 +     "data_source_combinator": "and"
18     }
19   }
1 @@ -1,6 +1,3 @@
2 - {% raw -%}
3 - {% import "ipa_macros.tmpl" as ipa -%}
4 - {%- endraw %}
5   #!/bin/bash -e
6   
7   if [[ $# -lt 1 ]]; then
1 @@ -1,6 +1,5 @@
2   {% raw -%}
3   {% import "openssl_macros.tmpl" as openssl -%}
4 - {% import "ipa_macros.tmpl" as ipa -%}
5   {%- endraw %}
6   #!/bin/bash -e
7   
 1 @@ -81,8 +81,6 @@
 2               keep_trailing_newline=True, undefined=IndexableUndefined)
 3   
 4           self.passthrough_globals = {}
 5 -         self._define_passthrough('ipa.syntaxrule')
 6 -         self._define_passthrough('ipa.datarule')
 7   
 8       def _define_passthrough(self, call):
 9   
10 @@ -109,8 +107,15 @@
11           for description, syntax_rule, data_rules in rules:
12               data_rules_prepared = [
13                   self._prepare_data_rule(rule) for rule in data_rules]
14 + 
15 +             data_sources = []
16 +             for rule in data_rules:
17 +                 data_source = rule.options.get('data_source')
18 +                 if data_source:
19 +                     data_sources.append(data_source)
20 + 
21               syntax_rules.append(self._prepare_syntax_rule(
22 -                 syntax_rule, data_rules_prepared, description))
23 +                 syntax_rule, data_rules_prepared, description, data_sources))
24   
25           template_params = self._get_template_params(syntax_rules)
26           base_template = self.jinja2.get_template(
27 @@ -129,11 +134,9 @@
28   
29           return combined_template
30   
31 -     def _wrap_rule(self, rule, rule_type):
32 -         template = '{%% call ipa.%srule() %%}%s{%% endcall %%}' % (
33 -             rule_type, rule)
34 - 
35 -         return template
36 +     def _wrap_conditional(self, rule, condition):
37 +         rule = '{%% if %s %%}%s{%% endif %%}' % (condition, rule)
38 +         return rule
39   
40       def _wrap_required(self, rule, description):
41           template = '{%% filter required("%s") %%}%s{%% endfilter %%}' % (
42 @@ -142,9 +145,16 @@
43           return template
44   
45       def _prepare_data_rule(self, data_rule):
46 -         return self._wrap_rule(data_rule.template, 'data')
47 +         template = data_rule.template
48 + 
49 +         data_source = data_rule.options.get('data_source')
50 +         if data_source:
51 +             template = self._wrap_conditional(template, data_source)
52 + 
53 +         return template
54   
55 -     def _prepare_syntax_rule(self, syntax_rule, data_rules, description):
56 +     def _prepare_syntax_rule(
57 +             self, syntax_rule, data_rules, description, data_sources):
58           logger.debug('Syntax rule template: %s' % syntax_rule.template)
59           template = self.jinja2.from_string(
60               syntax_rule.template, globals=self.passthrough_globals)
61 @@ -156,7 +166,10 @@
62               raise errors.CSRTemplateError(reason=_(
63                   'Template error when formatting certificate data'))
64   
65 -         prepared_template = self._wrap_rule(rendered, 'syntax')
66 +         combinator = ' %s ' % syntax_rule.options.get(
67 +             'data_source_combinator', 'or')
68 +         condition = combinator.join(data_sources)
69 +         prepared_template = self._wrap_conditional(rendered, condition)
70           if is_required:
71               prepared_template = self._wrap_required(
72                   prepared_template, description)
73 @@ -197,10 +210,11 @@
74   
75           return {'parameters': parameters, 'extensions': extensions}
76   
77 -     def _prepare_syntax_rule(self, syntax_rule, data_rules, description):
78 +     def _prepare_syntax_rule(
79 +             self, syntax_rule, data_rules, description, data_sources):
80           """Overrides method to pull out whether rule is an extension or not."""
81           prepared_template = super(OpenSSLFormatter, self)._prepare_syntax_rule(
82 -             syntax_rule, data_rules, description)
83 +             syntax_rule, data_rules, description, data_sources)
84           is_extension = syntax_rule.options.get('extension', False)
85           return self.SyntaxRule(prepared_template, is_extension)
86