Skip to content
Snippets Groups Projects
Commit 0fdf3088 authored by Erik Johnston's avatar Erik Johnston
Browse files

Track the IP users connect with. Add an admin column to users table.

parent 7a8307fe
No related branches found
No related tags found
No related merge requests found
...@@ -206,6 +206,7 @@ class Auth(object): ...@@ -206,6 +206,7 @@ class Auth(object):
defer.returnValue(True) defer.returnValue(True)
@defer.inlineCallbacks
def get_user_by_req(self, request): def get_user_by_req(self, request):
""" Get a registered user's ID. """ Get a registered user's ID.
...@@ -218,7 +219,14 @@ class Auth(object): ...@@ -218,7 +219,14 @@ class Auth(object):
""" """
# Can optionally look elsewhere in the request (e.g. headers) # Can optionally look elsewhere in the request (e.g. headers)
try: try:
return self.get_user_by_token(request.args["access_token"][0]) access_token = request.args["access_token"][0]
user = yield self.get_user_by_token(access_token)
ip_addr = self.hs.get_ip_from_request(request)
if user and access_token and ip_addr:
self.store.insert_client_ip(user, access_token, ip_addr)
defer.returnValue(user)
except KeyError: except KeyError:
raise AuthError(403, "Missing access token.") raise AuthError(403, "Missing access token.")
......
...@@ -195,13 +195,7 @@ class RegisterRestServlet(RestServlet): ...@@ -195,13 +195,7 @@ class RegisterRestServlet(RestServlet):
raise SynapseError(400, "Captcha response is required", raise SynapseError(400, "Captcha response is required",
errcode=Codes.CAPTCHA_NEEDED) errcode=Codes.CAPTCHA_NEEDED)
# May be an X-Forwarding-For header depending on config ip_addr = self.hs.get_ip_from_request(request)
ip_addr = request.getClientIP()
if self.hs.config.captcha_ip_origin_is_x_forwarded:
# use the header
if request.requestHeaders.hasHeader("X-Forwarded-For"):
ip_addr = request.requestHeaders.getRawHeaders(
"X-Forwarded-For")[0]
handler = self.handlers.registration_handler handler = self.handlers.registration_handler
yield handler.check_recaptcha( yield handler.check_recaptcha(
......
...@@ -143,6 +143,18 @@ class BaseHomeServer(object): ...@@ -143,6 +143,18 @@ class BaseHomeServer(object):
def serialize_event(self, e): def serialize_event(self, e):
return serialize_event(self, e) return serialize_event(self, e)
def get_ip_from_request(self, request):
# May be an X-Forwarding-For header depending on config
ip_addr = request.getClientIP()
if self.config.captcha_ip_origin_is_x_forwarded:
# use the header
if request.requestHeaders.hasHeader("X-Forwarded-For"):
ip_addr = request.requestHeaders.getRawHeaders(
"X-Forwarded-For"
)[0]
return ip_addr
# Build magic accessors for every dependency # Build magic accessors for every dependency
for depname in BaseHomeServer.DEPENDENCIES: for depname in BaseHomeServer.DEPENDENCIES:
BaseHomeServer._make_dependency_method(depname) BaseHomeServer._make_dependency_method(depname)
......
...@@ -63,7 +63,7 @@ SCHEMAS = [ ...@@ -63,7 +63,7 @@ SCHEMAS = [
# Remember to update this number every time an incompatible change is made to # Remember to update this number every time an incompatible change is made to
# database schema files, so the users will be informed on server restarts. # database schema files, so the users will be informed on server restarts.
SCHEMA_VERSION = 4 SCHEMA_VERSION = 5
class _RollbackButIsFineException(Exception): class _RollbackButIsFineException(Exception):
...@@ -294,6 +294,16 @@ class DataStore(RoomMemberStore, RoomStore, ...@@ -294,6 +294,16 @@ class DataStore(RoomMemberStore, RoomStore,
defer.returnValue(self.min_token) defer.returnValue(self.min_token)
def insert_client_ip(self, user, access_token, ip):
return self._simple_insert(
"user_ips",
{
"user": user.to_string(),
"access_token": access_token,
"ip": ip
}
)
def snapshot_room(self, room_id, user_id, state_type=None, state_key=None): def snapshot_room(self, room_id, user_id, state_type=None, state_key=None):
"""Snapshot the room for an update by a user """Snapshot the room for an update by a user
Args: Args:
......
CREATE TABLE IF NOT EXISTS user_ips (
user TEXT NOT NULL,
access_token TEXT NOT NULL,
ip TEXT NOT NULL,
CONSTRAINT user_ip UNIQUE (user, access_token, ip) ON CONFLICT IGNORE
);
CREATE INDEX IF NOT EXISTS user_ips_user ON user_ips(user);
ALTER TABLE users ADD COLUMN admin BOOL DEFAULT 0 NOT NULL;
PRAGMA user_version = 5;
...@@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS users( ...@@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS users(
name TEXT, name TEXT,
password_hash TEXT, password_hash TEXT,
creation_ts INTEGER, creation_ts INTEGER,
admin BOOL DEFAULT 0 NOT NULL,
UNIQUE(name) ON CONFLICT ROLLBACK UNIQUE(name) ON CONFLICT ROLLBACK
); );
...@@ -29,3 +30,13 @@ CREATE TABLE IF NOT EXISTS access_tokens( ...@@ -29,3 +30,13 @@ CREATE TABLE IF NOT EXISTS access_tokens(
FOREIGN KEY(user_id) REFERENCES users(id), FOREIGN KEY(user_id) REFERENCES users(id),
UNIQUE(token) ON CONFLICT ROLLBACK UNIQUE(token) ON CONFLICT ROLLBACK
); );
CREATE TABLE IF NOT EXISTS user_ips (
user TEXT NOT NULL,
access_token TEXT NOT NULL,
ip TEXT NOT NULL,
CONSTRAINT user_ip UNIQUE (user, access_token, ip) ON CONFLICT IGNORE
);
CREATE INDEX IF NOT EXISTS user_ips_user ON user_ips(user);
...@@ -51,10 +51,12 @@ class PresenceStateTestCase(unittest.TestCase): ...@@ -51,10 +51,12 @@ class PresenceStateTestCase(unittest.TestCase):
datastore=Mock(spec=[ datastore=Mock(spec=[
"get_presence_state", "get_presence_state",
"set_presence_state", "set_presence_state",
"insert_client_ip",
]), ]),
http_client=None, http_client=None,
resource_for_client=self.mock_resource, resource_for_client=self.mock_resource,
resource_for_federation=self.mock_resource, resource_for_federation=self.mock_resource,
config=Mock(),
) )
hs.handlers = JustPresenceHandlers(hs) hs.handlers = JustPresenceHandlers(hs)
...@@ -131,10 +133,12 @@ class PresenceListTestCase(unittest.TestCase): ...@@ -131,10 +133,12 @@ class PresenceListTestCase(unittest.TestCase):
"set_presence_list_accepted", "set_presence_list_accepted",
"del_presence_list", "del_presence_list",
"get_presence_list", "get_presence_list",
"insert_client_ip",
]), ]),
http_client=None, http_client=None,
resource_for_client=self.mock_resource, resource_for_client=self.mock_resource,
resource_for_federation=self.mock_resource resource_for_federation=self.mock_resource,
config=Mock(),
) )
hs.handlers = JustPresenceHandlers(hs) hs.handlers = JustPresenceHandlers(hs)
......
...@@ -50,10 +50,10 @@ class ProfileTestCase(unittest.TestCase): ...@@ -50,10 +50,10 @@ class ProfileTestCase(unittest.TestCase):
datastore=None, datastore=None,
) )
def _get_user_by_token(token=None): def _get_user_by_req(request=None):
return hs.parse_userid(myid) return hs.parse_userid(myid)
hs.get_auth().get_user_by_token = _get_user_by_token hs.get_auth().get_user_by_req = _get_user_by_req
hs.get_handlers().profile_handler = self.mock_handler hs.get_handlers().profile_handler = self.mock_handler
......
...@@ -264,6 +264,9 @@ class MemoryDataStore(object): ...@@ -264,6 +264,9 @@ class MemoryDataStore(object):
def get_ops_levels(self, room_id): def get_ops_levels(self, room_id):
return defer.succeed((5, 5, 5)) return defer.succeed((5, 5, 5))
def insert_client_ip(self, user, access_token, ip_addr):
return defer.succeed(None)
def _format_call(args, kwargs): def _format_call(args, kwargs):
return ", ".join( return ", ".join(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment