From 6043f7966b64d6922987bca9137a524fb06a3379 Mon Sep 17 00:00:00 2001 From: drebs Date: Mon, 19 Dec 2016 09:43:03 -0200 Subject: [refactor] separate url mapper, avoid hanging tests Because the wsgi resource has its own threadpool, tests might get confused when shutting down and the reactor may get clogged waiting for the threadpool to be stopped. By refactoring the URLMapper to its own module, server tests can avoid loading the resource module, where the wsgi threadpool resides, so the threapool will not be started. --- server/src/leap/soledad/server/auth.py | 68 ------------------------- server/src/leap/soledad/server/session.py | 17 ++++++- server/src/leap/soledad/server/url_mapper.py | 74 ++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 70 deletions(-) create mode 100644 server/src/leap/soledad/server/url_mapper.py (limited to 'server/src/leap') diff --git a/server/src/leap/soledad/server/auth.py b/server/src/leap/soledad/server/auth.py index c5b90359..13245cfe 100644 --- a/server/src/leap/soledad/server/auth.py +++ b/server/src/leap/soledad/server/auth.py @@ -21,7 +21,6 @@ import binascii import time from hashlib import sha512 -from routes.mapper import Mapper from zope.interface import implementer from twisted.cred import error @@ -33,9 +32,7 @@ from twisted.cred.portal import Portal from twisted.web.iweb import ICredentialFactory from twisted.web.resource import IResource -from leap.soledad.common import SHARED_DB_NAME from leap.soledad.common.couch import couch_server -from leap.soledad.common.l2db import DBNAME_CONSTRAINTS from leap.soledad.server.resource import SoledadResource from leap.soledad.server.application import get_config @@ -119,68 +116,3 @@ class TokenCredentialFactory(object): portal = Portal(SoledadRealm(), [TokenChecker()]) credentialFactory = TokenCredentialFactory() - - -class URLMapper(object): - """ - Maps the URLs users can access. - """ - - def __init__(self): - self._map = Mapper(controller_scan=None) - self._connect_urls() - self._map.create_regs() - - def match(self, path, method): - environ = {'PATH_INFO': path, 'REQUEST_METHOD': method} - return self._map.match(environ=environ) - - def _connect(self, pattern, http_methods): - self._map.connect( - None, pattern, http_methods=http_methods, - conditions=dict(method=http_methods), - requirements={'dbname': DBNAME_CONSTRAINTS}) - - def _connect_urls(self): - """ - Register the authorization info in the mapper using C{SHARED_DB_NAME} - as the user's database name. - - This method sets up the following authorization rules: - - URL path | Authorized actions - -------------------------------------------------- - / | GET - /shared-db | GET - /shared-db/docs | - - /shared-db/doc/{any_id} | GET, PUT, DELETE - /shared-db/sync-from/{source} | - - /user-db | - - /user-db/docs | - - /user-db/doc/{id} | - - /user-db/sync-from/{source} | GET, PUT, POST - """ - # auth info for global resource - self._connect('/', ['GET']) - # auth info for shared-db database resource - self._connect('/%s' % SHARED_DB_NAME, ['GET']) - # auth info for shared-db doc resource - self._connect('/%s/doc/{id:.*}' % SHARED_DB_NAME, - ['GET', 'PUT', 'DELETE']) - # auth info for user-db sync resource - self._connect('/user-{uuid}/sync-from/{source_replica_uid}', - ['GET', 'PUT', 'POST']) - - -@implementer(IResource) -class UnauthorizedResource(object): - isLeaf = True - - def render(self, request): - request.setResponseCode(401) - if request.method == b'HEAD': - return b'' - return b'Unauthorized' - - def getChildWithDefault(self, path, request): - return self diff --git a/server/src/leap/soledad/server/session.py b/server/src/leap/soledad/server/session.py index 75440089..1ef5b6a6 100644 --- a/server/src/leap/soledad/server/session.py +++ b/server/src/leap/soledad/server/session.py @@ -30,10 +30,9 @@ from twisted.web.server import Session from zope.interface import Interface from zope.interface import Attribute -from leap.soledad.server.auth import URLMapper from leap.soledad.server.auth import portal from leap.soledad.server.auth import credentialFactory -from leap.soledad.server.auth import UnauthorizedResource +from leap.soledad.server.urlmapper import URLMapper from leap.soledad.server.resource import SoledadResource @@ -58,6 +57,20 @@ def _sessionData(request): return data +@implementer(IResource) +class UnauthorizedResource(object): + isLeaf = True + + def render(self, request): + request.setResponseCode(401) + if request.method == b'HEAD': + return b'' + return b'Unauthorized' + + def getChildWithDefault(self, path, request): + return self + + @implementer(IResource) class SoledadSession(HTTPAuthSessionWrapper): diff --git a/server/src/leap/soledad/server/url_mapper.py b/server/src/leap/soledad/server/url_mapper.py new file mode 100644 index 00000000..483f7e87 --- /dev/null +++ b/server/src/leap/soledad/server/url_mapper.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# url_mapper.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +An URL mapper that represents authorized paths. +""" +from routes.mapper import Mapper + +from leap.soledad.common import SHARED_DB_NAME +from leap.soledad.common.l2db import DBNAME_CONSTRAINTS + + +class URLMapper(object): + """ + Maps the URLs users can access. + """ + + def __init__(self): + self._map = Mapper(controller_scan=None) + self._connect_urls() + self._map.create_regs() + + def match(self, path, method): + environ = {'PATH_INFO': path, 'REQUEST_METHOD': method} + return self._map.match(environ=environ) + + def _connect(self, pattern, http_methods): + self._map.connect( + None, pattern, http_methods=http_methods, + conditions=dict(method=http_methods), + requirements={'dbname': DBNAME_CONSTRAINTS}) + + def _connect_urls(self): + """ + Register the authorization info in the mapper using C{SHARED_DB_NAME} + as the user's database name. + + This method sets up the following authorization rules: + + URL path | Authorized actions + -------------------------------------------------- + / | GET + /shared-db | GET + /shared-db/docs | - + /shared-db/doc/{any_id} | GET, PUT, DELETE + /shared-db/sync-from/{source} | - + /user-db | - + /user-db/docs | - + /user-db/doc/{id} | - + /user-db/sync-from/{source} | GET, PUT, POST + """ + # auth info for global resource + self._connect('/', ['GET']) + # auth info for shared-db database resource + self._connect('/%s' % SHARED_DB_NAME, ['GET']) + # auth info for shared-db doc resource + self._connect('/%s/doc/{id:.*}' % SHARED_DB_NAME, + ['GET', 'PUT', 'DELETE']) + # auth info for user-db sync resource + self._connect('/user-{uuid}/sync-from/{source_replica_uid}', + ['GET', 'PUT', 'POST']) -- cgit v1.2.3