Hello! We are running our annual fundraising. Please consider making a donation if you value this freely available service or want to support people around the world working towards liberatory social change. https://riseup.net/donate.

Unverified Commit ccb28070 authored by Kali Kaneko's avatar Kali Kaneko
Browse files

[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
parent ed85f545
......@@ -10,6 +10,9 @@ I've added a new category `Misc` so we can track doc/style/packaging stuff.
- Refactor authentication code to use twisted credential system.
- `#8764 <https://0xacab.org/leap/soledad/issues/8764>`_: Allow unauthenticated
users to retrieve the capabilties banner.
- `#1234 <https://leap.se/code/issues/1234>`_: Description of the new feature corresponding with issue #1234.
- New feature without related issue number.
......@@ -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):
server_info = ServerInfo(enable_blobs)
self.putChild('', server_info)
self.putChild('robots.txt', Robots())
class SoledadResource(Resource):
......@@ -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()
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(
return (IResource, resource, lambda: None)
# Authenticated users
if IResource in interfaces:
enable_blobs = self._conf['blobs']
resource = SoledadResource(
......@@ -61,7 +76,7 @@ class SoledadRealm(object):
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.
......@@ -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()
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)
......@@ -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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment