1) Pushers are now associated with an access token

2) Change places where we mean unauthenticated to 401, not 403, in C/S v2: hack so it stays as 403 in v1 because web client relies on it.
This commit is contained in:
David Baker 2015-03-24 17:24:15 +00:00
parent d19e79ecc9
commit c7023f2155
8 changed files with 48 additions and 26 deletions

View File

@ -40,6 +40,7 @@ class Auth(object):
self.hs = hs self.hs = hs
self.store = hs.get_datastore() self.store = hs.get_datastore()
self.state = hs.get_state_handler() self.state = hs.get_state_handler()
self.TOKEN_NOT_FOUND_HTTP_STATUS = 401
def check(self, event, auth_events): def check(self, event, auth_events):
""" Checks if this event is correctly authed. """ Checks if this event is correctly authed.
@ -373,7 +374,9 @@ class Auth(object):
defer.returnValue((user, ClientInfo(device_id, token_id))) defer.returnValue((user, ClientInfo(device_id, token_id)))
except KeyError: except KeyError:
raise AuthError(403, "Missing access token.") raise AuthError(
self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token."
)
@defer.inlineCallbacks @defer.inlineCallbacks
def get_user_by_token(self, token): def get_user_by_token(self, token):
@ -387,21 +390,20 @@ class Auth(object):
Raises: Raises:
AuthError if no user by that token exists or the token is invalid. AuthError if no user by that token exists or the token is invalid.
""" """
try: ret = yield self.store.get_user_by_token(token)
ret = yield self.store.get_user_by_token(token) if not ret:
if not ret: raise AuthError(
raise StoreError(400, "Unknown token") self.TOKEN_NOT_FOUND_HTTP_STATUS, "Unrecognised access token.",
user_info = { errcode=Codes.UNKNOWN_TOKEN
"admin": bool(ret.get("admin", False)), )
"device_id": ret.get("device_id"), user_info = {
"user": UserID.from_string(ret.get("name")), "admin": bool(ret.get("admin", False)),
"token_id": ret.get("token_id", None), "device_id": ret.get("device_id"),
} "user": UserID.from_string(ret.get("name")),
"token_id": ret.get("token_id", None),
}
defer.returnValue(user_info) defer.returnValue(user_info)
except StoreError:
raise AuthError(403, "Unrecognised access token.",
errcode=Codes.UNKNOWN_TOKEN)
@defer.inlineCallbacks @defer.inlineCallbacks
def get_appservice_by_req(self, request): def get_appservice_by_req(self, request):
@ -409,11 +411,16 @@ class Auth(object):
token = request.args["access_token"][0] token = request.args["access_token"][0]
service = yield self.store.get_app_service_by_token(token) service = yield self.store.get_app_service_by_token(token)
if not service: if not service:
raise AuthError(403, "Unrecognised access token.", raise AuthError(
errcode=Codes.UNKNOWN_TOKEN) self.TOKEN_NOT_FOUND_HTTP_STATUS,
"Unrecognised access token.",
errcode=Codes.UNKNOWN_TOKEN
)
defer.returnValue(service) defer.returnValue(service)
except KeyError: except KeyError:
raise AuthError(403, "Missing access token.") raise AuthError(
self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token."
)
def is_server_admin(self, user): def is_server_admin(self, user):
return self.store.is_server_admin(user) return self.store.is_server_admin(user)

View File

@ -57,7 +57,7 @@ class PusherPool:
self._start_pushers(pushers) self._start_pushers(pushers)
@defer.inlineCallbacks @defer.inlineCallbacks
def add_pusher(self, user_name, profile_tag, kind, app_id, def add_pusher(self, user_name, access_token, profile_tag, kind, app_id,
app_display_name, device_display_name, pushkey, lang, data): app_display_name, device_display_name, pushkey, lang, data):
# we try to create the pusher just to validate the config: it # we try to create the pusher just to validate the config: it
# will then get pulled out of the database, # will then get pulled out of the database,
@ -79,17 +79,18 @@ class PusherPool:
"failing_since": None "failing_since": None
}) })
yield self._add_pusher_to_store( yield self._add_pusher_to_store(
user_name, profile_tag, kind, app_id, user_name, access_token, profile_tag, kind, app_id,
app_display_name, device_display_name, app_display_name, device_display_name,
pushkey, lang, data pushkey, lang, data
) )
@defer.inlineCallbacks @defer.inlineCallbacks
def _add_pusher_to_store(self, user_name, profile_tag, kind, app_id, def _add_pusher_to_store(self, user_name, access_token, profile_tag, kind,
app_display_name, device_display_name, app_id, app_display_name, device_display_name,
pushkey, lang, data): pushkey, lang, data):
yield self.store.add_pusher( yield self.store.add_pusher(
user_name=user_name, user_name=user_name,
access_token=access_token,
profile_tag=profile_tag, profile_tag=profile_tag,
kind=kind, kind=kind,
app_id=app_id, app_id=app_id,

View File

@ -48,5 +48,5 @@ class ClientV1RestServlet(RestServlet):
self.hs = hs self.hs = hs
self.handlers = hs.get_handlers() self.handlers = hs.get_handlers()
self.builder_factory = hs.get_event_builder_factory() self.builder_factory = hs.get_event_builder_factory()
self.auth = hs.get_auth() self.auth = hs.get_v1auth()
self.txns = HttpTransactionStore() self.txns = HttpTransactionStore()

View File

@ -27,7 +27,7 @@ class PusherRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks @defer.inlineCallbacks
def on_POST(self, request): def on_POST(self, request):
user, _ = yield self.auth.get_user_by_req(request) user, client = yield self.auth.get_user_by_req(request)
content = _parse_json(request) content = _parse_json(request)
@ -54,6 +54,7 @@ class PusherRestServlet(ClientV1RestServlet):
try: try:
yield pusher_pool.add_pusher( yield pusher_pool.add_pusher(
user_name=user.to_string(), user_name=user.to_string(),
access_token=client.token_id,
profile_tag=content['profile_tag'], profile_tag=content['profile_tag'],
kind=content['kind'], kind=content['kind'],
app_id=content['app_id'], app_id=content['app_id'],

View File

@ -65,6 +65,7 @@ class BaseHomeServer(object):
'replication_layer', 'replication_layer',
'datastore', 'datastore',
'handlers', 'handlers',
'v1auth',
'auth', 'auth',
'rest_servlet_factory', 'rest_servlet_factory',
'state_handler', 'state_handler',
@ -182,6 +183,15 @@ class HomeServer(BaseHomeServer):
def build_auth(self): def build_auth(self):
return Auth(self) return Auth(self)
def build_v1auth(self):
orf = Auth(self)
# Matrix spec makes no reference to what HTTP status code is returned,
# but the V1 API uses 403 where it means 401, and the webclient
# relies on this behaviour, so V1 gets its own copy of the auth
# with backwards compat behaviour.
orf.TOKEN_NOT_FOUND_HTTP_STATUS = 403
return orf
def build_state_handler(self): def build_state_handler(self):
return StateHandler(self) return StateHandler(self)

View File

@ -95,7 +95,7 @@ class PusherStore(SQLBaseStore):
defer.returnValue(ret) defer.returnValue(ret)
@defer.inlineCallbacks @defer.inlineCallbacks
def add_pusher(self, user_name, profile_tag, kind, app_id, def add_pusher(self, user_name, access_token, profile_tag, kind, app_id,
app_display_name, device_display_name, app_display_name, device_display_name,
pushkey, pushkey_ts, lang, data): pushkey, pushkey_ts, lang, data):
try: try:
@ -107,6 +107,7 @@ class PusherStore(SQLBaseStore):
), ),
dict( dict(
user_name=user_name, user_name=user_name,
access_token=access_token,
kind=kind, kind=kind,
profile_tag=profile_tag, profile_tag=profile_tag,
app_display_name=app_display_name, app_display_name=app_display_name,

View File

@ -174,4 +174,4 @@ class RegistrationStore(SQLBaseStore):
if rows: if rows:
return rows[0] return rows[0]
raise StoreError(404, "Token not found.") return None

View File

@ -0,0 +1,2 @@
ALTER TABLE pushers ADD COLUMN access_token INTEGER DEFAULT NULL;