From 66df05f6cd30821b00cbb6b09dc6c28ae3404b77 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: May 09 2016 15:15:24 +0000 Subject: Implement WebFinger (RFC7033) Ticket: #216 Signed-off-by: Patrick Uiterwijk Reviewed-by: Rob Crittenden --- diff --git a/ipsilon/root.py b/ipsilon/root.py index d701616..5978265 100644 --- a/ipsilon/root.py +++ b/ipsilon/root.py @@ -1,6 +1,7 @@ -# Copyright (C) 2013 Ipsilon project Contributors, for license see COPYING +# Copyright (C) 2013,2016 Ipsilon project Contributors, for license see COPYING from ipsilon.util.page import Page +from ipsilon.util.webfinger import WebFinger from ipsilon.util import errors from ipsilon.login.common import Login from ipsilon.login.common import Logout @@ -33,6 +34,9 @@ class Root(Page): cherrypy.config['error_page.404'] = errors.Error_404(self._site) cherrypy.config['error_page.500'] = errors.Errors(self._site) + # set up WebFinger endpoint + self.webfinger = WebFinger(self._site) + # now set up the default login plugins self.login = Login(self._site) self.logout = Logout(self._site) diff --git a/ipsilon/util/webfinger.py b/ipsilon/util/webfinger.py new file mode 100644 index 0000000..f551f2d --- /dev/null +++ b/ipsilon/util/webfinger.py @@ -0,0 +1,77 @@ +# Copyright (C) 2016 Ipsilon project Contributors, for license see COPYING + +import cherrypy +from ipsilon.util.page import Page +from ipsilon.util.log import Log +from ipsilon.util.endpoint import allow_iframe + +import json + + +class WebFinger(Page, Log): + + def __init__(self, site): + super(WebFinger, self).__init__(site) + self.supported_rels = {} + + @allow_iframe + def root(self, *args, **kwargs): + cherrypy.response.headers.update({ + 'Content-Type': 'application/jrd+json', + 'Access-Control-Allow-Origin': '*' + }) + + if 'resource' not in kwargs: + raise cherrypy.HTTPError(400, 'Missing resource parameter') + + resource = kwargs['resource'] + self.debug('WebFinger request for %s' % resource) + + response = {'subject': resource, + 'links': [], + 'properties': {}} + found = False + + if 'rel' in kwargs: + rels = kwargs['rel'] + if isinstance(rels, basestring): + rels = [rels] + else: + rels = self.supported_rels.keys() + + for rel in rels: + if rel in self.supported_rels: + func = self.supported_rels[rel] + rel_resp = func(resource) + self.debug('Rel %s returned %s' % (rel, rel_resp)) + if 'links' in rel_resp: + if len(rel_resp['links']) > 0: + found = True + response['links'].extend(rel_resp['links']) + if 'properties' in rel_resp: + if len(rel_resp['properties']) > 0: + found = True + response['properties'].update(rel_resp['properties']) + + if not found: + # None of the plugins had any info, we don't know this resource + raise cherrypy.HTTPError(404, 'No info about resource found') + + response['subject'] = resource + + return json.dumps(response) + + def register_rel(self, rel, function): + if rel in self.supported_rels: + raise KeyError('Rel %s already registered' % rel) + + self.debug('WebFinger rel %s registered as %s' + % (rel, function)) + self.supported_rels[rel] = function + + def unregister_rel(self, rel): + if rel not in self.supported_rels: + raise KeyError('Rel %s not registered' % rel) + + self.debug('WebFinger rel %s unregistered' % rel) + del self.supported_rels[rel] diff --git a/templates/install/idp.conf b/templates/install/idp.conf index 7c21774..83cf2a6 100644 --- a/templates/install/idp.conf +++ b/templates/install/idp.conf @@ -1,6 +1,8 @@ Alias /${instance}/ui ${staticdir}/ui Alias /.well-known ${wellknowndir} Alias /${instance}/cache /var/cache/ipsilon +Redirect /${instance}/.well-known/webfinger /${instance}/webfinger + WSGIScriptAlias /${instance} ${ipsilondir}/ipsilon WSGIDaemonProcess ${instance} user=${sysuser} group=${sysuser} home=${datadir} display-name=ipsilon-${instance} ${wsgi_socket}