End a user's sessions when the account is deactivated
There is no token denylist in this product, so the JWT user loader is
the only place a deactivated account can be stopped, and it did a bare
lookup by id. Somebody deactivated at 09:00 kept a working access token
until it expired, and a refresh token that minted fresh ones for thirty
days.
/api/auth/refresh and the API-token path already checked isactive. This
makes the loader consistent with them rather than inventing policy.
Scope: product
(cherry picked from commit 9c1848fc61)
This commit is contained in:
61
tests/test_core/test_deactivated_user.py
Normal file
61
tests/test_core/test_deactivated_user.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""Deactivating an account ends its sessions.
|
||||
|
||||
There is no token denylist in this product, so the JWT user loader is the only
|
||||
place a deactivated account can be stopped. It did a bare lookup by id, which
|
||||
meant deactivating somebody left their access token working until it expired and
|
||||
their refresh token minting new ones for thirty days. /auth/refresh and the API
|
||||
token path already checked isactive; the loader did not.
|
||||
"""
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
|
||||
def _login(client, username='deactivateme', password='testpass'):
|
||||
response = client.post('/api/auth/login',
|
||||
json={'username': username, 'password': password})
|
||||
assert response.status_code == 200, response.get_json()
|
||||
return response.get_json()['data']
|
||||
|
||||
|
||||
def _make_user(db, username='deactivateme'):
|
||||
from shopdb.core.models import User
|
||||
|
||||
user = User(
|
||||
username=username,
|
||||
email=f'{username}@test.local',
|
||||
passwordhash=generate_password_hash('testpass'),
|
||||
)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return user
|
||||
|
||||
|
||||
def test_a_live_token_stops_working_once_the_account_is_deactivated(client, db):
|
||||
user = _make_user(db)
|
||||
tokens = _login(client)
|
||||
headers = {'Authorization': 'Bearer {}'.format(tokens['access_token'])}
|
||||
|
||||
assert client.get('/api/auth/me', headers=headers).status_code == 200
|
||||
|
||||
user.isactive = False
|
||||
db.session.commit()
|
||||
|
||||
assert client.get('/api/auth/me', headers=headers).status_code == 401
|
||||
|
||||
|
||||
def test_a_deactivated_account_cannot_refresh(client, db):
|
||||
user = _make_user(db, 'refreshme')
|
||||
tokens = _login(client, 'refreshme')
|
||||
headers = {'Authorization': 'Bearer {}'.format(tokens['refresh_token'])}
|
||||
|
||||
user.isactive = False
|
||||
db.session.commit()
|
||||
|
||||
assert client.post('/api/auth/refresh', headers=headers).status_code == 401
|
||||
|
||||
|
||||
def test_an_active_account_is_unaffected(client, db):
|
||||
"""The loader must not reject everybody; isactive defaults true."""
|
||||
_make_user(db, 'stillhere')
|
||||
tokens = _login(client, 'stillhere')
|
||||
headers = {'Authorization': 'Bearer {}'.format(tokens['access_token'])}
|
||||
assert client.get('/api/auth/me', headers=headers).status_code == 200
|
||||
Reference in New Issue
Block a user