feat(api): rename user model fields (#1902)

This commit is contained in:
Jonas L 2022-06-21 23:43:03 +02:00 committed by GitHub
parent 28c4989d44
commit dc426f0aa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 204 additions and 142 deletions

View File

@ -1,7 +1,7 @@
from .auth import LoginAttempt, Session, UserToken from .auth import LoginAttempt, Session, UserToken
from .country import Country from .country import Country
from .preference import Preference, StreamSetting from .preference import Preference, StreamSetting
from .role import ADMIN, DJ, GUEST, PROGRAM_MANAGER, USER_TYPES from .role import Role
from .service import ServiceRegister from .service import ServiceRegister
from .user import User, UserManager from .user import User, UserManager
from .worker import CeleryTask, ThirdPartyTrackReference from .worker import CeleryTask, ThirdPartyTrackReference

View File

@ -1,11 +1,8 @@
GUEST = "G" from django.db import models
DJ = "H"
PROGRAM_MANAGER = "P"
ADMIN = "A"
USER_TYPES = {
GUEST: "Guest", class Role(models.TextChoices):
DJ: "DJ", GUEST = "G", "Guest"
PROGRAM_MANAGER: "Program Manager", EDITOR = "H", "Editor"
ADMIN: "Admin", MANAGER = "P", "Manager"
} ADMIN = "A", "Admin"

View File

@ -4,18 +4,14 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, Permis
from django.db import models from django.db import models
from ...permission_constants import GROUPS from ...permission_constants import GROUPS
from .role import ADMIN, USER_TYPES from .role import Role
USER_TYPE_CHOICES = ()
for item in USER_TYPES.items():
USER_TYPE_CHOICES = USER_TYPE_CHOICES + (item,)
class UserManager(BaseUserManager): class UserManager(BaseUserManager):
def create_user(self, username, type, email, first_name, last_name, password): def create_user(self, role, username, password, email, first_name, last_name):
user = self.model( user = self.model(
role=role,
username=username, username=username,
type=type,
email=email, email=email,
first_name=first_name, first_name=first_name,
last_name=last_name, last_name=last_name,
@ -24,33 +20,75 @@ class UserManager(BaseUserManager):
user.save(using=self._db) user.save(using=self._db)
return user return user
def create_superuser(self, username, email, first_name, last_name, password): def create_superuser(self, username, password, email, first_name, last_name):
user = self.create_user(username, "A", email, first_name, last_name, password) return self.create_user(
return user Role.ADMIN,
username,
password,
email,
first_name,
last_name,
)
def get_by_natural_key(self, username): def get_by_natural_key(self, username):
return self.get(username=username) return self.get(username=username)
class User(AbstractBaseUser): class User(AbstractBaseUser):
role = models.CharField(
db_column="type",
max_length=1,
choices=Role.choices,
)
username = models.CharField(db_column="login", unique=True, max_length=255) username = models.CharField(db_column="login", unique=True, max_length=255)
password = models.CharField( password = models.CharField(db_column="pass", max_length=255)
db_column="pass", max_length=255 email = models.CharField(max_length=1024, blank=True, null=True)
) # Field renamed because it was a Python reserved word.
type = models.CharField(max_length=1, choices=USER_TYPE_CHOICES)
first_name = models.CharField(max_length=255) first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255)
last_login = models.DateTimeField(db_column="lastlogin", blank=True, null=True)
lastfail = models.DateTimeField(blank=True, null=True) login_attempts = models.IntegerField(
skype_contact = models.CharField(max_length=1024, blank=True, null=True) db_column="login_attempts",
jabber_contact = models.CharField(max_length=1024, blank=True, null=True) blank=True,
email = models.CharField(max_length=1024, blank=True, null=True) null=True,
cell_phone = models.CharField(max_length=1024, blank=True, null=True) )
login_attempts = models.IntegerField(blank=True, null=True) last_login = models.DateTimeField(
db_column="lastlogin",
blank=True,
null=True,
)
last_failed_login = models.DateTimeField(
db_column="lastfail",
blank=True,
null=True,
)
skype = models.CharField(
db_column="skype_contact",
max_length=1024,
blank=True,
null=True,
)
jabber = models.CharField(
db_column="jabber_contact",
max_length=1024,
blank=True,
null=True,
)
phone = models.CharField(
db_column="cell_phone",
max_length=1024,
blank=True,
null=True,
)
class Meta:
managed = False
db_table = "cc_subjs"
USERNAME_FIELD = "username" USERNAME_FIELD = "username"
EMAIL_FIELD = "email" EMAIL_FIELD = "email"
REQUIRED_FIELDS = ["type", "email", "first_name", "last_name"] REQUIRED_FIELDS = ["role", "email", "first_name", "last_name"]
objects = UserManager() objects = UserManager()
def get_full_name(self): def get_full_name(self):
@ -66,7 +104,7 @@ class User(AbstractBaseUser):
self.password = hashlib.md5(raw_password.encode()).hexdigest() self.password = hashlib.md5(raw_password.encode()).hexdigest()
def is_staff(self): def is_staff(self):
return self.type == ADMIN return self.role == Role.ADMIN
def check_password(self, raw_password): def check_password(self, raw_password):
if self.has_usable_password(): if self.has_usable_password():
@ -81,7 +119,7 @@ class User(AbstractBaseUser):
# django.contrib.auth.models.PermissionMixin. # django.contrib.auth.models.PermissionMixin.
def is_superuser(self): def is_superuser(self):
return self.type == ADMIN return self.role == Role.ADMIN
def get_user_permissions(self, obj=None): def get_user_permissions(self, obj=None):
""" """
@ -90,7 +128,7 @@ class User(AbstractBaseUser):
return [] return []
def get_group_permissions(self, obj=None): def get_group_permissions(self, obj=None):
permissions = GROUPS[self.type] permissions = GROUPS[self.role]
if obj: if obj:
obj_name = obj.__class__.__name__.lower() obj_name = obj.__class__.__name__.lower()
permissions = [perm for perm in permissions if obj_name in perm] permissions = [perm for perm in permissions if obj_name in perm]
@ -120,7 +158,3 @@ class User(AbstractBaseUser):
for permission in perm_list: for permission in perm_list:
result = result and self.has_perm(permission, obj) result = result and self.has_perm(permission, obj)
return result return result
class Meta:
managed = False
db_table = "cc_subjs"

View File

@ -7,14 +7,15 @@ class UserSerializer(serializers.HyperlinkedModelSerializer):
model = get_user_model() model = get_user_model()
fields = [ fields = [
"item_url", "item_url",
"role",
"username", "username",
"type", "email",
"first_name", "first_name",
"last_name", "last_name",
"lastfail",
"skype_contact",
"jabber_contact",
"email",
"cell_phone",
"login_attempts", "login_attempts",
"last_login",
"last_failed_login",
"skype",
"jabber",
"phone",
] ]

View File

@ -1,16 +1,16 @@
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from ....permission_constants import GROUPS from ....permission_constants import GROUPS
from ...models import DJ, GUEST, User from ...models import Role, User
class TestUserManager(APITestCase): class TestUserManager(APITestCase):
def test_create_user(self): def test_create_user(self):
user = User.objects.create_user( user = User.objects.create_user(
"test", role=Role.EDITOR,
email="test@example.com", username="test",
password="test", password="test",
type=DJ, email="test@example.com",
first_name="test", first_name="test",
last_name="user", last_name="user",
) )
@ -19,27 +19,29 @@ class TestUserManager(APITestCase):
def test_create_superuser(self): def test_create_superuser(self):
user = User.objects.create_superuser( user = User.objects.create_superuser(
"test", username="test",
email="test@example.com",
password="test", password="test",
email="test@example.com",
first_name="test", first_name="test",
last_name="user", last_name="user",
) )
db_user = User.objects.get(pk=user.pk) db_user = User.objects.get(pk=user.pk)
self.assertEqual(db_user.username, user.username) self.assertEqual(db_user.username, user.username)
self.assertEqual(db_user.role, Role.ADMIN)
class TestUser(APITestCase): class TestUser(APITestCase):
def test_guest_get_group_perms(self): def test_guest_get_group_perms(self):
user = User.objects.create_user( user = User.objects.create_user(
"test", role=Role.GUEST,
email="test@example.com", username="test",
password="test", password="test",
type=GUEST, email="test@example.com",
first_name="test", first_name="test",
last_name="user", last_name="user",
) )
permissions = user.get_group_permissions() permissions = user.get_group_permissions()
# APIRoot permission hardcoded in the check as it isn't a Permission object # APIRoot permission hardcoded in the check as it isn't a Permission object
str_perms = [p.codename for p in permissions] + ["view_apiroot"] str_perms = [p.codename for p in permissions] + ["view_apiroot"]
self.assertCountEqual(str_perms, GROUPS[GUEST]) self.assertCountEqual(str_perms, GROUPS[Role.GUEST.value])

View File

@ -1,4 +1,4 @@
from .core.models import DJ, GUEST, PROGRAM_MANAGER from .core.models import Role
GUEST_PERMISSIONS = [ GUEST_PERMISSIONS = [
"view_schedule", "view_schedule",
@ -19,7 +19,7 @@ GUEST_PERMISSIONS = [
"view_apiroot", "view_apiroot",
] ]
DJ_PERMISSIONS = GUEST_PERMISSIONS + [ EDITOR_PERMISSIONS = GUEST_PERMISSIONS + [
"add_file", "add_file",
"add_podcast", "add_podcast",
"add_podcastepisode", "add_podcastepisode",
@ -50,7 +50,8 @@ DJ_PERMISSIONS = GUEST_PERMISSIONS + [
"delete_own_smartblockcriteria", "delete_own_smartblockcriteria",
"delete_own_webstream", "delete_own_webstream",
] ]
PROGRAM_MANAGER_PERMISSIONS = GUEST_PERMISSIONS + [
MANAGER_PERMISSIONS = GUEST_PERMISSIONS + [
"add_show", "add_show",
"add_showdays", "add_showdays",
"add_showhost", "add_showhost",
@ -98,7 +99,7 @@ PROGRAM_MANAGER_PERMISSIONS = GUEST_PERMISSIONS + [
] ]
GROUPS = { GROUPS = {
GUEST: GUEST_PERMISSIONS, Role.GUEST.value: GUEST_PERMISSIONS,
DJ: DJ_PERMISSIONS, Role.EDITOR.value: EDITOR_PERMISSIONS,
PROGRAM_MANAGER: PROGRAM_MANAGER_PERMISSIONS, Role.MANAGER.value: MANAGER_PERMISSIONS,
} }

View File

@ -3,7 +3,7 @@ from secrets import compare_digest
from django.conf import settings from django.conf import settings
from rest_framework.permissions import BasePermission from rest_framework.permissions import BasePermission
from .core.models.role import DJ from .core.models import Role
REQUEST_PERMISSION_TYPE_MAP = { REQUEST_PERMISSION_TYPE_MAP = {
"GET": "view", "GET": "view",
@ -18,7 +18,7 @@ REQUEST_PERMISSION_TYPE_MAP = {
def get_own_obj(request, view): def get_own_obj(request, view):
user = request.user user = request.user
if user is None or user.type != DJ: if user is None or user.role != Role.EDITOR:
return "" return ""
if request.method == "GET": if request.method == "GET":
return "" return ""
@ -67,12 +67,12 @@ class IsAdminOrOwnUser(BasePermission):
""" """
def has_permission(self, request, view): def has_permission(self, request, view):
if request.user.is_superuser: if request.user.is_superuser():
return True return True
return False return False
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
if request.user.is_superuser: if request.user.is_superuser():
return True return True
return obj.username == request.user return obj.username == request.user

View File

@ -4,7 +4,7 @@ from django.contrib.auth.models import AnonymousUser
from model_bakery import baker from model_bakery import baker
from rest_framework.test import APIRequestFactory, APITestCase from rest_framework.test import APIRequestFactory, APITestCase
from ..core.models import DJ, GUEST from ..core.models import Role
from ..permissions import IsSystemTokenOrUser from ..permissions import IsSystemTokenOrUser
@ -51,18 +51,18 @@ class TestPermissions(APITestCase):
"webstreams", "webstreams",
] ]
def logged_in_test_model(self, model, name, user_type, fn): def logged_in_test_model(self, model, role, username, fn):
path = self.path.format(model) path = self.path.format(model)
if not get_user_model().objects.filter(username=name): if not get_user_model().objects.filter(username=username):
get_user_model().objects.create_user( get_user_model().objects.create_user(
name, role=role,
email="test@example.com", username=username,
password="test", password="test",
type=user_type, email="test@example.com",
first_name="test", first_name="test",
last_name="user", last_name="user",
) )
self.client.login(username=name, password="test") self.client.login(username=username, password="test")
return fn(path) return fn(path)
@classmethod @classmethod
@ -71,55 +71,74 @@ class TestPermissions(APITestCase):
def test_guest_permissions_success(self): def test_guest_permissions_success(self):
for model in self.URLS: for model in self.URLS:
response = self.logged_in_test_model(model, "guest", GUEST, self.client.get) response = self.logged_in_test_model(
model,
Role.GUEST,
"guest",
self.client.get,
)
self.assertEqual( self.assertEqual(
response.status_code, 200, msg=f"Invalid for model {model}" response.status_code,
200,
msg=f"Invalid for model {model}",
) )
def test_guest_permissions_failure(self): def test_guest_permissions_failure(self):
for model in self.URLS: for model in self.URLS:
response = self.logged_in_test_model( response = self.logged_in_test_model(
model, "guest", GUEST, self.client.post model,
Role.GUEST,
"guest",
self.client.post,
) )
self.assertEqual( self.assertEqual(
response.status_code, 403, msg=f"Invalid for model {model}" response.status_code,
403,
msg=f"Invalid for model {model}",
) )
def test_dj_get_permissions(self): def test_editor_get_permissions(self):
for model in self.URLS: for model in self.URLS:
response = self.logged_in_test_model(model, "dj", DJ, self.client.get) response = self.logged_in_test_model(
model,
Role.EDITOR,
"editor",
self.client.get,
)
self.assertEqual( self.assertEqual(
response.status_code, 200, msg=f"Invalid for model {model}" response.status_code,
200,
msg=f"Invalid for model {model}",
) )
def test_dj_post_permissions(self): def test_editor_post_permissions(self):
user = get_user_model().objects.create_user( user = get_user_model().objects.create_user(
"test-dj", role=Role.EDITOR,
email="test@example.com", username="editor2",
password="test", password="test",
type=DJ, email="test@example.com",
first_name="test", first_name="test",
last_name="user", last_name="user",
) )
file = baker.make("storage.File", owner=user) file = baker.make("storage.File", owner=user)
model = f"files/{file.id}" model = f"files/{file.id}"
path = self.path.format(model) path = self.path.format(model)
self.client.login(username="test-dj", password="test") self.client.login(username="editor2", password="test")
response = self.client.patch(path, {"name": "newFilename"}) response = self.client.patch(path, {"name": "newFilename"})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_dj_post_permissions_failure(self): def test_editor_post_permissions_failure(self):
get_user_model().objects.create_user( get_user_model().objects.create_user(
"test-dj", role=Role.EDITOR,
email="test@example.com", username="editor2",
password="test", password="test",
type=DJ, email="test@example.com",
first_name="test", first_name="test",
last_name="user", last_name="user",
) )
file = baker.make("storage.File") file = baker.make("storage.File")
model = f"files/{file.id}" model = f"files/{file.id}"
path = self.path.format(model) path = self.path.format(model)
self.client.login(username="test-dj", password="test") self.client.login(username="editor2", password="test")
response = self.client.patch(path, {"name": "newFilename"}) response = self.client.patch(path, {"name": "newFilename"})
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)

View File

@ -7468,42 +7468,46 @@ components:
type: string type: string
format: uri format: uri
readOnly: true readOnly: true
role:
$ref: "#/components/schemas/RoleEnum"
username: username:
type: string type: string
maxLength: 255 maxLength: 255
type: email:
$ref: "#/components/schemas/TypeEnum" type: string
nullable: true
maxLength: 1024
first_name: first_name:
type: string type: string
maxLength: 255 maxLength: 255
last_name: last_name:
type: string type: string
maxLength: 255 maxLength: 255
lastfail:
type: string
format: date-time
nullable: true
skype_contact:
type: string
nullable: true
maxLength: 1024
jabber_contact:
type: string
nullable: true
maxLength: 1024
email:
type: string
nullable: true
maxLength: 1024
cell_phone:
type: string
nullable: true
maxLength: 1024
login_attempts: login_attempts:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
nullable: true nullable: true
last_login:
type: string
format: date-time
nullable: true
last_failed_login:
type: string
format: date-time
nullable: true
skype:
type: string
nullable: true
maxLength: 1024
jabber:
type: string
nullable: true
maxLength: 1024
phone:
type: string
nullable: true
maxLength: 1024
PatchedUserToken: PatchedUserToken:
type: object type: object
properties: properties:
@ -7886,6 +7890,13 @@ components:
- item_url - item_url
- key - key
- user - user
RoleEnum:
enum:
- G
- H
- P
- A
type: string
Schedule: Schedule:
type: object type: object
properties: properties:
@ -8429,13 +8440,6 @@ components:
required: required:
- code - code
- item_url - item_url
TypeEnum:
enum:
- G
- H
- P
- A
type: string
User: User:
type: object type: object
properties: properties:
@ -8443,47 +8447,51 @@ components:
type: string type: string
format: uri format: uri
readOnly: true readOnly: true
role:
$ref: "#/components/schemas/RoleEnum"
username: username:
type: string type: string
maxLength: 255 maxLength: 255
type: email:
$ref: "#/components/schemas/TypeEnum" type: string
nullable: true
maxLength: 1024
first_name: first_name:
type: string type: string
maxLength: 255 maxLength: 255
last_name: last_name:
type: string type: string
maxLength: 255 maxLength: 255
lastfail:
type: string
format: date-time
nullable: true
skype_contact:
type: string
nullable: true
maxLength: 1024
jabber_contact:
type: string
nullable: true
maxLength: 1024
email:
type: string
nullable: true
maxLength: 1024
cell_phone:
type: string
nullable: true
maxLength: 1024
login_attempts: login_attempts:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
nullable: true nullable: true
last_login:
type: string
format: date-time
nullable: true
last_failed_login:
type: string
format: date-time
nullable: true
skype:
type: string
nullable: true
maxLength: 1024
jabber:
type: string
nullable: true
maxLength: 1024
phone:
type: string
nullable: true
maxLength: 1024
required: required:
- first_name - first_name
- item_url - item_url
- last_name - last_name
- type - role
- username - username
UserToken: UserToken:
type: object type: object