diff options
author | Kali Kaneko <kali@leap.se> | 2017-02-14 23:40:29 +0100 |
---|---|---|
committer | Kali Kaneko <kali@leap.se> | 2017-02-15 01:20:57 +0100 |
commit | ccb280703ba851265702b8a92cdedb294cc93608 (patch) | |
tree | a2fc6844b3f10707a4c04f073b3711cc7c24fed0 /server | |
parent | ed85f545a9965eb637c1544b3973e3d68e5a602c (diff) |
[feature] authenticate as anonymous if no token in header
and serve / banner and robots to anon users.
instead of returning 401 for all cases, I treat the unauthenticated case
as a special case, and switch the service tree apart.
this allows to serve a different resource tree to unauthenticated users.
the new URLs are registered with the mapper.
I don't really like that dependency, could be handled by twisted alone, but meh.
- Resolves: #8764
Diffstat (limited to 'server')
-rw-r--r-- | server/src/leap/soledad/server/_resource.py | 19 | ||||
-rw-r--r-- | server/src/leap/soledad/server/auth.py | 26 | ||||
-rw-r--r-- | server/src/leap/soledad/server/session.py | 8 | ||||
-rw-r--r-- | server/src/leap/soledad/server/url_mapper.py | 3 |
4 files changed, 50 insertions, 6 deletions
diff --git a/server/src/leap/soledad/server/_resource.py b/server/src/leap/soledad/server/_resource.py index 156e18aa..e04c0708 100644 --- a/server/src/leap/soledad/server/_resource.py +++ b/server/src/leap/soledad/server/_resource.py @@ -24,7 +24,24 @@ from ._server_info import ServerInfo from ._wsgi import get_sync_resource -__all__ = ['SoledadResource'] +__all__ = ['SoledadResource', 'SoledadAnonResource'] + + +class Robots(Resource): + def render_GET(self, request): + return 'robots, go away! please!' + + +class SoledadAnonResource(Resource): + """ + The parts of Soledad Server that unauthenticated users can see + """ + + def __init__(self, enable_blobs=False): + Resource.__init__(self) + server_info = ServerInfo(enable_blobs) + self.putChild('', server_info) + self.putChild('robots.txt', Robots()) class SoledadResource(Resource): diff --git a/server/src/leap/soledad/server/auth.py b/server/src/leap/soledad/server/auth.py index 7112aa35..6ce11e71 100644 --- a/server/src/leap/soledad/server/auth.py +++ b/server/src/leap/soledad/server/auth.py @@ -26,19 +26,25 @@ from zope.interface import implementer from twisted.cred import error from twisted.cred.checkers import ICredentialsChecker from twisted.cred.credentials import IUsernamePassword +from twisted.cred.credentials import IAnonymous +from twisted.cred.credentials import Anonymous from twisted.cred.credentials import UsernamePassword from twisted.cred.portal import IRealm from twisted.cred.portal import Portal +from twisted.logger import Logger from twisted.internet import defer from twisted.web.iweb import ICredentialFactory from twisted.web.resource import IResource from leap.soledad.common.couch import couch_server -from ._resource import SoledadResource +from ._resource import SoledadResource, SoledadAnonResource from ._config import get_config +log = Logger() + + @implementer(IRealm) class SoledadRealm(object): @@ -49,8 +55,17 @@ class SoledadRealm(object): self._sync_pool = sync_pool def requestAvatar(self, avatarId, mind, *interfaces): + log.warn('avatarId {0}'.format(avatarId)) + enable_blobs = self._conf['blobs'] + + # Anonymous access + if IAnonymous.providedBy(avatarId): + resource = SoledadAnonResource( + enable_blobs=enable_blobs) + return (IResource, resource, lambda: None) + + # Authenticated users if IResource in interfaces: - enable_blobs = self._conf['blobs'] resource = SoledadResource( enable_blobs=enable_blobs, sync_pool=self._sync_pool) @@ -61,7 +76,7 @@ class SoledadRealm(object): @implementer(ICredentialsChecker) class TokenChecker(object): - credentialInterfaces = [IUsernamePassword] + credentialInterfaces = [IUsernamePassword, IAnonymous] TOKENS_DB_PREFIX = "tokens_" TOKENS_DB_EXPIRE = 30 * 24 * 3600 # 30 days in seconds @@ -97,6 +112,10 @@ class TokenChecker(object): return db def requestAvatarId(self, credentials): + if IAnonymous.providedBy(credentials): + log.warn('we are anon') + return defer.succeed(Anonymous()) + uuid = credentials.username token = credentials.password @@ -106,6 +125,7 @@ class TokenChecker(object): db = self._tokens_db() token = db.get(sha512(token).hexdigest()) if token is None: + log.warn('token is none') return defer.fail(error.UnauthorizedLogin()) # TODO -- use cryptography constant time builtin comparison. diff --git a/server/src/leap/soledad/server/session.py b/server/src/leap/soledad/server/session.py index a2793bd3..70e4a35b 100644 --- a/server/src/leap/soledad/server/session.py +++ b/server/src/leap/soledad/server/session.py @@ -19,8 +19,9 @@ Twisted resource containing an authenticated Soledad session. """ from zope.interface import implementer +from twisted.cred.credentials import Anonymous from twisted.cred import error -from twisted.python import log +from twisted.logger import Logger from twisted.web import util from twisted.web._auth import wrapper from twisted.web.guard import HTTPAuthSessionWrapper @@ -32,6 +33,9 @@ from leap.soledad.server.auth import credentialFactory from leap.soledad.server.url_mapper import URLMapper +log = Logger() + + @implementer(IResource) class UnauthorizedResource(wrapper.UnauthorizedResource): isLeaf = True @@ -80,7 +84,7 @@ class SoledadSession(HTTPAuthSessionWrapper): # get authorization header or fail header = request.getHeader(b'authorization') if not header: - return UnauthorizedResource() + return util.DeferredResource(self._login(Anonymous())) # parse the authorization header auth_data = self._parseHeader(header) diff --git a/server/src/leap/soledad/server/url_mapper.py b/server/src/leap/soledad/server/url_mapper.py index 483f7e87..a0edeaca 100644 --- a/server/src/leap/soledad/server/url_mapper.py +++ b/server/src/leap/soledad/server/url_mapper.py @@ -53,6 +53,7 @@ class URLMapper(object): URL path | Authorized actions -------------------------------------------------- / | GET + /robots.txt | GET /shared-db | GET /shared-db/docs | - /shared-db/doc/{any_id} | GET, PUT, DELETE @@ -64,6 +65,8 @@ class URLMapper(object): """ # auth info for global resource self._connect('/', ['GET']) + # robots + self._connect('/robots.txt', ['GET']) # auth info for shared-db database resource self._connect('/%s' % SHARED_DB_NAME, ['GET']) # auth info for shared-db doc resource |