From 8a4692dcd5b090ca67953985d97c9e51208f8cc3 Mon Sep 17 00:00:00 2001 From: meejah Date: Tue, 26 Jan 2016 23:49:23 -0700 Subject: [PATCH] Add simple auth-token to get JSON data --- src/allmydata/client.py | 31 +++++++++++++++++++++++ src/allmydata/scripts/magic_folder_cli.py | 10 ++++++-- src/allmydata/web/magic_folder.py | 10 +++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/allmydata/client.py b/src/allmydata/client.py index a919d9a1..12ebb148 100644 --- a/src/allmydata/client.py +++ b/src/allmydata/client.py @@ -1,5 +1,6 @@ import os, stat, time, weakref from allmydata import node +from base64 import urlsafe_b64encode from zope.interface import implements from twisted.internet import reactor, defer @@ -334,6 +335,9 @@ class Client(node.Node, pollmixin.PollMixin): DEP["n"] = int(self.get_config("client", "shares.total", DEP["n"])) DEP["happy"] = int(self.get_config("client", "shares.happy", DEP["happy"])) + # for the CLI to authenticate to local JSON endpoints + self._auth_token = self._create_or_read_auth_token() + self.init_client_storage_broker() self.history = History(self.stats_provider) self.terminator = Terminator() @@ -343,6 +347,33 @@ class Client(node.Node, pollmixin.PollMixin): self.init_blacklist() self.init_nodemaker() + def get_auth_token(self): + """ + This returns a local authentication token, which is just some + random data in "api_auth_token" which must be echoed to API + calls. + + Currently only the URI '/magic' for magic-folder status; other + endpoints are invited to include this as well, as appropriate. + """ + return self._auth_token + + def _create_or_read_auth_token(self): + """ + This returns the current auth-token data, possibly creating it and + writing 'private/api_auth_token' in the process. + """ + fname = os.path.join(self.basedir, 'private', 'api_auth_token') + try: + with open(fname, 'rb') as f: + data = f.read() + except (OSError, IOError): + log.msg("Creating '%s'." % (fname,)) + with open(fname, 'wb') as f: + data = urlsafe_b64encode(os.urandom(32)) + f.write(data) + return data + def init_client_storage_broker(self): # create a StorageFarmBroker object, for use by Uploader/Downloader # (and everybody else who wants to use storage servers) diff --git a/src/allmydata/scripts/magic_folder_cli.py b/src/allmydata/scripts/magic_folder_cli.py index 4c9a469b..dc26a36b 100644 --- a/src/allmydata/scripts/magic_folder_cli.py +++ b/src/allmydata/scripts/magic_folder_cli.py @@ -217,7 +217,7 @@ class StatusOptions(BasedirOptions): self['node-url'] = f.read().strip() -def _get_json_for_fragment(options, fragment): +def _get_json_for_fragment(options, fragment, method='GET'): nodeurl = options['node-url'] if nodeurl.endswith('/'): nodeurl = nodeurl[:-1] @@ -334,7 +334,13 @@ def status(options): nice_created = humanize.naturaltime(now - created) print " %s (%s): %s, version=%s, created %s" % (n, nice_size, status, version, nice_created) - magicdata = _get_json_for_fragment(options, 'magic_folder?t=json') + with open(os.path.join(nodedir, u'private', u'api_auth_token'), 'rb') as f: + token = f.read() + magicdata = _get_json_for_fragment( + options, + 'magic_folder?t=json&token=' + token, + method='POST', + ) if len(magicdata): uploads = [item for item in magicdata if item['kind'] == 'upload'] downloads = [item for item in magicdata if item['kind'] == 'download'] diff --git a/src/allmydata/web/magic_folder.py b/src/allmydata/web/magic_folder.py index 5d2f3e5e..bf72cc39 100644 --- a/src/allmydata/web/magic_folder.py +++ b/src/allmydata/web/magic_folder.py @@ -1,5 +1,7 @@ import simplejson +from twisted.web.server import UnsupportedMethod + from nevow import rend, url, tags as T from nevow.inevow import IRequest @@ -12,7 +14,6 @@ class MagicFolderWebApi(rend.Page): """ def __init__(self, client): - ##rend.Page.__init__(self, storage) super(MagicFolderWebApi, self).__init__(client) self.client = client @@ -47,6 +48,13 @@ class MagicFolderWebApi(rend.Page): def renderHTTP(self, ctx): req = IRequest(ctx) t = get_arg(req, "t", None) + if req.method != 'POST': + raise UnsupportedMethod(('POST',)) + + token = get_arg(req, "token", None) + # XXX need constant-time comparison? + if token is None or token != self.client.get_auth_token(): + raise WebError("Missing or invalid token.", 400) if t is None: return rend.Page.renderHTTP(self, ctx) -- 2.45.2