Repository: privacyidea/privacyidea

Issue:
CA connector not accessible if template file not found
The /caconnector/ [GET] endpoint tries to read the templates file. The request fails if the file is not there or not readable.

The exception looks as follows:

```python
[2020-09-07 07:02:07,480][7805][140558478960384][ERROR][privacyidea.app:1892] Exception on /caconnector/ [GET]
Traceback (most recent call last):
  File "/opt/privacyidea/lib/python3.6/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/opt/privacyidea/lib/python3.6/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/opt/privacyidea/lib/python3.6/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/opt/privacyidea/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/opt/privacyidea/lib/python3.6/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/opt/privacyidea/lib/python3.6/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/opt/privacyidea/lib/python3.6/site-packages/privacyidea/lib/log.py", line 155, in log_wrapper
    return func(*args, **kwds)
  File "/opt/privacyidea/lib/python3.6/site-packages/privacyidea/api/lib/prepolicy.py", line 154, in policy_wrapper
    return wrapped_function(*args, **kwds)
  File "/opt/privacyidea/lib/python3.6/site-packages/privacyidea/api/caconnector.py", line 54, in get_caconnector_api
    return_config=True)  # the endpoint is only accessed by admins
  File "/opt/privacyidea/lib/python3.6/site-packages/privacyidea/lib/caconnector.py", line 169, in get_caconnector_list
    c_obj = c_obj_class(conn.name, data)
  File "/opt/privacyidea/lib/python3.6/site-packages/privacyidea/lib/caconnectors/localca.py", line 285, in __init__
    self.set_config(config)
  File "/opt/privacyidea/lib/python3.6/site-packages/privacyidea/lib/caconnectors/localca.py", line 327, in set_config
    self.templates = self.get_templates()
  File "/opt/privacyidea/lib/python3.6/site-packages/privacyidea/lib/caconnectors/localca.py", line 471, in get_templates
    with open(self.template_file, 'r') as content_file:
FileNotFoundError: [Errno 2] No such file or directory: '/nonexisting/path/to/templates.conf'
```

The error states the problem, but in case of one broken file entry, no connectors are listed. We should introduce a softer handling for this. Maybe in [localca.py](https://github.com/privacyidea/privacyidea/blob/4929d4e0a7a11cbc564a88d034f7903665ba8024/privacyidea/lib/caconnectors/localca.py#L465) like this. But I'm not sure where the CAError goes to...
```python
    def get_templates(self):
        """
        Return the dict of available templates, which are read from the
        template YAML file.

        :return: dict
        """
        content = {}
        if self.template_file:
            if os.access(self.template_file, os.R_OK):
                with open(self.template_file, 'r') as content_file:
                    file_content = content_file.read()
                    content = yaml.safe_load(file_content)
            else:
                CAError("The Certificates template file {0!s} is not readable.",
                        self.template_file)
        return content
```


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/caconnectors/localca.py b/privacyidea/lib/caconnectors/localca.py
--- a/privacyidea/lib/caconnectors/localca.py
+++ b/privacyidea/lib/caconnectors/localca.py
@@ -40,6 +40,7 @@
 import os
 from six import string_types
 from six.moves import input
+import traceback
 
 log = logging.getLogger(__name__)
 
@@ -468,9 +469,14 @@ def get_templates(self):
         """
         content = {}
         if self.template_file:
-            with open(self.template_file, 'r') as content_file:
-                file_content = content_file.read()
-                content = yaml.safe_load(file_content)
+            try:
+                with open(self.template_file, 'r') as content_file:
+                    file_content = content_file.read()
+                    content = yaml.safe_load(file_content)
+            except EnvironmentError:
+                log.warning("Template file {0!s} for {1!s} not found or "
+                            "not permitted.".format(self.template_file, self.name))
+                log.debug(u'{0!s}'.format(traceback.format_exc()))
         return content
 
     def publish_cert(self):


Test patch:
diff --git a/tests/test_lib_caconnector.py b/tests/test_lib_caconnector.py
--- a/tests/test_lib_caconnector.py
+++ b/tests/test_lib_caconnector.py
@@ -324,6 +324,10 @@ def test_05_templates(self):
         self.assertTrue(ddiff.days > 740, ddiff.days)
         self.assertTrue(ddiff.days < 760, ddiff.days)
 
+        # in case of a nonexistent template file, no exception is raised
+        # but an empty value is returned
+        cacon.template_file = "nonexistent"
+        self.assertEquals(cacon.get_templates(), {})
 
 class CreateLocalCATestCase(MyTestCase):
     """