Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement user_permissions model, api, and test #342

Merged
merged 33 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a4f0c37
Implement user_permissions model, api, and test
ethanstrominger Jul 1, 2024
9839dcf
Refactoring
ethanstrominger Jul 1, 2024
85c0bc0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 1, 2024
ed5a6d9
Fix serializers for user permissions
ethanstrominger Jul 2, 2024
dbdcb4c
Run black
ethanstrominger Jul 2, 2024
75ddc86
Alter permission type
ethanstrominger Jul 5, 2024
0c38e0f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 5, 2024
137e718
Refactor userpermissions migrate scripts
ethanstrominger Jul 12, 2024
1241293
Resolve conflict
ethanstrominger Jul 12, 2024
417ec84
Fix run.sh so it returns instead of exits
ethanstrominger Jul 12, 2024
017e89c
Fix based on comments from Fang
ethanstrominger Aug 30, 2024
c2452aa
Merge branch 'main' of https://github.com/hackforla/peopledepot into …
ethanstrominger Aug 30, 2024
dad2deb
Fix script and permission type seed issues
ethanstrominger Aug 31, 2024
74f3cdd
Fix test to correct lit url
ethanstrominger Aug 31, 2024
93da46e
Fix test for permission type to avoid global
ethanstrominger Aug 31, 2024
f682d9f
Changes based on self review
ethanstrominger Aug 31, 2024
abfa53e
Restore accidentally removed code
ethanstrominger Aug 31, 2024
54171dd
Rename user_admin to user_superuser_admin
ethanstrominger Sep 1, 2024
1b5411b
Revert changes to shell scripts
ethanstrominger Sep 11, 2024
2556fd3
Merge branch 'main' of https://github.com/hackforla/peopledepot into …
ethanstrominger Sep 11, 2024
17565f1
Update Dockerfile to remove 2.42-2-5
ethanstrominger Sep 11, 2024
2b0d025
Revert changes to test.sh
ethanstrominger Sep 11, 2024
cd49450
Fix dockerfile to ignore hadolint error
ethanstrominger Sep 11, 2024
e0273e4
Update Dockerfile for hadolint error
ethanstrominger Sep 11, 2024
4390bcb
Add comment to ignore hadolint DL30008 hadolint check
ethanstrominger Sep 11, 2024
b9c9886
Merge remote-tracking branch 'hackforla/main' into user-permissions-22
fyliu Sep 12, 2024
1419b64
Make changes based on Fang's review
ethanstrominger Sep 17, 2024
e3f2ffb
Merge branch 'user-permissions-22' of https://github.com/hackforla/pe…
ethanstrominger Sep 17, 2024
08edcfa
Restore files
ethanstrominger Sep 17, 2024
f74345e
fix wrong migration order from last merge
fyliu Sep 19, 2024
413bd75
remove debug print from migration script
fyliu Sep 19, 2024
8cc5b38
add core migration dependency for updated permissiontype data migration
fyliu Sep 19, 2024
efc9ff4
remove API description for non-read endpoints
fyliu Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
global_admin = "Global Admin"
ExperimentsInHonesty marked this conversation as resolved.
Show resolved Hide resolved
admin_project = "Project Lead"
practice_lead_project = "Practice Area Admin"
member_project = "Project Member"
self_value = "Self"
17 changes: 17 additions & 0 deletions app/core/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from core.models import StackElementType
from core.models import Technology
from core.models import User
from core.models import UserPermission


class PracticeAreaSerializer(serializers.ModelSerializer):
Expand All @@ -38,6 +39,22 @@ class Meta:
)


class UserPermissionSerializer(serializers.ModelSerializer):
"""Used to retrieve user permissions"""

class Meta:
model = UserPermission
fields = (
"uuid",
"created_at",
"updated_at",
"user",
"permission_type",
"project",
"practice_area",
)
ethanstrominger marked this conversation as resolved.
Show resolved Hide resolved


class UserSerializer(serializers.ModelSerializer):
"""Used to retrieve user info"""

