mirror of
https://github.com/pretix/pretix.git
synced 2026-09-30 19:04:43 +00:00
[SECURITY] OAuth: Disable existing tokens when deactivating Application (CVE-2026-101271, Z#23247296)
This commit is contained in:
@@ -101,6 +101,10 @@ class OAuthAccessToken(AbstractAccessToken):
|
||||
self.expires = now() - timedelta(hours=1)
|
||||
self.save(update_fields=['expires'])
|
||||
|
||||
def is_valid(self, scopes=None):
|
||||
# Can maybe be removed after upgrading django-oauth-toolkit to 3.4.1
|
||||
return super().is_valid(scopes) and self.application.is_usable(None)
|
||||
|
||||
|
||||
class OAuthRefreshToken(AbstractRefreshToken):
|
||||
application = models.ForeignKey(
|
||||
|
||||
@@ -70,14 +70,27 @@ def admin_user(admin_team):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application():
|
||||
def app_developer():
|
||||
return User.objects.create_user('app-developer@example.org', 'app-developer')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client2():
|
||||
# We need a second test client instance to log in as the app developer user
|
||||
from django.test import Client
|
||||
return Client()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application(app_developer):
|
||||
secret = get_random_string(32)
|
||||
a = OAuthApplication.objects.create(
|
||||
name="pretalx",
|
||||
redirect_uris="https://pretalx.com",
|
||||
client_type='confidential',
|
||||
client_secret=secret,
|
||||
authorization_grant_type='authorization-code'
|
||||
authorization_grant_type='authorization-code',
|
||||
user=app_developer,
|
||||
)
|
||||
a._cached_secret = secret
|
||||
a.save()
|
||||
@@ -703,6 +716,47 @@ def test_token_revoke_access_token(client, admin_user, organizer, application: O
|
||||
assert list(grant.organizers.all()) == [organizer]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_token_app_disabled(client, client2, admin_user, organizer, application: OAuthApplication, app_developer):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
session = client.session
|
||||
session['pretix_auth_login_time'] = int(time.time())
|
||||
session.save()
|
||||
resp = client.get('/api/v1/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code' % (
|
||||
application.client_id, quote(application.redirect_uris)
|
||||
))
|
||||
assert resp.status_code == 200
|
||||
resp = client.post('/api/v1/oauth/authorize', data={
|
||||
'organizers': str(organizer.pk),
|
||||
'redirect_uri': application.redirect_uris,
|
||||
'scope': 'read write',
|
||||
'client_id': application.client_id,
|
||||
'response_type': 'code',
|
||||
'allow': 'Authorize',
|
||||
})
|
||||
assert resp.status_code == 302
|
||||
assert resp['Location'].startswith('https://pretalx.com?code=')
|
||||
code = resp['Location'].split("=")[1]
|
||||
client.logout()
|
||||
resp = client.post('/api/v1/oauth/token', data={
|
||||
'code': code,
|
||||
'redirect_uri': application.redirect_uris,
|
||||
'grant_type': 'authorization_code',
|
||||
}, HTTP_AUTHORIZATION='Basic ' + base64.b64encode(
|
||||
('%s:%s' % (application.client_id, application._cached_secret)).encode()).decode())
|
||||
assert resp.status_code == 200
|
||||
data = json.loads(resp.content.decode())
|
||||
access_token = data['access_token']
|
||||
resp = client.get('/api/v1/organizers/dummy/events/', HTTP_AUTHORIZATION='Bearer %s' % access_token)
|
||||
assert resp.status_code == 200
|
||||
|
||||
client2.login(email='app-developer@example.org', password='app-developer')
|
||||
client2.post(f'/control/settings/oauth/apps/{application.pk}/disable', {})
|
||||
|
||||
resp = client.get('/api/v1/organizers/dummy/events/', HTTP_AUTHORIZATION='Bearer %s' % access_token)
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_user_revoke(client, admin_user, organizer, application: OAuthApplication):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
|
||||
Reference in New Issue
Block a user