1010from django .core .files .storage import default_storage
1111from django .urls import reverse
1212from django .db import models , transaction
13- from django .core .validators import MaxValueValidator , MinValueValidator
13+ from django .core .validators import (
14+ MaxValueValidator ,
15+ MinValueValidator ,
16+ MinLengthValidator ,
17+ )
1418from django .core .exceptions import ImproperlyConfigured
15- from cryptography .fernet import Fernet
19+ from cryptography .fernet import Fernet , InvalidToken
1620from cryptography .hazmat .primitives .kdf .pbkdf2 import PBKDF2HMAC
1721from cryptography .hazmat .primitives import hashes
1822from django .forms import ValidationError
@@ -103,24 +107,7 @@ def __str__(self) -> str:
103107 return f"{ self .user .username } - { self .organization .name } "
104108
105109
106- class ImapConnection (models .Model ):
107- server = models .CharField (max_length = 200 , validators = [is_domain_or_ip ])
108- port = models .IntegerField (db_default = 993 )
109- email = models .EmailField (max_length = 200 , unique = True )
110- password = models .CharField (max_length = 200 )
111- organization = models .ForeignKey (Organization , on_delete = models .CASCADE )
112-
113- def __str__ (self ) -> str :
114- return f"{ self .email } |{ self .server } :{ self .port } "
115-
116- def save (self , * args , ** kwargs ) -> None : # type: ignore[no-untyped-def]
117- if self .password :
118- self .password = self ._encrypt_password (self .password )
119- if self .server :
120- self .server = self .server .lower ()
121- self .full_clean ()
122- super ().save (* args , ** kwargs )
123-
110+ class EncryptionMixin :
124111 def _fernet (self ) -> Fernet :
125112 kdf = PBKDF2HMAC (
126113 algorithm = hashes .SHA256 (), length = 32 , salt = FIXED_SALT , iterations = 100000
@@ -143,6 +130,29 @@ def decrypt_password(self, encrypted_password: str) -> str:
143130 return f .decrypt (encrypted_password .encode ()).decode ()
144131
145132
133+ class ImapConnection (EncryptionMixin , models .Model ):
134+ server = models .CharField (max_length = 200 , validators = [is_domain_or_ip ])
135+ port = models .IntegerField (db_default = 993 )
136+ email = models .EmailField (max_length = 200 , unique = True )
137+ password = models .CharField (max_length = 200 )
138+ organization = models .ForeignKey (Organization , on_delete = models .CASCADE )
139+
140+ def __str__ (self ) -> str :
141+ return f"{ self .email } |{ self .server } :{ self .port } "
142+
143+ def save (self , * args , ** kwargs ) -> None : # type: ignore[no-untyped-def]
144+ if self .password :
145+ try :
146+ self .decrypt_password (self .password )
147+ # Password is already encrypted
148+ except InvalidToken :
149+ self .password = self ._encrypt_password (self .password )
150+ if self .server :
151+ self .server = self .server .lower ()
152+ self .full_clean ()
153+ super ().save (* args , ** kwargs )
154+
155+
146156class Course (models .Model ):
147157 title = models .CharField (max_length = 200 )
148158 slug = models .SlugField (
@@ -654,3 +664,30 @@ def save(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
654664 )
655665 self .full_clean ()
656666 super ().save (* args , ** kwargs )
667+
668+
669+ class ApiKey (EncryptionMixin , models .Model ):
670+ key = models .CharField (
671+ max_length = 256 , unique = True , validators = [MinLengthValidator (50 )]
672+ )
673+ created_at = models .DateTimeField (auto_now_add = True )
674+ created_by = models .ForeignKey (
675+ User , on_delete = models .SET_NULL , null = True , blank = True
676+ )
677+
678+ @classmethod
679+ def generate_key (cls ) -> str :
680+ return (
681+ base64 .urlsafe_b64encode (uuid .uuid4 ().bytes + uuid .uuid4 ().bytes )
682+ .decode ()
683+ .rstrip ("=" )
684+ )
685+
686+ def save (self , * args , ** kwargs ) -> None : # type: ignore[no-untyped-def]
687+ try :
688+ self .decrypt_password (self .key )
689+ # Key is already encrypted
690+ except InvalidToken :
691+ self .key = self ._encrypt_password (self .key )
692+ self .full_clean ()
693+ super ().save (* args , ** kwargs )
0 commit comments