Skip to content

Commit

Permalink
added owner to all models (#434)
Browse files Browse the repository at this point in the history
* added owner to all models
---------

Signed-off-by: Trey <[email protected]>
  • Loading branch information
TreyWW authored Jul 1, 2024
1 parent df942a7 commit c5db885
Show file tree
Hide file tree
Showing 31 changed files with 337 additions and 161 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run_mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ jobs:
- name: Run mypy checks
run: |
source .venv/bin/activate
mypy .
mypy . -i
4 changes: 2 additions & 2 deletions backend/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
TracebackError,
UserSettings,
Notification,
Team,
Organization,
TeamInvitation,
TeamMemberPermission,
User,
Expand Down Expand Up @@ -51,7 +51,7 @@
Error,
TracebackError,
Notification,
Team,
Organization,
TeamInvitation,
TeamMemberPermission,
InvoiceProduct,
Expand Down
4 changes: 2 additions & 2 deletions backend/api/base/modal.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from backend.models import Client, Receipt
from backend.models import Invoice
from backend.models import QuotaLimit
from backend.models import Team
from backend.models import Organization
from backend.models import UserSettings
from backend.types.htmx import HtmxHttpRequest
from backend.utils.feature_flags import get_feature_status
Expand All @@ -32,7 +32,7 @@ def open_modal(request: HtmxHttpRequest, modal_name, context_type=None, context_
context["code"] = context_value
elif context_type == "leave_team":
if request.user.teams_joined.filter(id=context_value).exists():
context["team"] = Team.objects.filter(id=context_value).first()
context["team"] = Organization.objects.filter(id=context_value).first()
elif context_type == "edit_receipt":
try:
receipt = Receipt.objects.get(pk=context_value)
Expand Down
6 changes: 3 additions & 3 deletions backend/api/public/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework.exceptions import AuthenticationFailed

from backend.api.public import APIAuthToken
from backend.models import User, Team
from backend.models import User, Organization


class CustomBearerAuthentication(TokenAuthentication):
Expand All @@ -14,7 +14,7 @@ class CustomBearerAuthentication(TokenAuthentication):
def get_model(self) -> Type[APIAuthToken]:
return APIAuthToken

def authenticate_credentials(self, raw_key) -> tuple[User | Team | None, APIAuthToken]:
def authenticate_credentials(self, raw_key) -> tuple[User | Organization | None, APIAuthToken]:
model = self.get_model()

try:
Expand All @@ -26,6 +26,6 @@ def authenticate_credentials(self, raw_key) -> tuple[User | Team | None, APIAuth
raise AuthenticationFailed("Token has expired.")

# todo: make sure this is safe to set request.user = <Team> obj
return token.user or token.team, token
return token.user or token.organization, token

# todo: override more methods + add hashing
2 changes: 1 addition & 1 deletion backend/api/public/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework.response import Response
from rest_framework import status

from backend.models import TeamMemberPermission, Team, Client
from backend.models import TeamMemberPermission, Organization, Client

import logging

Expand Down
4 changes: 2 additions & 2 deletions backend/api/public/endpoints/Invoices/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

def get_client(request: APIRequest) -> Client | None:
if request.team:
client = Client.objects.get(organization=request.team, id=request.data.get("client_id"))
client = Client.objects.get(organization=request.team, id=request.data.get("client_id")) # type: ignore[misc]
return client
elif request.user:
client = Client.objects.get(user=request.user, id=request.data.get("client_id"))
client = Client.objects.get(user=request.user, id=request.data.get("client_id")) # type: ignore[misc]
return client
return None

Expand Down
5 changes: 3 additions & 2 deletions backend/api/public/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework.response import Response

from backend.api.public import APIAuthToken
from backend.models import Team
from backend.models import Organization


class AttachTokenMiddleware(MiddlewareMixin):
Expand Down Expand Up @@ -42,6 +42,7 @@ def process_request(self, request):
# No team_id provided, proceed with user context
return

team = Team.objects.filter(id=team_id).first()
team = Organization.objects.filter(id=team_id).first()

request.team = team
request.actor = team
9 changes: 5 additions & 4 deletions backend/api/public/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.contrib.auth.hashers import check_password, make_password
import binascii
import os
from django.utils import timezone

from backend.models import OwnerBase

class APIAuthToken(models.Model):

class APIAuthToken(OwnerBase):
id = models.AutoField(primary_key=True)

hashed_key = models.CharField("Key", max_length=128, unique=True)
Expand All @@ -19,9 +23,6 @@ class APIAuthToken(models.Model):
active = models.BooleanField("Active", default=True, help_text="If the key is active")
scopes = models.JSONField("Scopes", default=list, help_text="List of permitted scopes")

user = models.ForeignKey("backend.User", on_delete=models.CASCADE, null=True, blank=True)
team = models.ForeignKey("backend.Team", on_delete=models.CASCADE, null=True, blank=True, related_name="tokens")

class Meta:
verbose_name = "API Key"
verbose_name_plural = "API Keys"
Expand Down
4 changes: 2 additions & 2 deletions backend/api/public/types.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from rest_framework.request import Request

from backend.api.public import APIAuthToken
from backend.models import User, Team
from backend.models import User, Organization


class APIRequest(Request):
user: User
auth: APIAuthToken
api_token: APIAuthToken
team: Team | None
team: Organization | None
team_id: int | None
27 changes: 9 additions & 18 deletions backend/api/receipts/delete.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest, JsonResponse, HttpResponse, HttpResponseRedirect, QueryDict
from django.shortcuts import render, redirect
from django.urls import resolve, Resolver404, reverse
from django.http import JsonResponse
from django.shortcuts import render
from django.views.decorators.http import require_http_methods

from backend.models import Receipt
from backend.types.htmx import HtmxHttpRequest
from backend.types.requests import WebRequest


@require_http_methods(["DELETE"])
@login_required
def receipt_delete(request: HtmxHttpRequest, id: int):
def receipt_delete(request: WebRequest, id: int):
try:
receipt = Receipt.objects.get(id=id)
except Receipt.DoesNotExist:
Expand All @@ -20,20 +19,12 @@ def receipt_delete(request: HtmxHttpRequest, id: int):
if not receipt:
return JsonResponse(status=404, data={"message": "Receipt not found"})

if not receipt.has_access(request.user):
if not receipt.has_access(request.actor):
return JsonResponse({"message": "You do not have permission to delete this invoice"}, status=404)

receipt.delete()
messages.success(request, f"Receipt deleted with the name of {receipt.name}")
if request.user.logged_in_as_team:
return render(
request,
"pages/receipts/_search_results.html",
{"receipts": Receipt.objects.filter(organization=request.user.logged_in_as_team).order_by("-date")},
)
else:
return render(
request,
"pages/receipts/_search_results.html",
{"receipts": Receipt.objects.filter(user=request.user).order_by("-date")},
)
Receipt.objects.filter()
return render(
request, "pages/receipts/_search_results.html", {"receipts": Receipt.filter_by_owner(owner=request.actor).order_by("-date")}
)
12 changes: 4 additions & 8 deletions backend/api/receipts/new.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

from backend.decorators import quota_usage_check
from backend.models import Receipt, QuotaUsage
from backend.types.htmx import HtmxHttpRequest
from backend.types.requests import WebRequest


@require_http_methods(["POST"])
@quota_usage_check("receipts-count", api=True, htmx=True)
@login_required
def receipt_create(request: HtmxHttpRequest):
def receipt_create(request: WebRequest):
if not request.htmx:
return redirect("receipts dashboard")
file = request.FILES.get("receipt_image") # InMemoryUploadedFile
Expand Down Expand Up @@ -42,14 +42,10 @@ def receipt_create(request: HtmxHttpRequest):
"merchant_store": merchant_store,
"purchase_category": purchase_category,
"total_price": total_price,
"owner": request.actor,
}

if request.user.logged_in_as_team:
receipt_data["organization"] = request.user.logged_in_as_team
receipts = Receipt.objects.filter(organization=request.user.logged_in_as_team).order_by("-date")
else:
receipt_data["user"] = request.user
receipts = Receipt.objects.filter(user=request.user).order_by("-date")
receipts = Receipt.filter_by_owner(owner=request.actor).order_by("-date")

receipt = Receipt(**receipt_data)
QuotaUsage.create_str(request.user, "receipts-count", receipt.id)
Expand Down
6 changes: 3 additions & 3 deletions backend/api/teams/create.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.views.decorators.http import require_POST

from backend.decorators import *
from backend.models import Team, QuotaUsage
from backend.models import Organization, QuotaUsage
from backend.types.htmx import HtmxHttpRequest


Expand All @@ -14,11 +14,11 @@ def create_team(request: HtmxHttpRequest):
messages.error(request, "A team name field must be filled.")
return render(request, "partials/messages_list.html")

if Team.objects.filter(name=name).exists():
if Organization.objects.filter(name=name).exists():
messages.error(request, "A team with this name already exists.")
return render(request, "partials/messages_list.html")

team = Team.objects.create(name=name, leader=request.user)
team = Organization.objects.create(name=name, leader=request.user)

QuotaUsage.create_str(request.user, "teams-count", team.id)
QuotaUsage.create_str(request.user, "teams-joined", team.id)
Expand Down
8 changes: 4 additions & 4 deletions backend/api/teams/invites.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from backend.decorators import *
from backend.models import Notification, Team, TeamInvitation, User
from backend.models import Notification, Organization, TeamInvitation, User
from backend.types.htmx import HtmxHttpRequest


def delete_notification(user: User, code: TeamInvitation):
notification = Notification.objects.filter(
user=user,
message="New Team Invite",
message="New Organization Invite",
action="modal",
action_value="accept_invite",
extra_type="accept_invite_with_code",
Expand Down Expand Up @@ -43,7 +43,7 @@ def check_team_invitation_is_valid(request, invitation: TeamInvitation, code=Non
def send_user_team_invite(request: HtmxHttpRequest):
user_email = request.POST.get("email")
team_id = request.POST.get("team_id", "")
team = Team.objects.filter(leader=request.user, id=team_id).first()
team = Organization.objects.filter(leader=request.user, id=team_id).first()

def return_error_notif(request: HtmxHttpRequest, message: str, autohide=None):
messages.error(request, message)
Expand Down Expand Up @@ -97,7 +97,7 @@ def return_error_notif(request: HtmxHttpRequest, message: str, autohide=None):

Notification.objects.create(
user=user,
message=f"New Team Invite",
message=f"New Organization Invite",
action="modal",
action_value="accept_invite",
extra_type="accept_invite_with_code",
Expand Down
4 changes: 2 additions & 2 deletions backend/api/teams/kick.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.http import HttpRequest
from django.shortcuts import redirect

from backend.models import User, Team
from backend.models import User, Organization


def kick_user(request: HttpRequest, user_id):
Expand All @@ -16,7 +16,7 @@ def kick_user(request: HttpRequest, user_id):
messages.error(request, "Invalid confirmation")
return redirect("teams:dashboard")

team: Team | None = user.teams_joined.first()
team: Organization | None = user.teams_joined.first()
if not team:
messages.error(request, "User is not apart of your team")
return redirect("teams:dashboard")
Expand Down
2 changes: 1 addition & 1 deletion backend/api/teams/leave.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def return_error_notif(request: HtmxHttpRequest, message: str):


def leave_team_confirmed(request: HtmxHttpRequest, team_id):
team: Team | None = Team.objects.filter(id=team_id).first()
team: Organization | None = Organization.objects.filter(id=team_id).first()

if not team:
return return_error_notif(request, "Team not found")
Expand Down
4 changes: 2 additions & 2 deletions backend/api/teams/switch_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.http import HttpResponse
from django.shortcuts import render

from backend.models import Team
from backend.models import Organization
from backend.types.htmx import HtmxHttpRequest


Expand All @@ -19,7 +19,7 @@ def switch_team(request: HtmxHttpRequest, team_id):
response["HX-Refresh"] = "true"
return response

team: Team | None = Team.objects.filter(id=team_id).first()
team: Organization | None = Organization.objects.filter(id=team_id).first()

if not team:
messages.error(request, "Team not found")
Expand Down
10 changes: 7 additions & 3 deletions backend/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from django.db import connection, OperationalError
from django.http import HttpResponse

from backend.models import User
from backend.models import User, Organization
from backend.types.htmx import HtmxAnyHttpRequest
from backend.types.requests import WebRequest


class HealthCheckMiddleware:
Expand Down Expand Up @@ -55,12 +56,15 @@ def __call__(self, request):


class CustomUserMiddleware(MiddlewareMixin):
def process_request(self, request):
def process_request(self, request: WebRequest):
user = get_user(request)

# Replace request.user with CustomUser instance if authenticated
if user.is_authenticated:
request.user = User.objects.get(pk=user.pk)

request.actor = request.user.logged_in_as_team or request.user
else:
# If user is not authenticated, set request.user to AnonymousUser
request.user = AnonymousUser()
request.user = AnonymousUser() # type: ignore[assignment]
request.actor = request.user
Loading

0 comments on commit c5db885

Please sign in to comment.