Allow multiple users per tenant

This commit is contained in:
Michael Herrmann
2023-08-03 13:01:22 +02:00
parent 5ccf836ed5
commit 7778792e85
10 changed files with 92 additions and 20 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ def can_pick_tenant(user):
def filter_for_user(model, user):
tenant_accessor = get_tenant_accessor(model)
query = {tenant_accessor + '__user': user}
query = {tenant_accessor + '__users': user}
return model.objects.filter(**query)
+1 -1
View File
@@ -4,6 +4,6 @@ from django.contrib.admin import ModelAdmin
from .models import Tenant
class TenantAdmin(ModelAdmin):
list_display = ('user', 'uuid')
list_display = ('__str__', 'uuid')
admin.site.register(Tenant, TenantAdmin)
+1 -1
View File
@@ -14,7 +14,7 @@ class TenantModelForm(ModelForm):
def full_clean(self):
super().full_clean()
if not can_pick_tenant(self.user) and self.instance:
self.instance.tenant = Tenant.objects.get(user=self.user)
self.instance.tenant = Tenant.objects.get(users=self.user)
self.validate_unique_for_tenant()
def validate_unique_for_tenant(self):
+20
View File
@@ -0,0 +1,20 @@
# Generated by Django 3.2.10 on 2023-08-03 10:45
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('tenants', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='tenant',
name='users',
field=models.ManyToManyField(related_name='users', to=settings.AUTH_USER_MODEL),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 3.2.10 on 2023-08-03 10:45
from django.db import migrations
def migrate_existing_users(apps, _):
Tenant = apps.get_model('tenants', 'Tenant')
for tenant in Tenant.objects.all():
tenant.users.add(tenant.user)
class Migration(migrations.Migration):
dependencies = [
('tenants', '0002_tenant_users'),
]
operations = [
migrations.RunPython(migrate_existing_users)
]
@@ -0,0 +1,24 @@
# Generated by Django 3.2.10 on 2023-08-03 10:49
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('tenants', '0003_migrate_existing_users'),
]
operations = [
migrations.RemoveField(
model_name='tenant',
name='user',
),
migrations.AlterField(
model_name='tenant',
name='users',
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL),
),
]
+10 -3
View File
@@ -1,13 +1,20 @@
from uuid import uuid4
from django.contrib.auth.models import User
from django.db.models import Model, ForeignKey, CASCADE, UUIDField
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Model, UUIDField, ManyToManyField
class Tenant(Model):
user = ForeignKey(User, on_delete=CASCADE)
users = ManyToManyField(User)
uuid = UUIDField(unique=True, default=uuid4, editable=False)
def __str__(self):
return self.user.username
# We have to check that `id` is set to avoid a `RecursionError`.
if self.id:
try:
return self.users.earliest('date_joined').username
except ObjectDoesNotExist:
pass
return str(self.uuid)
+4 -8
View File
@@ -1,11 +1,10 @@
from base64 import b64encode
from django.contrib.auth.models import User
from django.core.files import File
from django.urls import reverse
from rest_framework.test import APIClient
from tenants.models import Tenant
from winget.models import Package, Version, Installer
from winget.tests.util import TestCaseThatUploadsFiles, MultiUploadInMemoryFile
from winget.tests.util import TestCaseThatUploadsFiles, \
MultiUploadInMemoryFile, create_tenant
import json
@@ -15,9 +14,9 @@ class APITest(TestCaseThatUploadsFiles):
super().setUp()
self.client = APIClient()
self.credentials = ('user', 'pw')
self.tenant = self._create_user_tenant(self.credentials)
self.tenant = create_tenant(*self.credentials)
self.other_credentials = ('user2', 'pw2')
self._create_user_tenant(self.other_credentials)
create_tenant(*self.other_credentials)
def test_create_package(self):
response = self._check_unauthorized_and_request(
'post', 'package-list', _PACKAGE_PAYLOAD, self.credentials
@@ -128,9 +127,6 @@ class APITest(TestCaseThatUploadsFiles):
self.test_create_version(use_correct_credentials=False)
def test_cannot_create_installer_for_other_tenants_version(self):
self.test_create_installer(use_correct_credentials=False)
def _create_user_tenant(self, credentials):
user = User.objects.create_user(credentials[0], password=credentials[1])
return Tenant.objects.create(user=user)
def _check_unauthorized_and_request(
self, method, view_name, data, credentials=None, view_kwargs=None
):
+2 -5
View File
@@ -1,10 +1,9 @@
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, Client
from django.urls import reverse
from tenants.models import Tenant
from winget.models import Package, Version, Installer
from winget.tests.util import create_tenant
class APITest(TestCase):
@@ -12,9 +11,7 @@ class APITest(TestCase):
def setUp(self):
super().setUp()
self.client = Client()
self.tenant = Tenant.objects.create(
user=User.objects.create_user('user@gmail.com')
)
self.tenant = create_tenant()
def test_information_required_parts(self):
data = self._get('information')['Data']
+10 -1
View File
@@ -1,7 +1,10 @@
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from tempfile import TemporaryDirectory
from tenants.models import Tenant
"""
When a test case uploads files, they by default get placed into media/. This
folder (out of the box) is not cleaned up after the test case is run.
@@ -24,4 +27,10 @@ class MultiUploadInMemoryFile(SimpleUploadedFile):
"""
def read(self):
self.seek(0)
return super().read()
return super().read()
def create_tenant(username='user@gmail.com', password=None):
user = User.objects.create_user(username, password=password)
tenant = Tenant.objects.create()
tenant.users.add(user)
return tenant