Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Stop putting a time caveat on access tokens #1656

Merged
merged 2 commits into from
Nov 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,9 +790,6 @@ def validate_macaroon(self, macaroon, type_string, verify_expiry, user_id):
type_string(str): The kind of token required (e.g. "access", "refresh",
"delete_pusher")
verify_expiry(bool): Whether to verify whether the macaroon has expired.
This should really always be True, but there exist access tokens
in the wild which expire when they should not, so we can't
enforce expiry yet.
user_id (str): The user_id required
"""
v = pymacaroons.Verifier()
Expand All @@ -805,11 +802,24 @@ def validate_macaroon(self, macaroon, type_string, verify_expiry, user_id):
v.satisfy_exact("type = " + type_string)
v.satisfy_exact("user_id = %s" % user_id)
v.satisfy_exact("guest = true")

# verify_expiry should really always be True, but there exist access
# tokens in the wild which expire when they should not, so we can't
# enforce expiry yet (so we have to allow any caveat starting with
# 'time < ' in access tokens).
#
# On the other hand, short-term login tokens (as used by CAS login, for
# example) have an expiry time which we do want to enforce.

if verify_expiry:
v.satisfy_general(self._verify_expiry)
else:
v.satisfy_general(lambda c: c.startswith("time < "))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well remove the verify_expiry config option while you are at it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and put a comment explaining why we aren't ever going to check the "time < " caveats.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But leave the v.satisfy_general(lambda c: c.startswith("time < ")) so that existing tokens will still work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except you can't remove the verify_expiry option because it's used in validate_short_term_login_token_and_get_user_id

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you probably want to add a comment to explain what's going on.


# access_tokens and refresh_tokens include a nonce for uniqueness: any
# value is acceptable
v.satisfy_general(lambda c: c.startswith("nonce = "))

v.verify(macaroon, self.hs.config.macaroon_secret_key)

def _verify_expiry(self, caveat):
Expand Down
6 changes: 0 additions & 6 deletions synapse/config/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def read_config(self, config):
)

self.registration_shared_secret = config.get("registration_shared_secret")
self.user_creation_max_duration = int(config["user_creation_max_duration"])

self.bcrypt_rounds = config.get("bcrypt_rounds", 12)
self.trusted_third_party_id_servers = config["trusted_third_party_id_servers"]
Expand All @@ -55,11 +54,6 @@ def default_config(self, **kwargs):
# secret, even if registration is otherwise disabled.
registration_shared_secret: "%(registration_shared_secret)s"

# Sets the expiry for the short term user creation in
# milliseconds. For instance the bellow duration is two weeks
# in milliseconds.
user_creation_max_duration: 1209600000

# Set the number of bcrypt rounds used to generate password hash.
# Larger numbers increase the work factor needed to generate the hash.
# The default number of rounds is 12.
Expand Down
11 changes: 6 additions & 5 deletions synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,14 +538,15 @@ def issue_refresh_token(self, user_id, device_id=None):
device_id)
defer.returnValue(refresh_token)

def generate_access_token(self, user_id, extra_caveats=None,
duration_in_ms=(60 * 60 * 1000)):
def generate_access_token(self, user_id, extra_caveats=None):
extra_caveats = extra_caveats or []
macaroon = self._generate_base_macaroon(user_id)
macaroon.add_first_party_caveat("type = access")
now = self.hs.get_clock().time_msec()
expiry = now + duration_in_ms
macaroon.add_first_party_caveat("time < %d" % (expiry,))
# Include a nonce, to make sure that each login gets a different
# access token.
macaroon.add_first_party_caveat("nonce = %s" % (
stringutils.random_string_with_symbols(16),
))
for caveat in extra_caveats:
macaroon.add_first_party_caveat(caveat)
return macaroon.serialize()
Expand Down
5 changes: 2 additions & 3 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def _submit_captcha(self, ip_addr, private_key, challenge, response):
defer.returnValue(data)

@defer.inlineCallbacks
def get_or_create_user(self, requester, localpart, displayname, duration_in_ms,
def get_or_create_user(self, requester, localpart, displayname,
password_hash=None):
"""Creates a new user if the user does not exist,
else revokes all previous access tokens and generates a new one.
Expand Down Expand Up @@ -399,8 +399,7 @@ def get_or_create_user(self, requester, localpart, displayname, duration_in_ms,

user = UserID(localpart, self.hs.hostname)
user_id = user.to_string()
token = self.auth_handler().generate_access_token(
user_id, None, duration_in_ms)
token = self.auth_handler().generate_access_token(user_id)

if need_register:
yield self.store.register(
Expand Down
12 changes: 0 additions & 12 deletions synapse/rest/client/v1/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,6 @@ class CreateUserRestServlet(ClientV1RestServlet):
def __init__(self, hs):
super(CreateUserRestServlet, self).__init__(hs)
self.store = hs.get_datastore()
self.direct_user_creation_max_duration = hs.config.user_creation_max_duration
self.handlers = hs.get_handlers()

@defer.inlineCallbacks
Expand Down Expand Up @@ -418,18 +417,8 @@ def _do_create(self, requester, user_json):
if "displayname" not in user_json:
raise SynapseError(400, "Expected 'displayname' key.")

if "duration_seconds" not in user_json:
raise SynapseError(400, "Expected 'duration_seconds' key.")

localpart = user_json["localpart"].encode("utf-8")
displayname = user_json["displayname"].encode("utf-8")
duration_seconds = 0
try:
duration_seconds = int(user_json["duration_seconds"])
except ValueError:
raise SynapseError(400, "Failed to parse 'duration_seconds'")
if duration_seconds > self.direct_user_creation_max_duration:
duration_seconds = self.direct_user_creation_max_duration
password_hash = user_json["password_hash"].encode("utf-8") \
if user_json.get("password_hash") else None

Expand All @@ -438,7 +427,6 @@ def _do_create(self, requester, user_json):
requester=requester,
localpart=localpart,
displayname=displayname,
duration_in_ms=(duration_seconds * 1000),
password_hash=password_hash
)

Expand Down
6 changes: 3 additions & 3 deletions tests/handlers/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ def verify_user(caveat):
def verify_type(caveat):
return caveat == "type = access"

def verify_expiry(caveat):
return caveat == "time < 8600000"
def verify_nonce(caveat):
return caveat.startswith("nonce =")

v = pymacaroons.Verifier()
v.satisfy_general(verify_gen)
v.satisfy_general(verify_user)
v.satisfy_general(verify_type)
v.satisfy_general(verify_expiry)
v.satisfy_general(verify_nonce)
v.verify(macaroon, self.hs.config.macaroon_secret_key)

def test_short_term_login_token_gives_user_id(self):
Expand Down
6 changes: 2 additions & 4 deletions tests/handlers/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,12 @@ def setUp(self):

@defer.inlineCallbacks
def test_user_is_created_and_logged_in_if_doesnt_exist(self):
duration_ms = 200
local_part = "someone"
display_name = "someone"
user_id = "@someone:test"
requester = create_requester("@as:test")
result_user_id, result_token = yield self.handler.get_or_create_user(
requester, local_part, display_name, duration_ms)
requester, local_part, display_name)
self.assertEquals(result_user_id, user_id)
self.assertEquals(result_token, 'secret')

Expand All @@ -71,12 +70,11 @@ def test_if_user_exists(self):
user_id=frank.to_string(),
token="jkv;g498752-43gj['eamb!-5",
password_hash=None)
duration_ms = 200
local_part = "frank"
display_name = "Frank"
user_id = "@frank:test"
requester = create_requester("@as:test")
result_user_id, result_token = yield self.handler.get_or_create_user(
requester, local_part, display_name, duration_ms)
requester, local_part, display_name)
self.assertEquals(result_user_id, user_id)
self.assertEquals(result_token, 'secret')