Expand Down
2 changes: 2 additions & 0 deletions app/core/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
from .views import SkillViewSet
from .views import StackElementTypeViewSet
from .views import TechnologyViewSet
from .views import UserPermissionViewSet
from .views import UserProfileAPIView
from .views import UserViewSet

router = routers.SimpleRouter()
router.register(r"user-permissions", UserPermissionViewSet, basename="user-permission")
router.register(r"users", UserViewSet, basename="user")
router.register(r"projects", ProjectViewSet, basename="project")
router.register(r"events", EventViewSet, basename="event")
Expand Down
15 changes: 15 additions & 0 deletions app/core/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from ..models import Skill
from ..models import StackElementType
from ..models import Technology
from ..models import UserPermission
from .serializers import AffiliateSerializer
from .serializers import AffiliationSerializer
from .serializers import CheckTypeSerializer
Expand All @@ -41,6 +42,7 @@
from .serializers import SkillSerializer
from .serializers import StackElementTypeSerializer
from .serializers import TechnologySerializer
from .serializers import UserPermissionSerializer
from .serializers import UserSerializer


Expand Down Expand Up @@ -346,3 +348,16 @@ class CheckTypeViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
queryset = CheckType.objects.all()
serializer_class = CheckTypeSerializer


@extend_schema_view(
list=extend_schema(description="Return a list of all the user permissions"),
create=extend_schema(description="Create a new user permission"),
retrieve=extend_schema(description="Return the details of a user permission"),
destroy=extend_schema(description="Delete a user permission"),
partial_update=extend_schema(description="Patch a user permission"),
)
class UserPermissionViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = []
queryset = UserPermission.objects.all()
serializer_class = UserPermissionSerializer
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Generated by Django 4.2.11 on 2024-08-30 05:51

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
("core", "0024_checktype"),
]

operations = [
migrations.AddField(
model_name="permissiontype",
name="rank",
field=models.IntegerField(default=0, unique=True),
),
migrations.AlterField(
model_name="permissiontype",
name="name",
field=models.CharField(max_length=255, unique=True),
),
migrations.CreateModel(
name="UserPermission",
fields=[
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
unique=True,
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created at"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Updated at"),
),
(
"permission_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="core.permissiontype",
),
),
(
"practice_area",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="core.practicearea",
),
),
(
"project",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="core.project"
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="permissions",
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.AddConstraint(
model_name="userpermission",
constraint=models.UniqueConstraint(
fields=("user", "permission_type", "project", "practice_area"),
name="unique_user_permission",
),
),
]
2 changes: 1 addition & 1 deletion app/core/migrations/max_migration.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0024_checktype
0025_permissiontype_rank_alter_permissiontype_name_and_more
27 changes: 26 additions & 1 deletion app/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,9 @@ class PermissionType(AbstractBaseModel):
Permission Type
"""

name = models.CharField(max_length=255)
name = models.CharField(max_length=255, unique=True)
description = models.TextField(blank=True)
rank = models.IntegerField(unique=True, default=0)

def __str__(self):
if self.description and isinstance(self.description, str):
Expand All @@ -311,6 +312,30 @@ def __str__(self):
return f"{self.name}"


class UserPermission(AbstractBaseModel):
"""
User Permissions
"""

user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="permissions")
permission_type = models.ForeignKey(PermissionType, on_delete=models.CASCADE)
practice_area = models.ForeignKey(
PracticeArea, on_delete=models.CASCADE, blank=True, null=True
)
project = models.ForeignKey(Project, on_delete=models.CASCADE)

class Meta:
constraints = [
models.UniqueConstraint(
fields=["user", "permission_type", "project", "practice_area"],
name="unique_user_permission",
)
]

def __str__(self):
ethanstrominger marked this conversation as resolved.
Show resolved Hide resolved
return f"{self.user} has permission {self.permission_type}"


class StackElementType(AbstractBaseModel):
"""
Stack element type used to update a shared data store across projects
Expand Down
34 changes: 34 additions & 0 deletions app/core/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@
from ..models import Skill
from ..models import StackElementType
from ..models import Technology
from ..models import User
from ..models import UserPermission


