Add base REST provider framework classes
authorRob Crittenden <rcritten@redhat.com>
Thu, 26 Feb 2015 20:50:37 +0000 (15:50 -0500)
committerSimo Sorce <simo@redhat.com>
Fri, 27 Feb 2015 21:05:49 +0000 (16:05 -0500)
These classes handle mounting the REST plugins.

The starting mount point is: /idp/rest/providers

https://fedorahosted.org/ipsilon/ticket/26

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
ipsilon/providers/common.py
ipsilon/rest/__init__.py [new file with mode: 0644]
ipsilon/rest/common.py [new file with mode: 0644]
ipsilon/rest/providers.py [new file with mode: 0644]

index 4206387..dff302d 100644 (file)
@@ -19,6 +19,7 @@ from ipsilon.util.log import Log
 from ipsilon.util.plugin import PluginInstaller, PluginLoader
 from ipsilon.util.plugin import PluginObject, PluginConfig
 from ipsilon.util.page import Page
 from ipsilon.util.plugin import PluginInstaller, PluginLoader
 from ipsilon.util.plugin import PluginObject, PluginConfig
 from ipsilon.util.page import Page
+from ipsilon.rest.common import RestPage
 import cherrypy
 
 
 import cherrypy
 
 
@@ -153,3 +154,43 @@ class ProvidersInstall(object):
     def __init__(self):
         pi = PluginInstaller(ProvidersInstall, FACILITY)
         self.plugins = pi.get_plugins()
     def __init__(self):
         pi = PluginInstaller(ProvidersInstall, FACILITY)
         self.plugins = pi.get_plugins()
+
+
+class RestProviderBase(RestPage):
+
+    def __init__(self, site, config):
+        super(RestProviderBase, self).__init__(site)
+        self.plugin_name = config.name
+        self.cfg = config
+
+    def GET(self, *args, **kwargs):
+        raise cherrypy.HTTPError(501)
+
+    def POST(self, *args, **kwargs):
+        raise cherrypy.HTTPError(501)
+
+    def DELETE(self, *args, **kwargs):
+        raise cherrypy.HTTPError(501)
+
+    def PUT(self, *args, **kwargs):
+        raise cherrypy.HTTPError(501)
+
+    def root(self, *args, **kwargs):
+        method = cherrypy.request.method
+
+        preop = getattr(self, 'pre_%s' % method, None)
+        if preop and callable(preop):
+            preop(*args, **kwargs)
+
+        op = getattr(self, method, self.GET)
+        if callable(op):
+            return op(*args, **kwargs)
+        else:
+            raise cherrypy.HTTPError(405)
+
+    def _debug(self, fact):
+        superfact = '%s: %s' % (self.plugin_name, fact)
+        super(RestProviderBase, self)._debug(superfact)
+
+    def _audit(self, fact):
+        cherrypy.log('%s: %s' % (self.plugin_name, fact))
diff --git a/ipsilon/rest/__init__.py b/ipsilon/rest/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ipsilon/rest/common.py b/ipsilon/rest/common.py
new file mode 100644 (file)
index 0000000..4103e8e
--- /dev/null
@@ -0,0 +1,89 @@
+# Copyright (C) 2015  Ipsilon Contributors see COPYING for license
+
+import cherrypy
+import json
+from functools import wraps
+from ipsilon.util.endpoint import Endpoint
+
+
+def jsonout(func):
+    """
+    JSON output decorator. Does not handle binary data.
+    """
+    @wraps(func)
+    def wrapper(*args, **kw):
+        value = func(*args, **kw)
+        cherrypy.response.headers["Content-Type"] = \
+            "application/json;charset=utf-8"
+        return json.dumps(value, sort_keys=True, indent=2)
+
+    return wrapper
+
+
+def rest_error(status=500, message=''):
+    """
+    Create a REST error response.
+
+    The assumption is that the jsonout wrapper will handle converting
+    the response to JSON.
+    """
+    cherrypy.response.status = status
+    cherrypy.response.headers['Content-Type'] = 'application/json'
+    return {'status': status, 'message': message}
+
+
+class RestPage(Endpoint):
+
+    def __init__(self, *args, **kwargs):
+        super(RestPage, self).__init__(*args, **kwargs)
+        self.default_headers.update({
+            'Cache-Control': 'no-cache, must-revalidate',
+            'Pragma': 'no-cache',
+            'Expires': 'Thu, 01 Dec 1994 16:00:00 GMT',
+        })
+        self.auth_protect = True
+
+
+class RestPlugins(RestPage):
+    def __init__(self, name, site, parent, facility, ordered=True):
+        super(RestPlugins, self).__init__(site)
+        self._master = parent
+        self.name = name
+        self.title = '%s plugins' % name
+        self.url = '%s/%s' % (parent.url, name)
+        self.facility = facility
+        self.template = None
+        self.order = None
+        parent.add_subtree(name, self)
+
+        for plugin in self._site[facility].available:
+            obj = self._site[facility].available[plugin]
+            if hasattr(obj, 'rest'):
+                cherrypy.log.error('Rest plugin: %s' % plugin)
+                obj.rest.mount(self)
+
+    def root_with_msg(self, message=None, message_type=None, changed=None):
+        return None
+
+    def root(self, *args, **kwargs):
+        return self.root_with_msg()
+
+
+class Rest(RestPage):
+
+    def __init__(self, site, mount):
+        super(Rest, self).__init__(site)
+        self.title = None
+        self.mount = mount
+        self.url = '%s/%s' % (self.basepath, mount)
+        self.menu = [self]
+
+    @jsonout
+    def root(self, *args, **kwargs):
+        return rest_error(404, 'Not Found')
+
+    def add_subtree(self, name, page):
+        self.__dict__[name] = page
+
+    def del_subtree(self, name):
+        del self.__dict__[name]
diff --git a/ipsilon/rest/providers.py b/ipsilon/rest/providers.py
new file mode 100644 (file)
index 0000000..b32efc5
--- /dev/null
@@ -0,0 +1,11 @@
+# Copyright (C) 2015  Ipsilon Contributors see COPYING for license
+
+from ipsilon.rest.common import RestPlugins
+from ipsilon.providers.common import FACILITY
+
+
+class RestProviderPlugins(RestPlugins):
+    def __init__(self, site, parent):
+        super(RestProviderPlugins, self).__init__('providers', site, parent,
+                                                  FACILITY, ordered=False)
+        self.title = 'Identity Providers'