summaryrefslogtreecommitdiff
path: root/server/src/leap
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2017-02-14 23:40:29 +0100
committerKali Kaneko <kali@leap.se>2017-02-15 01:20:57 +0100
commitccb280703ba851265702b8a92cdedb294cc93608 (patch)
treea2fc6844b3f10707a4c04f073b3711cc7c24fed0 /server/src/leap
parented85f545a9965eb637c1544b3973e3d68e5a602c (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/src/leap')
-rw-r--r--server/src/leap/soledad/server/_resource.py19
-rw-r--r--server/src/leap/soledad/server/auth.py26
-rw-r--r--server/src/leap/soledad/server/session.py8
-rw-r--r--server/src/leap/soledad/server/url_mapper.py3
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