44import logging
55import os
66import string
7+ import uuid
78from datetime import timedelta
89from io import StringIO
910
@@ -1058,7 +1059,11 @@ def save_user(self, user):
10581059 OrganizationUser = swapper .load_model ("openwisp_users" , "OrganizationUser" )
10591060 RegisteredUser = swapper .load_model ("openwisp_radius" , "RegisteredUser" )
10601061 user .save ()
1061- registered_user = RegisteredUser (user = user , method = "manual" )
1062+ registered_user = RegisteredUser (
1063+ user = user ,
1064+ method = "manual" ,
1065+ organization = self .organization ,
1066+ )
10621067 if self .organization .radius_settings .needs_identity_verification :
10631068 registered_user .is_verified = True
10641069 registered_user .save ()
@@ -1570,14 +1575,12 @@ def is_valid(self, token):
15701575 return self .verified
15711576
15721577 def _validate_already_verified (self ):
1573- try :
1574- if self .user .registered_user .is_verified :
1575- logger .warning (f"User { self .user .pk } is already verified" )
1576- raise exceptions .UserAlreadyVerified (
1577- _ ("This user has been already verified." )
1578- )
1579- except ObjectDoesNotExist :
1580- pass
1578+ RegisteredUser = swapper .load_model ("openwisp_radius" , "RegisteredUser" )
1579+ if RegisteredUser .objects .filter (user = self .user , is_verified = True ).exists ():
1580+ logger .warning (f"User { self .user .pk } is already verified" )
1581+ raise exceptions .UserAlreadyVerified (
1582+ _ ("This user has been already verified." )
1583+ )
15811584
15821585 def __check (self , token ):
15831586 self ._validate_already_verified ()
@@ -1602,12 +1605,23 @@ def __check(self, token):
16021605 return token == self .token
16031606
16041607
1605- class AbstractRegisteredUser (models . Model ):
1606- user = models .OneToOneField (
1608+ class AbstractRegisteredUser (UUIDModel ):
1609+ user = models .ForeignKey (
16071610 settings .AUTH_USER_MODEL ,
16081611 on_delete = models .CASCADE ,
1609- related_name = "registered_user" ,
1610- primary_key = True ,
1612+ related_name = "registered_users" ,
1613+ )
1614+ organization = models .ForeignKey (
1615+ swapper .get_model_name ("openwisp_users" , "Organization" ),
1616+ on_delete = models .CASCADE ,
1617+ null = True ,
1618+ blank = True ,
1619+ related_name = "registered_users" ,
1620+ verbose_name = _ ("organization" ),
1621+ help_text = (
1622+ "The organization this registration info belongs to. "
1623+ "If null, applies to all orgs without specific requirements."
1624+ ),
16111625 )
16121626 method = models .CharField (
16131627 _ ("registration method" ),
@@ -1649,6 +1663,54 @@ class Meta:
16491663 abstract = True
16501664 verbose_name = _ ("Registration Information" )
16511665 verbose_name_plural = verbose_name
1666+ constraints = [
1667+ models .UniqueConstraint (
1668+ fields = ["user" , "organization" ],
1669+ name = "unique_registered_user_per_org" ,
1670+ ),
1671+ models .UniqueConstraint (
1672+ fields = ["user" ],
1673+ condition = Q (organization__isnull = True ),
1674+ name = "unique_global_registered_user" ,
1675+ ),
1676+ ]
1677+
1678+ def clean (self ):
1679+ super ().clean ()
1680+ Model = self ._meta .model
1681+ qs = Model .objects .filter (user = self .user , organization = self .organization )
1682+ if self .pk :
1683+ qs = qs .exclude (pk = self .pk )
1684+ if qs .exists ():
1685+ raise ValidationError (
1686+ _ ("A registration record already exists for this user/organization." )
1687+ )
1688+
1689+ @classmethod
1690+ def get_for_user_and_org (cls , user , organization ):
1691+ try :
1692+ return cls .objects .get (user = user , organization = organization )
1693+ except cls .DoesNotExist :
1694+ return None
1695+
1696+ @classmethod
1697+ def get_or_create_for_user_and_org (cls , user , organization , defaults = None ):
1698+ defaults = defaults or {}
1699+ return cls .objects .get_or_create (
1700+ user = user , organization = organization , defaults = defaults
1701+ )
1702+
1703+ @classmethod
1704+ def get_global_or_org_specific (cls , user , organization = None ):
1705+ if organization :
1706+ try :
1707+ return cls .objects .get (user = user , organization = organization )
1708+ except cls .DoesNotExist :
1709+ pass
1710+ try :
1711+ return cls .objects .get (user = user , organization__isnull = True )
1712+ except cls .DoesNotExist :
1713+ return None
16521714
16531715 @classmethod
16541716 def unverify_inactive_users (cls ):
0 commit comments