URL: https://github.com/freeipa/freeipa/pull/433
Author: LiptonB
 Title: #433: csrgen: Allow some certificate fields to be specified by the user
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/433/head:pr433
git checkout pr433
From bf80c8e7c5ae1e67a497b06819ef56bfedc4339a Mon Sep 17 00:00:00 2001
From: Ben Lipton <blip...@redhat.com>
Date: Thu, 28 Jul 2016 16:21:44 -0400
Subject: [PATCH 1/3] csrgen: Implement fields that prompt user for data

Allows some data to be user-specified rather than coming out of the
database. The provided data can be formatted with jinja2 rules just as
database values can.

https://fedorahosted.org/freeipa/ticket/4899
---
 ipaclient/csrgen.py                                | 35 ++++++++++++++++++++--
 ipaclient/csrgen/rules/dataEmailUserSpecified.json | 16 ++++++++++
 ipaclient/plugins/csrgen.py                        | 10 +++++--
 ipatests/test_ipaclient/test_csrgen.py             | 11 +++----
 4 files changed, 63 insertions(+), 9 deletions(-)
 create mode 100644 ipaclient/csrgen/rules/dataEmailUserSpecified.json

diff --git a/ipaclient/csrgen.py b/ipaclient/csrgen.py
index 0f52a8b..67442f6 100644
--- a/ipaclient/csrgen.py
+++ b/ipaclient/csrgen.py
@@ -372,8 +372,9 @@ def __init__(self, rule_provider, formatter_class=OpenSSLFormatter):
         self.rule_provider = rule_provider
         self.formatter = formatter_class()
 
-    def csr_config(self, principal, config, profile_id):
-        render_data = {'subject': principal, 'config': config}
+    def csr_config(self, principal, config, userdata, profile_id):
+        render_data = {
+            'subject': principal, 'config': config, 'userdata': userdata}
 
         rules = self.rule_provider.rules_for_profile(profile_id)
         template = self.formatter.build_template(rules)
@@ -387,6 +388,36 @@ def csr_config(self, principal, config, profile_id):
 
         return config
 
+    def get_user_prompts(self, profile_id):
+        prompts = {}
+        rules = self.rule_provider.rules_for_profile(profile_id)
+
+        for field_mapping in rules:
+            for rule in field_mapping.data_rules:
+                if 'prompt' in rule.options:
+                    try:
+                        var = rule.options['data_source']
+                    except KeyError:
+                        raise errors.CSRTemplateError(reason=_(
+                            'Certificate mapping rule %(rule)s has a prompt'
+                            ' but no data_source set') % {'rule': rule.name})
+                    if var in prompts:
+                        raise errors.CSRTemplateError(reason=_(
+                            'More than one data rule in this profile prompts'
+                            ' for the %(item)s data item') % {'item': var})
+                    var_parts = var.split('.')
+                    if len(var_parts) != 2 or var_parts[0] != 'userdata':
+                        raise errors.CSRTemplateError(
+                            reason=_(
+                                'Format of variable name in rule %(rule)s is'
+                                ' incorrect. Rules that prompt for data must'
+                                ' use a variable "userdata.<var_name>"') %
+                            {'rule': rule.name})
+
+                    prompts[var_parts[1]] = rule.options['prompt']
+
+        return prompts
+
 
 class CSRLibraryAdaptor(object):
     def get_subject_public_key_info(self):
diff --git a/ipaclient/csrgen/rules/dataEmailUserSpecified.json b/ipaclient/csrgen/rules/dataEmailUserSpecified.json
new file mode 100644
index 0000000..3fb2fb1
--- /dev/null
+++ b/ipaclient/csrgen/rules/dataEmailUserSpecified.json
@@ -0,0 +1,16 @@
+{
+  "rules": [
+    {
+      "helper": "openssl",
+      "template": "email = {{userdata.email}}"
+    },
+    {
+      "helper": "certutil",
+      "template": "email:{{userdata.email|quote}}"
+    }
+  ],
+  "options": {
+    "data_source": "userdata.email",
+    "prompt": "Email address"
+  }
+}
diff --git a/ipaclient/plugins/csrgen.py b/ipaclient/plugins/csrgen.py
index 568a79f..6503f57 100644
--- a/ipaclient/plugins/csrgen.py
+++ b/ipaclient/plugins/csrgen.py
@@ -90,6 +90,9 @@ def execute(self, *args, **options):
         if not backend.isconnected():
             backend.connect()
 
+        generator = csrgen.CSRGenerator(csrgen.FileRuleProvider())
+        prompts = generator.get_user_prompts(profile_id, helper)
+
         try:
             if principal.is_host:
                 principal_obj = api.Command.host_show(
@@ -106,9 +109,12 @@ def execute(self, *args, **options):
         principal_obj = principal_obj['result']
         config = api.Command.config_show()['result']
 
-        generator = csrgen.CSRGenerator(csrgen.FileRuleProvider())
+        userdata = {}
+        for name, prompt in prompts.items():
+            userdata[name] = self.Backend.textui.prompt(prompt)
 
-        csr_config = generator.csr_config(principal_obj, config, profile_id)
+        csr_config = generator.csr_config(
+            principal_obj, config, userdata, profile_id)
         request_info = base64.b64encode(csrgen_ffi.build_requestinfo(
             csr_config.encode('utf8'), public_key_info))
 
diff --git a/ipatests/test_ipaclient/test_csrgen.py b/ipatests/test_ipaclient/test_csrgen.py
index d0798f8..85bd75c 100644
--- a/ipatests/test_ipaclient/test_csrgen.py
+++ b/ipatests/test_ipaclient/test_csrgen.py
@@ -176,7 +176,8 @@ def test_userCert_OpenSSL(self, generator):
             ],
         }
 
