mirror of
https://github.com/suitenumerique/meet.git
synced 2026-10-07 22:10:56 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| deb1ef9ed6 |
+1
-4
@@ -8,13 +8,10 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- ⚡️(backend) add UPPER(email) index for case-insensitive user lookups
|
||||
|
||||
### Changed
|
||||
|
||||
- ⚡️(backend) hash application secrets with SHA-256
|
||||
- ⚡️(backend) reduce domain queries on the application token endpoint
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 5.2.14 on 2026-10-07 18:45
|
||||
|
||||
from django.contrib.postgres.operations import AddIndexConcurrently
|
||||
from django.db import migrations, models
|
||||
from django.db.models.functions import Upper
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
('core', '0025_application_client_secret_sha256'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
AddIndexConcurrently(
|
||||
model_name="user",
|
||||
index=models.Index(
|
||||
Upper("email"),
|
||||
name="user_email_upper_idx",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -246,9 +246,6 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
||||
name="unique_email_when_sub_is_null",
|
||||
)
|
||||
]
|
||||
indexes = [
|
||||
models.Index(models.functions.Upper("email"), name="user_email_upper_idx"),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.email or self.admin_email or str(self.id)
|
||||
@@ -906,11 +903,12 @@ class Application(BaseModel):
|
||||
def can_delegate_email(self, email):
|
||||
"""Check if this application can delegate the given email."""
|
||||
|
||||
if not self.allowed_domains.exists():
|
||||
allowed_domains = {d.domain for d in self.allowed_domains.all()}
|
||||
if not allowed_domains:
|
||||
return True # No domain restrictions
|
||||
|
||||
domain = get_domain_from_email(email)
|
||||
return self.allowed_domains.filter(domain__iexact=domain).exists()
|
||||
return bool(domain) and domain.lower() in allowed_domains
|
||||
|
||||
|
||||
class ApplicationDomain(BaseModel):
|
||||
|
||||
@@ -87,6 +87,60 @@ def test_api_applications_generate_token_success(settings):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"restricted", [True, False], ids=["restricted", "unrestricted"]
|
||||
)
|
||||
def test_api_applications_generate_token_success_query_count(
|
||||
restricted, django_assert_num_queries
|
||||
):
|
||||
"""An existing user needs one query each for application, domains, and user."""
|
||||
user = UserFactory(email="user@example.com")
|
||||
plain_secret = "test-secret-123"
|
||||
application = ApplicationFactory(client_secret=plain_secret)
|
||||
if restricted:
|
||||
ApplicationDomainFactory(application=application, domain="example.com")
|
||||
|
||||
client = APIClient()
|
||||
with django_assert_num_queries(3):
|
||||
response = client.post(
|
||||
"/external-api/v1.0/application/token/",
|
||||
{
|
||||
"client_id": application.client_id,
|
||||
"client_secret": plain_secret,
|
||||
"grant_type": "client_credentials",
|
||||
"scope": user.email,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "access_token" in response.data
|
||||
|
||||
|
||||
def test_api_applications_generate_token_invalid_credentials_query_count(
|
||||
django_assert_num_queries,
|
||||
):
|
||||
"""An invalid secret must be rejected before querying domains or users."""
|
||||
application = ApplicationFactory(client_secret="test-secret-123")
|
||||
ApplicationDomainFactory(application=application, domain="example.com")
|
||||
|
||||
client = APIClient()
|
||||
with django_assert_num_queries(1):
|
||||
response = client.post(
|
||||
"/external-api/v1.0/application/token/",
|
||||
{
|
||||
"client_id": application.client_id,
|
||||
"client_secret": "wrong-secret",
|
||||
"grant_type": "client_credentials",
|
||||
"scope": "user@example.com",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert "Invalid credentials" in str(response.data)
|
||||
|
||||
|
||||
def test_api_applications_generate_token_form_urlencoded(settings):
|
||||
"""The token endpoint should accept "application/x-www-form-urlencoded"
|
||||
requests, as mandated by RFC 6749 (sections 3.2 and 4.4.2) for OAuth 2.0
|
||||
|
||||
Reference in New Issue
Block a user