summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2013-02-26 19:16:09 -0300
committerdrebs <drebs@leap.se>2013-02-26 19:16:09 -0300
commitf5616981e8ba043da71a21371c2cbfc5fcb5da56 (patch)
treee472ebf66847205c2fb9fe570e3d59e2d181852d
parentfa4c1786f1dde0235ba27f2804ae6e3e1fcc8389 (diff)
Add auth middleware structure for soledad.
-rw-r--r--server.py89
1 files changed, 86 insertions, 3 deletions
diff --git a/server.py b/server.py
index eb565e9f..4d30a1f8 100644
--- a/server.py
+++ b/server.py
@@ -6,16 +6,91 @@ This should be run with:
"""
import configparser
+from wsgiref.util import shift_path_info
+
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
+
from u1db.remote import http_app
+
from leap.soledad.backends.couch import CouchServerState
+#-----------------------------------------------------------------------------
+# Authentication
+#-----------------------------------------------------------------------------
+
+class Unauthorized(Exception):
+ """
+ User authentication failed.
+ """
+
+
+class SoledadAuthMiddleware(object):
+ """
+ Soledad Authentication WSGI middleware.
+
+ In general, databases are accessed using a token provided by the LEAP API.
+ Some special databases can be read without authentication.
+ """
+
+ def __init__(self, app, prefix, public_dbs=None):
+ self.app = app
+ self.prefix = prefix
+ self.public_dbs = public_dbs
+
+ def _error(self, start_response, status, description, message=None):
+ start_response("%d %s" % (status, httplib.responses[status]),
+ [('content-type', 'application/json')])
+ err = {"error": description}
+ if message:
+ err['message'] = message
+ return [json.dumps(err)]
+
+ def __call__(self, environ, start_response):
+ if self.prefix and not environ['PATH_INFO'].startswith(self.prefix):
+ return self._error(start_response, 400, "bad request")
+ token = environ.get('HTTP_AUTHORIZATION')
+ if not token:
+ if need_auth(environ):
+ return self._error(start_response, 401, "unauthorized",
+ "Missing Authentication Token.")
+ else:
+ try:
+ self.verify_token(environ, token)
+ except Unauthorized:
+ return self._error(
+ start_response, 401, "unauthorized",
+ "Incorrect password or login.")
+ del environ['HTTP_AUTHORIZATION']
+ shift_path_info(environ)
+ return self.app(environ, start_response)
+
+ def verify_token(self, environ, token):
+ """
+ Verify if token is valid for authenticating this action.
+ """
+ # TODO: implement token verification
+ raise NotImplementedError(self.verify_user)
+
+ def need_auth(self, environ):
+ """
+ Check if action can be performed on database without authentication.
+ """
+ # TODO: implement unauth verification.
+ raise NotImplementedError(self.allow_unauth)
+
+
+#-----------------------------------------------------------------------------
+# Auxiliary functions
+#-----------------------------------------------------------------------------
+
def load_configuration(file_path):
conf = {
'couch_url': 'http://localhost:5984',
'working_dir': '/tmp',
+ 'public_dbs': 'keys'
+ 'prefix': '/soledad/'
}
config = configparser.ConfigParser()
config.read(file_path)
@@ -23,14 +98,22 @@ def load_configuration(file_path):
for key in conf:
if key in config['soledad-server']:
conf[key] = config['soledad-server'][key]
+ # TODO: implement basic parsing of options comming from config file.
return conf
-conf = load_configuration('/etc/leap/soledad-server.ini')
+#-----------------------------------------------------------------------------
+# Run as Twisted WSGI Resource
+#-----------------------------------------------------------------------------
+conf = load_configuration('/etc/leap/soledad-server.ini')
state = CouchServerState(conf['couch_url'])
-# TODO: change working dir to something meaningful
+# TODO: change working dir to something meaningful (maybe eliminate it)
state.set_workingdir(conf['working_dir'])
-application = http_app.HTTPApp(state)
+
+application = SoledadAuthMiddleware(
+ http_app.HTTPApp(state),
+ conf['prefix'],
+ conf['public_dbs'].split(','))
resource = WSGIResource(reactor, reactor.getThreadPool(), application)