-        script = generator.csr_config(principal, config, 'userCert')
+        script = generator.csr_config(
+            principal, config, {}, 'userCert')
         with open(os.path.join(
                 CSR_DATA_DIR, 'configs', 'userCert.conf')) as f:
             expected_script = f.read()
@@ -195,7 +196,7 @@ def test_caIPAserviceCert_OpenSSL(self, generator):
         }
 
         script = generator.csr_config(
-            principal, config, 'caIPAserviceCert')
+            principal, config, {}, 'caIPAserviceCert')
         with open(os.path.join(
                 CSR_DATA_DIR, 'configs', 'caIPAserviceCert.conf')) as f:
             expected_script = f.read()
@@ -212,7 +213,7 @@ def test_optionalAttributeMissing(self, generator):
             rule_provider, formatter_class=IdentityFormatter)
 
         script = generator.csr_config(
-            principal, {}, 'example')
+            principal, {}, {}, 'example')
         assert script == '\n'
 
     def test_twoDataRulesOneMissing(self, generator):
@@ -225,7 +226,7 @@ def test_twoDataRulesOneMissing(self, generator):
         generator = csrgen.CSRGenerator(
             rule_provider, formatter_class=IdentityFormatter)
 
-        script = generator.csr_config(principal, {}, 'example')
+        script = generator.csr_config(principal, {}, {}, 'example')
         assert script == ',testuser\n'
 
     def test_requiredAttributeMissing(self):
@@ -239,4 +240,4 @@ def test_requiredAttributeMissing(self):
 
         with pytest.raises(errors.CSRTemplateError):
             _script = generator.csr_config(
-                principal, {}, 'example')
+                principal, {}, {}, 'example')

From e8be6c83740b8b182b070890d36b43cf85912e75 Mon Sep 17 00:00:00 2001
From: Ben Lipton <blip...@redhat.com>
Date: Mon, 29 Aug 2016 12:36:17 -0400
Subject: [PATCH 2/3] csrgen: Run user prompts through gettext before
 displaying

Currently doesn't change anything because the strings are not
translated.  Need to find a way to include them in the translation
files.

https://fedorahosted.org/freeipa/ticket/4899
---
 ipaclient/csrgen.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaclient/csrgen.py b/ipaclient/csrgen.py
index 67442f6..fc2ada3 100644
--- a/ipaclient/csrgen.py
+++ b/ipaclient/csrgen.py
@@ -414,7 +414,7 @@ def get_user_prompts(self, profile_id):
                                 ' use a variable "userdata.<var_name>"') %
                             {'rule': rule.name})
 
-                    prompts[var_parts[1]] = rule.options['prompt']
+                    prompts[var_parts[1]] = _(rule.options['prompt'])
 
         return prompts
 

From d8ee8b09b50a0c70282137ab49234823f3c5352b Mon Sep 17 00:00:00 2001
From: Ben Lipton <blip...@redhat.com>
Date: Wed, 14 Sep 2016 13:16:51 -0400
Subject: [PATCH 3/3] tests: Add tests for handling of user-specified data

https://fedorahosted.org/freeipa/ticket/4899
---
 ipatests/test_ipaclient/test_csrgen.py | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/ipatests/test_ipaclient/test_csrgen.py b/ipatests/test_ipaclient/test_csrgen.py
index 85bd75c..c260795 100644
--- a/ipatests/test_ipaclient/test_csrgen.py
+++ b/ipatests/test_ipaclient/test_csrgen.py
@@ -7,6 +7,7 @@
 
 from ipaclient import csrgen
 from ipalib import errors
+from ipalib.text import _
 
 BASE_DIR = os.path.dirname(__file__)
 CSR_DATA_DIR = os.path.join(BASE_DIR, 'data', 'test_csrgen')
@@ -241,3 +242,30 @@ def test_requiredAttributeMissing(self):
         with pytest.raises(errors.CSRTemplateError):
             _script = generator.csr_config(
                 principal, {}, {}, 'example')
+
+    def test_get_user_prompts(self):
+        rule_provider = StubRuleProvider()
+        rule_provider.data_rule.options = {
+            'data_source': 'userdata.nickname', 'prompt': "Nickname"}
+        generator = csrgen.CSRGenerator(
+            rule_provider, formatter_class=IdentityFormatter)
+
+        prompts = generator.get_user_prompts('example')
+
+        expected_prompts = {'nickname': _('Nickname')}
+        assert prompts == expected_prompts
+
+    def test_userdata_included(self):
+        principal = {'uid': 'testuser'}
+        userdata = {'nickname': 'mynick'}
+        rule_provider = StubRuleProvider()
+        rule_provider.data_rule.template = 'nickname:{{userdata.nickname}}'
+        rule_provider.data_rule.options = {
+            'data_source': 'userdata.nickname', 'prompt': "Nickname"}
+        generator = csrgen.CSRGenerator(
+            rule_provider, formatter_class=IdentityFormatter)
+
+        script = generator.csr_config(
+            principal, {}, userdata, 'example')
+        expected_script = 'nickname:mynick\n'
+        assert script == expected_script
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to