diff --git a/certidude/api/token.py b/certidude/api/token.py index a34a101..d9a4fa1 100644 --- a/certidude/api/token.py +++ b/certidude/api/token.py @@ -37,11 +37,11 @@ class TokenResource(object): margin = 300 # Tolerate 5 minute clock skew as Kerberos does if csum.hexdigest() != req.get_param("c", required=True): - raise falcon.HTTPUnauthorized("Forbidden", "Invalid token supplied, did you copy-paste link correctly?") + raise falcon.HTTPForbidden("Forbidden", "Invalid token supplied, did you copy-paste link correctly?") if now < timestamp - margin: - raise falcon.HTTPUnauthorized("Forbidden", "Token not valid yet, are you sure server clock is correct?") + raise falcon.HTTPForbidden("Forbidden", "Token not valid yet, are you sure server clock is correct?") if now > timestamp + margin + config.TOKEN_LIFETIME: - raise falcon.HTTPUnauthorized("Forbidden", "Token expired") + raise falcon.HTTPForbidden("Forbidden", "Token expired") # At this point consider token to be legitimate @@ -88,9 +88,12 @@ class TokenResource(object): # Token lifetime in local time, to select timezone: dpkg-reconfigure tzdata token_created = datetime.fromtimestamp(timestamp) token_expires = datetime.fromtimestamp(timestamp + config.TOKEN_LIFETIME) - with open("/etc/timezone") as fh: - token_timezone = fh.read().strip() - + try: + with open("/etc/timezone") as fh: + token_timezone = fh.read().strip() + except EnvironmentError: + token_timezone = None context = globals() context.update(locals()) mailer.send("token.md", to=user, **context) + resp.body = args diff --git a/certidude/templates/mail/token.md b/certidude/templates/mail/token.md index 56a4be1..fdb077a 100644 --- a/certidude/templates/mail/token.md +++ b/certidude/templates/mail/token.md @@ -15,5 +15,5 @@ To set up OpenVPN for your device: {% endif %} Click [here]({{ config.TOKEN_URL }}?{{ args }}) to claim the token. -Token is usable until {{ token_expires }} ({{ token_timezone }} time). +Token is usable until {{ token_expires }}{% if token_timezone %} ({{ token_timezone }} time){% endif %}. diff --git a/tests/test_cli.py b/tests/test_cli.py index 85e013d..69c3027 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -258,11 +258,9 @@ def test_cli_setup_authority(): # Test static r = client().simulate_delete("/nonexistant.html") assert r.status_code == 404 - r = client().simulate_delete("/index.html") assert r.status_code == 200 - # Log can be read only by admin r = client().simulate_get("/api/log/") assert r.status_code == 401 @@ -273,3 +271,29 @@ def test_cli_setup_authority(): headers={"Authorization":admintoken}) assert r.status_code == 200 assert r.headers.get('content-type') == "application/json; charset=UTF-8" + + # Test token mech + r = client().simulate_post("/api/token/") + assert r.status_code == 404 + + config.BUNDLE_FORMAT = "ovpn" + config.USER_ENROLLMENT_ALLOWED = True + + r = client().simulate_post("/api/token/") + assert r.status_code == 401 # needs auth + r = client().simulate_post("/api/token/", + headers={"Authorization":usertoken}) + assert r.status_code == 403 # regular user forbidden + r = client().simulate_post("/api/token/", + body="user=userbot", # TODO: test nonexistant user + headers={"content-type": "application/x-www-form-urlencoded", "Authorization":admintoken}) + assert r.status_code == 200 # token generated by admin + + r2 = client().simulate_get("/api/token/", + query_string="u=userbot&t=1493184342&c=ac9b71421d5741800c5a4905b20c1072594a2df863e60ba836464888786bf2a6", + headers={"content-type": "application/x-www-form-urlencoded", "Authorization":admintoken}) + assert r2.status_code == 403 # invalid checksum/timestamp + r2 = client().simulate_get("/api/token/", query_string=r.content, + headers={"User-Agent":"Mozilla/5.0 (X11; Fedora; Linux x86_64) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}) + assert r2.status_code == 200 # token consumed by anyone