🔒️(users) restrict listable users to same organization
This is a quick fix to a security issue. Previously, any user could list all users. Now /users/ endpoint only lists users from same organization.
This commit is contained in:
committed by
Marie
parent
a009f3ccb7
commit
b4de7fda92
@@ -22,6 +22,7 @@ and this project adheres to
|
||||
### Changed
|
||||
|
||||
- ♻️(plugins) rewrite plugin system as django app #844
|
||||
- 🔒️(users) restrict listable users to same organization #846
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@
|
||||
"firstName": "John",
|
||||
"lastName": "Doe",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -81,6 +84,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Chromium",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -95,6 +101,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Webkit",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -109,6 +118,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Firefox",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -123,6 +135,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Member",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -137,6 +152,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Administrator",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -151,6 +169,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Owner",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -165,6 +186,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Mailbox Member",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -179,6 +203,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Mailbox Administrator",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -193,6 +220,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Mailbox Owner",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -207,6 +237,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Member Mailbox Member",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -221,6 +254,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Member Mailbox Administrator",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -235,6 +271,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Member Mailbox Owner",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -249,6 +288,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Administrator Mailbox Member",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -263,6 +305,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Administrator Mailbox Administrator",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -277,6 +322,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Administrator Mailbox Owner",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -291,6 +339,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Owner Mailbox Member",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -305,6 +356,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Group Owner Mailbox Administrator",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
@@ -319,6 +373,9 @@
|
||||
"firstName": "E2E",
|
||||
"lastName": "Mailbox Owner",
|
||||
"enabled": true,
|
||||
"attributes": {
|
||||
"siret": "13002526500013"
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
|
||||
@@ -263,9 +263,10 @@ class UserViewSet(
|
||||
queryset = self.queryset
|
||||
|
||||
if self.action == "list":
|
||||
# Exclude inactive contacts
|
||||
# Filter active users
|
||||
# and users from same organization
|
||||
queryset = queryset.filter(
|
||||
is_active=True,
|
||||
is_active=True, organization_id=self.request.user.organization_id
|
||||
)
|
||||
|
||||
# Exclude all users already in the given team
|
||||
|
||||
@@ -31,26 +31,29 @@ def test_api_users_list_anonymous():
|
||||
|
||||
def test_api_users_list_authenticated():
|
||||
"""
|
||||
Authenticated users should be able to list all users.
|
||||
Authenticated users should be able to list users from their organization.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
organization = factories.OrganizationFactory(with_registration_id=True)
|
||||
user = factories.UserFactory(organization=organization)
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
factories.UserFactory.create_batch(2)
|
||||
factories.UserFactory(organization=organization)
|
||||
factories.UserFactory.create_batch(2) # 2 users outside organization
|
||||
response = client.get(
|
||||
"/api/v1.0/users/",
|
||||
)
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert len(response.json()["results"]) == 3
|
||||
assert len(response.json()["results"]) == 2
|
||||
|
||||
|
||||
def test_api_users_list_authenticated_response_content(
|
||||
client, django_assert_num_queries
|
||||
):
|
||||
"""
|
||||
Authenticated users should be able to list all users with the expected output.
|
||||
Authenticated users should be able to list all users from their organization
|
||||
with the expected output.
|
||||
"""
|
||||
user_organization = factories.OrganizationFactory(
|
||||
with_registration_id=True, name="HAL 9000"
|
||||
@@ -67,7 +70,7 @@ def test_api_users_list_authenticated_response_content(
|
||||
other_user_organization = factories.OrganizationFactory(
|
||||
with_registration_id=True, name="Corp. Inc."
|
||||
)
|
||||
other_user = factories.UserFactory(
|
||||
factories.UserFactory(
|
||||
organization=other_user_organization,
|
||||
email="sara83@example.com",
|
||||
name="Christopher Thompson",
|
||||
@@ -85,23 +88,10 @@ def test_api_users_list_authenticated_response_content(
|
||||
.first()
|
||||
)
|
||||
assert edited_json == {
|
||||
"count": 2,
|
||||
"count": 1,
|
||||
"next": None,
|
||||
"previous": None,
|
||||
"results": [
|
||||
{
|
||||
"email": "sara83@example.com",
|
||||
"id": str(other_user.pk),
|
||||
"is_device": False,
|
||||
"is_staff": False,
|
||||
"language": "fr-fr",
|
||||
"name": "Christopher Thompson",
|
||||
"organization": {
|
||||
"id": str(other_user.organization.pk),
|
||||
"name": "Corp. Inc.",
|
||||
},
|
||||
"timezone": "UTC",
|
||||
},
|
||||
{
|
||||
"email": "kylefields@example.net",
|
||||
"id": str(user.pk),
|
||||
@@ -124,13 +114,17 @@ def test_api_users_authenticated_list_by_email():
|
||||
Authenticated users should be able to search users with a case-insensitive and
|
||||
partial query on the email.
|
||||
"""
|
||||
user = factories.UserFactory(email="tester@ministry.fr", name="john doe")
|
||||
dave = factories.UserFactory(email="david.bowman@work.com", name=None)
|
||||
user = factories.UserFactory(
|
||||
email="tester@ministry.fr", name="john doe", with_organization=True
|
||||
)
|
||||
dave = factories.UserFactory(
|
||||
email="david.bowman@work.com", name=None, organization=user.organization
|
||||
)
|
||||
nicole = factories.UserFactory(
|
||||
email="nicole_foole@work.com", name=None, with_organization=True
|
||||
email="nicole_foole@work.com", name=None, organization=user.organization
|
||||
)
|
||||
frank = factories.UserFactory(
|
||||
email="frank_poole@work.com", name=None, with_organization=True
|
||||
email="frank_poole@work.com", name=None, organization=user.organization
|
||||
)
|
||||
factories.UserFactory(email="heywood_floyd@work.com", name=None)
|
||||
|
||||
@@ -203,13 +197,17 @@ def test_api_users_authenticated_list_by_name():
|
||||
Authenticated users should be able to search users with a case-insensitive and
|
||||
partial query on the name.
|
||||
"""
|
||||
user = factories.UserFactory(email="tester@ministry.fr", name="john doe")
|
||||
dave = factories.UserFactory(name="Dave bowman", email=None)
|
||||
user = factories.UserFactory(
|
||||
email="tester@ministry.fr", name="john doe", with_organization=True
|
||||
)
|
||||
dave = factories.UserFactory(
|
||||
name="Dave bowman", email=None, organization=user.organization
|
||||
)
|
||||
nicole = factories.UserFactory(
|
||||
name="nicole foole", email=None, with_organization=True
|
||||
name="nicole foole", email=None, organization=user.organization
|
||||
)
|
||||
frank = factories.UserFactory(
|
||||
name="frank poolé", email=None, with_organization=True
|
||||
name="frank poolé", email=None, organization=user.organization
|
||||
)
|
||||
factories.UserFactory(name="heywood floyd", email=None)
|
||||
|
||||
|
||||
@@ -201,6 +201,10 @@ def create_demo(stdout): # pylint: disable=too-many-locals
|
||||
)
|
||||
# this is a quick fix to fix e2e tests
|
||||
# tests needs some no random data
|
||||
organization, _created = models.Organization.objects.get_or_create(
|
||||
name="13002526500013",
|
||||
registration_id_list=["13002526500013"],
|
||||
)
|
||||
queue.push(
|
||||
models.User(
|
||||
sub=uuid4(),
|
||||
@@ -210,6 +214,7 @@ def create_demo(stdout): # pylint: disable=too-many-locals
|
||||
is_superuser=False,
|
||||
is_active=True,
|
||||
is_staff=False,
|
||||
organization=organization,
|
||||
language=random.choice(settings.LANGUAGES)[0],
|
||||
)
|
||||
)
|
||||
@@ -222,6 +227,7 @@ def create_demo(stdout): # pylint: disable=too-many-locals
|
||||
is_superuser=False,
|
||||
is_active=True,
|
||||
is_staff=False,
|
||||
organization=organization,
|
||||
language=random.choice(settings.LANGUAGES)[0],
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user