@pytest.fixture
def user_admin():
return User.objects.create_user(
username="AdminUser",
email="[email protected]",
password="adminuser",
is_superuser=True,
)


@pytest.fixture
def user_permissions():
user1 = User.objects.create(username="TestUser1", email="[email protected]")
user2 = User.objects.create(username="TestUser2", email="[email protected]")
project = Project.objects.create(name="Test Project")
permission_type = PermissionType.objects.first()
practice_area = PracticeArea.objects.first()
user1_permission = UserPermission.objects.create(
user=user1,
permission_type=permission_type,
project=project,
practice_area=practice_area,
)
user2_permissions = UserPermission.objects.create(
user=user2,
permission_type=permission_type,
project=project,
practice_area=practice_area,
)
return [user1_permission, user2_permissions]


@pytest.fixture
Expand Down
9 changes: 9 additions & 0 deletions app/core/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

pytestmark = pytest.mark.django_db

USER_PERMISSIONS_URL = reverse("user-permissions-list")
ME_URL = reverse("my_profile")
USERS_URL = reverse("user-list")
EVENTS_URL = reverse("event-list")
Expand Down Expand Up @@ -322,6 +323,14 @@ def test_create_stack_element_type(auth_client):
assert res.data["name"] == payload["name"]


def test_get_user_permissions(user_admin, user_permissions, auth_client):
auth_client.force_authenticate(user=user_admin)
permissions = user_permissions
res = auth_client.get(USER_PERMISSIONS_URL)
assert len(res.data) == len(permissions)
ethanstrominger marked this conversation as resolved.
Show resolved Hide resolved
assert res.status_code == status.HTTP_200_OK


def test_create_sdg(auth_client):
payload = {
"name": "Test SDG name",
Expand Down
16 changes: 13 additions & 3 deletions app/data/migrations/0004_permissiontype_seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,55 @@

def insert_data(__code__, __reverse_code__):
items = [
(1, "adminGlobal", "Granted to People Depo Admins. Can CRUD anything."),
(1, "adminGlobal", "Granted to People Depo Admins. Can CRUD anything.", 1),
(
2,
"adminVrms",
"Granted to VRMS the application. Can R anything and update permissions for users to the level of Admin Brigade",
2,
),
(
3,
"adminBrigade",
"Granted to Brigage Leads. Can CRUD anything related to a user or project assigned to their brigade ",
3,
),
(
4,
"adminProject",
"Granted to Product Managers. Can Read and Update anything related to their assigned project",
4,
),
(
5,
"practiceLeadProject",
"Granted when a user is promoted or assigned to be a Project Lead. Can R and U anything related to people in their practice area (must attend lead events)",
5,
),
(
6,
"practiceLeadJrProject",
"Granted when a user is promoted or assigned to be a Jr Project Lead. Can R and U anything related to people in their practice area (should attend lead events)",
6,
),
(
7,
"memberProject",
"Granted when a user joins a project. Can Read anything related to their project that is visible",
7,
),
(
8,
"memberGeneral",
"Granted when a user finishes onboaring. Can express interest in a project's open role if they are the role",
8,
),
]
for uuid, name, description in items:
PermissionType.objects.create(uuid=uuid, name=name, description=description)
for uuid, name, description, rank in items:
print("Creating", name)
PermissionType.objects.create(
uuid=uuid, name=name, description=description, rank=rank
)


def clear_table(__code__, __reverse_code__):
Expand Down
2 changes: 1 addition & 1 deletion scripts/loadenv.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
echo SQL USER "$SQL_USER"
export file=$1
echo "file = $file / $1 / $2"
echo "file = $file / $1 "
if [ "$file" == "" ]
then
echo "File not specified. Using .env.local"
Expand Down
4 changes: 2 additions & 2 deletions scripts/run.sh
ethanstrominger marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ usage() {

exit_abnormal() {
usage
exit 1
return 1
}

migrate() {
Expand Down Expand Up @@ -71,7 +71,7 @@ while getopts "lhdvobcms" option; do
case $option in
h)
usage
exit;;
return 1;;
c) # delete containers and networks
DELETE_CONTAINERS=1;;
v) # remove volumes
Expand Down
Loading