freeipa

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

Commit 5420e9c csrgen: Remove helper abstraction

19 files Authored by benlipton a year ago , Committed by jcholast a year ago ,
csrgen: Remove helper abstraction

All requests now use the OpenSSL formatter. However, we keep Formatter
a separate class so that it can be changed out for tests.

https://pagure.io/freeipa/issue/4899

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

    
  1 @@ -244,13 +244,6 @@
  2           return self.SyntaxRule(prepared_template, is_extension)
  3   
  4   
  5 - class CertutilFormatter(Formatter):
  6 -     base_template_name = 'certutil_base.tmpl'
  7 - 
  8 -     def _get_template_params(self, syntax_rules):
  9 -         return {'options': syntax_rules}
 10 - 
 11 - 
 12   class FieldMapping(object):
 13       """Representation of the rules needed to construct a complete cert field.
 14   
 15 @@ -279,13 +272,11 @@
 16   
 17   
 18   class RuleProvider(object):
 19 -     def rules_for_profile(self, profile_id, helper):
 20 +     def rules_for_profile(self, profile_id):
 21           """
 22           Return the rules needed to build a CSR using the given profile.
 23   
 24           :param profile_id: str, name of the CSR generation profile to use
 25 -         :param helper: str, name of tool (e.g. openssl, certutil) that will be
 26 -             used to create CSR
 27   
 28           :returns: list of FieldMapping, filled out with the appropriate rules
 29           """
 30 @@ -321,40 +312,31 @@
 31               )
 32           )
 33   
 34 -     def _rule(self, rule_name, helper):
 35 -         if (rule_name, helper) not in self.rules:
 36 +     def _rule(self, rule_name):
 37 +         if rule_name not in self.rules:
 38               try:
 39                   with self._open('rules', '%s.json' % rule_name) as f:
 40 -                     ruleset = json.load(f)
 41 +                     ruleconf = json.load(f)
 42               except IOError:
 43                   raise errors.NotFound(
 44 -                     reason=_('Ruleset %(ruleset)s does not exist.') %
 45 -                     {'ruleset': rule_name})
 46 +                     reason=_('No generation rule %(rulename)s found.') %
 47 +                     {'rulename': rule_name})
 48   
 49 -             matching_rules = [r for r in ruleset['rules']
 50 -                               if r['helper'] == helper]
 51 -             if len(matching_rules) == 0:
 52 +             try:
 53 +                 rule = ruleconf['rule']
 54 +             except KeyError:
 55                   raise errors.EmptyResult(
 56 -                     reason=_('No transformation in "%(ruleset)s" rule supports'
 57 -                              ' helper "%(helper)s"') %
 58 -                     {'ruleset': rule_name, 'helper': helper})
 59 -             elif len(matching_rules) > 1:
 60 -                 raise errors.RedundantMappingRule(
 61 -                     ruleset=rule_name, helper=helper)
 62 -             rule = matching_rules[0]
 63 - 
 64 -             options = {}
 65 -             if 'options' in ruleset:
 66 -                 options.update(ruleset['options'])
 67 -             if 'options' in rule:
 68 -                 options.update(rule['options'])
 69 - 
 70 -             self.rules[(rule_name, helper)] = Rule(
 71 +                     reason=_('Generation rule "%(rulename)s" is missing the'
 72 +                              ' "rule" key') % {'rulename': rule_name})
 73 + 
 74 +             options = ruleconf.get('options', {})
 75 + 
 76 +             self.rules[rule_name] = Rule(
 77                   rule_name, rule['template'], options)
 78   
 79 -         return self.rules[(rule_name, helper)]
 80 +         return self.rules[rule_name]
 81   
 82 -     def rules_for_profile(self, profile_id, helper):
 83 +     def rules_for_profile(self, profile_id):
 84           try:
 85               with self._open('profiles', '%s.json' % profile_id) as f:
 86                   profile = json.load(f)
 87 @@ -365,28 +347,23 @@
 88   
 89           field_mappings = []
 90           for field in profile:
 91 -             syntax_rule = self._rule(field['syntax'], helper)
 92 -             data_rules = [self._rule(name, helper) for name in field['data']]
 93 +             syntax_rule = self._rule(field['syntax'])
 94 +             data_rules = [self._rule(name) for name in field['data']]
 95               field_mappings.append(FieldMapping(
 96                   syntax_rule.name, syntax_rule, data_rules))
 97           return field_mappings
 98   
 99   
100   class CSRGenerator(object):
101 -     FORMATTERS = {
102 -         'openssl': OpenSSLFormatter,
103 -         'certutil': CertutilFormatter,
104 -     }
105 - 
106 -     def __init__(self, rule_provider):
107 +     def __init__(self, rule_provider, formatter_class=OpenSSLFormatter):
108           self.rule_provider = rule_provider
109 +         self.formatter = formatter_class()
110   
111 -     def csr_script(self, principal, config, profile_id, helper):
112 +     def csr_script(self, principal, config, profile_id):
113           render_data = {'subject': principal, 'config': config}
114   
115 -         formatter = self.FORMATTERS[helper]()
116 -         rules = self.rule_provider.rules_for_profile(profile_id, helper)
117 -         template = formatter.build_template(rules)
118 +         rules = self.rule_provider.rules_for_profile(profile_id)
119 +         template = self.formatter.build_template(rules)
120   
121           try:
122               script = template.render(render_data)
 1 @@ -1,14 +1,7 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "DNS = {{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
 7 -     },
 8 -     {
 9 -       "helper": "certutil",
10 -       "template": "dns:{{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]|quote}}"
11 -     }
12 -   ],
13 +   "rule": {
14 +     "template": "DNS = {{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
15 +   },
16     "options": {
17       "data_source": "subject.krbprincipalname.0.partition('/')[2].partition('@')[0]"
18     }
 1 @@ -1,14 +1,7 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "email = {{subject.mail.0}}"
 7 -     },
 8 -     {
 9 -       "helper": "certutil",
10 -       "template": "email:{{subject.mail.0|quote}}"
11 -     }
12 -   ],
13 +   "rule": {
14 +     "template": "email = {{subject.mail.0}}"
15 +   },
16     "options": {
17       "data_source": "subject.mail.0"
18     }
 1 @@ -1,14 +1,7 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "CN={{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
 7 -     },
 8 -     {
 9 -       "helper": "certutil",
10 -       "template": "CN={{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]|quote}}"
11 -     }
12 -   ],
13 +   "rule": {
14 +     "template": "CN={{subject.krbprincipalname.0.partition('/')[2].partition('@')[0]}}"
15 +   },
16     "options": {
17       "data_source": "subject.krbprincipalname.0.partition('/')[2].partition('@')[0]"
18     }
 1 @@ -1,14 +1,7 @@
 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 +   "rule": {
14 +     "template": "{{config.ipacertificatesubjectbase.0}}"
15 +   },
16     "options": {
17       "data_source": "config.ipacertificatesubjectbase.0"
18     }
 1 @@ -1,14 +1,7 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "CN={{subject.uid.0}}"
 7 -     },
 8 -     {
 9 -       "helper": "certutil",
10 -       "template": "CN={{subject.uid.0|quote}}"
11 -     }
12 -   ],
13 +   "rule": {
14 +     "template": "CN={{subject.uid.0}}"
15 +   },
16     "options": {
17       "data_source": "subject.uid.0"
18     }
 1 @@ -1,15 +1,8 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "subjectAltName = @{% call openssl.section() %}{{ datarules|join('\n') }}{% endcall %}",
 7 -       "options": {
 8 -         "extension": true
 9 -       }
10 -     },
11 -     {
12 -       "helper": "certutil",
13 -       "template": "--extSAN {{ datarules|join(',') }}"
14 -     }
15 -   ]
16 +   "rule": {
17 +     "template": "subjectAltName = @{% call openssl.section() %}{{ datarules|join('\n') }}{% endcall %}"
18 +   },
19 +   "options": {
20 +     "extension": true
21 +   }
22   }
 1 @@ -1,14 +1,7 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "distinguished_name = {% call openssl.section() %}{{ datarules|reverse|join('\n') }}{% endcall %}"
 7 -     },
 8 -     {
 9 -       "helper": "certutil",
10 -       "template": "-s {{ datarules|join(',') }}"
11 -     }
12 -   ],
13 +   "rule": {
14 +     "template": "distinguished_name = {% call openssl.section() %}{{ datarules|reverse|join('\n') }}{% endcall %}"
15 +   },
16     "options": {
17       "required": true,
18       "data_source_combinator": "and"
 1 @@ -1,11 +0,0 @@
 2 - #!/bin/bash -e
 3 - 
 4 - if [[ $# -lt 1 ]]; then
 5 - echo "Usage: $0 <outfile> [<any> <certutil> <args>]"
 6 - echo "Called as: $0 $@"
 7 - exit 1
 8 - fi
 9 - 
10 - CSR="$1"
11 - shift
12 - certutil -R -a -z <(head -c 4096 /dev/urandom) -o "$CSR" {{ options|join(' ') }} "$@"
1 @@ -106,7 +106,7 @@
2           generator = CSRGenerator(FileRuleProvider())
3   
4           script = generator.csr_script(
5 -             principal_obj, config, profile_id, helper)
6 +             principal_obj, config, profile_id)
7   
8           result = {}
9           if 'out' in options:
 1 @@ -0,0 +1,34 @@
 2 + #!/bin/bash -e
 3 + 
 4 + if [[ $# -lt 2 ]]; then
 5 + echo "Usage: $0 <outfile> <keyfile> <other openssl arguments>"
 6 + echo "Called as: $0 $@"
 7 + exit 1
 8 + fi
 9 + 
10 + CONFIG="$(mktemp)"
11 + CSR="$1"
12 + KEYFILE="$2"
13 + shift; shift
14 + 
15 + echo \
16 + '[ req ]
17 + prompt = no
18 + encrypt_key = no
19 + 
20 + distinguished_name = sec0
21 + req_extensions = sec2
22 + 
23 + [ sec0 ]
24 + O=DOMAIN.EXAMPLE.COM
25 + CN=machine.example.com
26 + 
27 + [ sec1 ]
28 + DNS = machine.example.com
29 + 
30 + [ sec2 ]
31 + subjectAltName = @sec1
32 + ' > "$CONFIG"
33 + 
34 + openssl req -new -config "$CONFIG" -out "$CSR" -key "$KEYFILE" "$@"
35 + rm "$CONFIG"
 1 @@ -0,0 +1,34 @@
 2 + #!/bin/bash -e
 3 + 
 4 + if [[ $# -lt 2 ]]; then
 5 + echo "Usage: $0 <outfile> <keyfile> <other openssl arguments>"
 6 + echo "Called as: $0 $@"
 7 + exit 1
 8 + fi
 9 + 
10 + CONFIG="$(mktemp)"
11 + CSR="$1"
12 + KEYFILE="$2"
13 + shift; shift
14 + 
15 + echo \
16 + '[ req ]
17 + prompt = no
18 + encrypt_key = no
19 + 
20 + distinguished_name = sec0
21 + req_extensions = sec2
22 + 
23 + [ sec0 ]
24 + O=DOMAIN.EXAMPLE.COM
25 + CN=testuser
26 + 
27 + [ sec1 ]
28 + email = testuser@example.com
29 + 
30 + [ sec2 ]
31 + subjectAltName = @sec1
32 + ' > "$CONFIG"
33 + 
34 + openssl req -new -config "$CONFIG" -out "$CSR" -key "$KEYFILE" "$@"
35 + rm "$CONFIG"
 1 @@ -1,12 +1,5 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "openssl_rule"
 7 -     },
 8 -     {
 9 -       "helper": "certutil",
10 -       "template": "certutil_rule"
11 -     }
12 -   ]
13 +   "rule": {
14 +     "template": "openssl_rule"
15 +   }
16   }
 1 @@ -1,18 +1,8 @@
 2   {
 3 -   "rules": [
 4 -     {
 5 -       "helper": "openssl",
 6 -       "template": "openssl_rule",
 7 -       "options": {
 8 -         "helper_option": true
 9 -       }
10 -     },
11 -     {
12 -       "helper": "certutil",
13 -       "template": "certutil_rule"
14 -     }
15 -   ],
16 +   "rule": {
17 +     "template": "openssl_rule"
18 +   },
19     "options": {
20 -     "global_option": true
21 +     "rule_option": true
22     }
23   }
 1 @@ -1,11 +0,0 @@
 2 - #!/bin/bash -e
 3 - 
 4 - if [[ $# -lt 1 ]]; then
 5 - echo "Usage: $0 <outfile> [<any> <certutil> <args>]"
 6 - echo "Called as: $0 $@"
 7 - exit 1
 8 - fi
 9 - 
10 - CSR="$1"
11 - shift
12 - certutil -R -a -z <(head -c 4096 /dev/urandom) -o "$CSR" -s CN=machine.example.com,O=DOMAIN.EXAMPLE.COM --extSAN dns:machine.example.com "$@"
 1 @@ -1,34 +0,0 @@
 2 - #!/bin/bash -e
 3 - 
 4 - if [[ $# -lt 2 ]]; then
 5 - echo "Usage: $0 <outfile> <keyfile> <other openssl arguments>"
 6 - echo "Called as: $0 $@"
 7 - exit 1
 8 - fi
 9 - 
10 - CONFIG="$(mktemp)"
11 - CSR="$1"
12 - KEYFILE="$2"
13 - shift; shift
14 - 
15 - echo \
16 - '[ req ]
17 - prompt = no
18 - encrypt_key = no
19 - 
20 - distinguished_name = sec0
21 - req_extensions = sec2
22 - 
23 - [ sec0 ]
24 - O=DOMAIN.EXAMPLE.COM
25 - CN=machine.example.com
26 - 
27 - [ sec1 ]
28 - DNS = machine.example.com
29 - 
30 - [ sec2 ]
31 - subjectAltName = @sec1
32 - ' > "$CONFIG"
33 - 
34 - openssl req -new -config "$CONFIG" -out "$CSR" -key "$KEYFILE" "$@"
35 - rm "$CONFIG"
 1 @@ -1,11 +0,0 @@
 2 - #!/bin/bash -e
 3 - 
 4 - if [[ $# -lt 1 ]]; then
 5 - echo "Usage: $0 <outfile> [<any> <certutil> <args>]"
 6 - echo "Called as: $0 $@"
 7 - exit 1
 8 - fi
 9 - 
10 - CSR="$1"
11 - shift
12 - certutil -R -a -z <(head -c 4096 /dev/urandom) -o "$CSR" -s CN=testuser,O=DOMAIN.EXAMPLE.COM --extSAN email:testuser@example.com "$@"
 1 @@ -1,34 +0,0 @@
 2 - #!/bin/bash -e
 3 - 
 4 - if [[ $# -lt 2 ]]; then
 5 - echo "Usage: $0 <outfile> <keyfile> <other openssl arguments>"
 6 - echo "Called as: $0 $@"
 7 - exit 1
 8 - fi
 9 - 
10 - CONFIG="$(mktemp)"
11 - CSR="$1"
12 - KEYFILE="$2"
13 - shift; shift
14 - 
15 - echo \
16 - '[ req ]
17 - prompt = no
18 - encrypt_key = no
19 - 
20 - distinguished_name = sec0
21 - req_extensions = sec2
22 - 
23 - [ sec0 ]
24 - O=DOMAIN.EXAMPLE.COM
25 - CN=testuser
26 - 
27 - [ sec1 ]
28 - email = testuser@example.com
29 - 
30 - [ sec2 ]
31 - subjectAltName = @sec1
32 - ' > "$CONFIG"
33 - 
34 - openssl req -new -config "$CONFIG" -out "$CSR" -key "$KEYFILE" "$@"
35 - rm "$CONFIG"
  1 @@ -36,7 +36,7 @@
  2               'example', self.syntax_rule, [self.data_rule])
  3           self.rules = [self.field_mapping]
  4   
  5 -     def rules_for_profile(self, profile_id, helper):
  6 +     def rules_for_profile(self, profile_id):
  7           return self.rules
  8   
  9   
 10 @@ -50,10 +50,6 @@
 11           return {'options': syntax_rules}
 12   
 13   
 14 - class IdentityCSRGenerator(csrgen.CSRGenerator):
 15 -     FORMATTERS = {'identity': IdentityFormatter}
 16 - 
 17 - 
 18   class test_Formatter(object):
 19       def test_prepare_data_rule_with_data_source(self, formatter):
 20           data_rule = csrgen.Rule('uid', '{{subject.uid.0}}',
 21 @@ -139,40 +135,23 @@
 22       def test_rule_basic(self, rule_provider):
 23           rule_name = 'basic'
 24   
 25 -         rule1 = rule_provider._rule(rule_name, 'openssl')
 26 -         rule2 = rule_provider._rule(rule_name, 'certutil')
 27 +         rule = rule_provider._rule(rule_name)
 28   
 29 -         assert rule1.template == 'openssl_rule'
 30 -         assert rule2.template == 'certutil_rule'
 31 +         assert rule.template == 'openssl_rule'
 32   
 33       def test_rule_global_options(self, rule_provider):
 34           rule_name = 'options'
 35   
 36 -         rule1 = rule_provider._rule(rule_name, 'openssl')
 37 -         rule2 = rule_provider._rule(rule_name, 'certutil')
 38 - 
 39 -         assert rule1.options['global_option'] is True
 40 -         assert rule2.options['global_option'] is True
 41 - 
 42 -     def test_rule_helper_options(self, rule_provider):
 43 -         rule_name = 'options'
 44 - 
 45 -         rule1 = rule_provider._rule(rule_name, 'openssl')
 46 -         rule2 = rule_provider._rule(rule_name, 'certutil')
 47 +         rule = rule_provider._rule(rule_name)
 48   
 49 -         assert rule1.options['helper_option'] is True
 50 -         assert 'helper_option' not in rule2.options
 51 +         assert rule.options['rule_option'] is True
 52   
 53       def test_rule_nosuchrule(self, rule_provider):
 54           with pytest.raises(errors.NotFound):
 55 -             rule_provider._rule('nosuchrule', 'openssl')
 56 - 
 57 -     def test_rule_nosuchhelper(self, rule_provider):
 58 -         with pytest.raises(errors.EmptyResult):
 59 -             rule_provider._rule('basic', 'nosuchhelper')
 60 +             rule_provider._rule('nosuchrule')
 61   
 62       def test_rules_for_profile_success(self, rule_provider):
 63 -         rules = rule_provider.rules_for_profile('profile', 'certutil')
 64 +         rules = rule_provider.rules_for_profile('profile')
 65   
 66           assert len(rules) == 1
 67           field_mapping = rules[0]
 68 @@ -182,7 +161,7 @@
 69   
 70       def test_rules_for_profile_nosuchprofile(self, rule_provider):
 71           with pytest.raises(errors.NotFound):
 72 -             rule_provider.rules_for_profile('nosuchprofile', 'certutil')
 73 +             rule_provider.rules_for_profile('nosuchprofile')
 74   
 75   
 76   class test_CSRGenerator(object):
 77 @@ -197,28 +176,9 @@
 78               ],
 79           }
 80   
 81 -         script = generator.csr_script(principal, config, 'userCert', 'openssl')
 82 +         script = generator.csr_script(principal, config, 'userCert')
 83           with open(os.path.join(
 84 -                 CSR_DATA_DIR, 'scripts', 'userCert_openssl.sh')) as f:
 85 -             expected_script = f.read()
 86 -         assert script == expected_script
 87 - 
 88 -     def test_userCert_Certutil(self, generator):
 89 -         principal = {
 90 -             'uid': ['testuser'],
 91 -             'mail': ['testuser@example.com'],
 92 -         }
 93 -         config = {
 94 -             'ipacertificatesubjectbase': [
 95 -                 'O=DOMAIN.EXAMPLE.COM'
 96 -             ],
 97 -         }
 98 - 
 99 -         script = generator.csr_script(
100 -             principal, config, 'userCert', 'certutil')
101 - 
102 -         with open(os.path.join(
103 -                 CSR_DATA_DIR, 'scripts', 'userCert_certutil.sh')) as f:
104 +                 CSR_DATA_DIR, 'configs', 'userCert.conf')) as f:
105               expected_script = f.read()
106           assert script == expected_script
107   
108 @@ -235,28 +195,9 @@
109           }
110   
111           script = generator.csr_script(
112 -             principal, config, 'caIPAserviceCert', 'openssl')
113 -         with open(os.path.join(
114 -                 CSR_DATA_DIR, 'scripts', 'caIPAserviceCert_openssl.sh')) as f:
115 -             expected_script = f.read()
116 -         assert script == expected_script
117 - 
118 -     def test_caIPAserviceCert_Certutil(self, generator):
119 -         principal = {
120 -             'krbprincipalname': [
121 -                 'HTTP/machine.example.com@DOMAIN.EXAMPLE.COM'
122 -             ],
123 -         }
124 -         config = {
125 -             'ipacertificatesubjectbase': [
126 -                 'O=DOMAIN.EXAMPLE.COM'
127 -             ],
128 -         }
129 - 
130 -         script = generator.csr_script(
131 -             principal, config, 'caIPAserviceCert', 'certutil')
132 +             principal, config, 'caIPAserviceCert')
133           with open(os.path.join(
134 -                 CSR_DATA_DIR, 'scripts', 'caIPAserviceCert_certutil.sh')) as f:
135 +                 CSR_DATA_DIR, 'configs', 'caIPAserviceCert.conf')) as f:
136               expected_script = f.read()
137           assert script == expected_script
138   
139 @@ -267,10 +208,11 @@
140           rule_provider = StubRuleProvider()
141           rule_provider.data_rule.template = '{{subject.mail}}'
142           rule_provider.data_rule.options = {'data_source': 'subject.mail'}
143 -         generator = IdentityCSRGenerator(rule_provider)
144 +         generator = csrgen.CSRGenerator(
145 +             rule_provider, formatter_class=IdentityFormatter)
146   
147           script = generator.csr_script(
148 -             principal, {}, 'example', 'identity')
149 +             principal, {}, 'example')
150           assert script == '\n'
151   
152       def test_twoDataRulesOneMissing(self, generator):
153 @@ -280,9 +222,10 @@
154           rule_provider.data_rule.options = {'data_source': 'subject.mail'}
155           rule_provider.field_mapping.data_rules.append(csrgen.Rule(
156               'data2', '{{subject.uid}}', {'data_source': 'subject.uid'}))
157 -         generator = IdentityCSRGenerator(rule_provider)
158 +         generator = csrgen.CSRGenerator(
159 +             rule_provider, formatter_class=IdentityFormatter)
160   
161 -         script = generator.csr_script(principal, {}, 'example', 'identity')
162 +         script = generator.csr_script(principal, {}, 'example')
163           assert script == ',testuser\n'
164   
165       def test_requiredAttributeMissing(self):
166 @@ -291,8 +234,9 @@
167           rule_provider.data_rule.template = '{{subject.mail}}'
168           rule_provider.data_rule.options = {'data_source': 'subject.mail'}
169           rule_provider.syntax_rule.options = {'required': True}
170 -         generator = IdentityCSRGenerator(rule_provider)
171 +         generator = csrgen.CSRGenerator(
172 +             rule_provider, formatter_class=IdentityFormatter)
173   
174           with pytest.raises(errors.CSRTemplateError):
175               _script = generator.csr_script(
176 -                 principal, {}, 'example', 'identity')
177 +                 principal, {}, 'example')