-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathapi_key.py
More file actions
109 lines (91 loc) · 3.59 KB
/
api_key.py
File metadata and controls
109 lines (91 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import uuid6
import secrets
import string
from typing import Literal, Optional, TYPE_CHECKING
from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.contrib.auth.hashers import make_password
from .workspace import Workspace
from .utils import PermissionChecker
if TYPE_CHECKING:
from django.contrib.auth import get_user_model
User = get_user_model()
class APIKeyQueryset(models.QuerySet):
def visible(self, principal: Optional["User"]):
if principal is None:
return self.none()
elif hasattr(principal, "account_type"):
if principal.account_type == "admin":
return self
else:
return self.filter(
Q(workspace__owner=principal)
| Q(
workspace__collaborators__user=principal,
workspace__collaborators__role__permissions__resource_type__in=[
"*",
"APIKey",
],
workspace__collaborators__role__permissions__permission_type__in=[
"*",
"view",
],
)
)
else:
return self.none()
class APIKeyManager(models.Manager.from_queryset(APIKeyQueryset)):
def create_with_key(self, **kwargs):
prefix = "".join(
secrets.choice(string.ascii_letters + string.digits) for _ in range(12)
)
secret = secrets.token_urlsafe(32)
raw_key = f"{prefix}{secret}"
hashed_key = f"{prefix}${make_password(raw_key)}"
kwargs["hashed_key"] = hashed_key
obj = self.model(**kwargs)
obj.save()
return obj, raw_key
class APIKey(models.Model, PermissionChecker):
id = models.UUIDField(primary_key=True, default=uuid6.uuid7, editable=False)
workspace = models.ForeignKey(
"Workspace", on_delete=models.DO_NOTHING, related_name="apikeys"
)
role = models.ForeignKey(
"Role", on_delete=models.DO_NOTHING, related_name="apikeys"
)
name = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
created_at = models.DateTimeField(default=timezone.now)
expires_at = models.DateTimeField(null=True, blank=True)
last_used = models.DateTimeField(null=True, blank=True)
is_active = models.BooleanField(default=True)
hashed_key = models.CharField(max_length=128, editable=False)
objects = APIKeyManager()
def __str__(self):
return f"{self.name} - {self.id}"
def generate_key(self):
prefix = "".join(
secrets.choice(string.ascii_letters + string.digits) for _ in range(12)
)
secret = secrets.token_urlsafe(32)
raw_key = f"{prefix}{secret}"
self.hashed_key = f"{prefix}${make_password(raw_key)}"
self.save(update_fields=["hashed_key"])
return raw_key
@classmethod
def can_principal_create(cls, principal: Optional["User"], workspace: Workspace):
return cls.check_create_permissions(
principal=principal, workspace=workspace, resource_type="APIKey"
)
def get_principal_permissions(
self, principal: Optional["User"]
) -> list[Literal["edit", "delete", "view"]]:
permissions = self.check_object_permissions(
principal=principal, workspace=self.workspace, resource_type="APIKey"
)
return permissions
class Meta:
verbose_name = "API Key"
verbose_name_plural = "API Keys"