Социальная аутентификация (dj_rest_auth) с пользователем с несколькими ролями
#user_model:
class User(AbstractUser, PermissionsMixin):
class Role(models.TextChoices):
SELLER = "seller"
BUYER = "buyer"
SUPER = "super"
name = models.CharField(max_length=100)
phone = models.CharField(max_length=15)
email = models.EmailField()
created_at = models.DateTimeField(default=timezone.now)
shipping_address = models.CharField(max_length=100, blank=True, null=True)
role = models.CharField(
max_length=100, default=Role.SELLER.value, choices=Role.choices
)
updated_at = models.DateTimeField(default=timezone.now, null=True)
first_name = None
last_name = None
profile_picture = models.ImageField(
default="profile 1.jpg", upload_to="profile_images"
)
is_staff = models.BooleanField(default=False)
is_blocked = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
username = models.CharField(max_length=30, unique=True)
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["email", "name", "role"]
objects = myUserManager()
class Meta:
unique_together = [("email", "role"), ("phone", "role")]
ordering = ["id"]
db_table = "users"
Я реализовал dj_rest_auth с Google Authentication, и он функционирует так, как задумано. Однако возникает ограничение, когда он поддерживает только пользователей с одной ролью. Например, если мы создаем пользователя с ролью seller, он успешно создает учетную запись и входит в систему. Однако если мы попытаемся войти в систему с ролью пользователя, например покупатель, она ошибочно вернет пользователя-продавца и войдет вместо него.
#CustomSocialLoginSerializer:
import requests
from allauth.socialaccount.models import SocialAccount
from dj_rest_auth.registration.serializers import SocialLoginSerializer
from django.contrib.auth import get_user_model
from django.core.files.base import ContentFile
from rest_framework import serializers
from rest_framework.response import Response
User = get_user_model()
class CustomSocialSerializer(SocialLoginSerializer):
role = serializers.ChoiceField(choices=User.Role.choices, required=True)
def validate(self, attrs):
attrs = super().validate(attrs)
user = attrs["user"]
role = attrs.get("role")
if role:
user.role = role
user.username = User.generate_username(user.email, user.role)
try:
sociallogin = SocialAccount.objects.get(user=user)
user.name = sociallogin.extra_data.get("name", "")
picture = sociallogin.extra_data.get("picture", "")
if picture:
response = requests.get(picture)
if response.status_code == 200:
user.profile_picture.save(
f"{user.name}_picture.jpg",
ContentFile(response.content),
save=True,
)
except:
return Response({"Message": "Failed to save extra Details !"})
user.save()
return attrs
#views.py:
class CustomLoginView(SocialLoginView):
serializer_class = CustomSocialSerializer
class GoogleLogin(CustomLoginView):
adapter_class = GoogleOAuth2Adapter
callback_url = "/"
client_class = OAuth2Client
Я обнаружил, что процесс аутентификации Google ищет UID аккаунта SocialAccount, и если находит совпадение, то просто регистрирует пользователя на основе этого ID. Как я могу изменить эту функциональность, чтобы добиться другого поведения?
def _lookup_by_socialaccount(self):
assert not self.is_existing
try:
a = SocialAccount.objects.get(
provider=self.account.provider, uid=self.account.uid
)
# Update account
a.extra_data = self.account.extra_data
self.account = a
self.user = self.account.user
a.save()
signals.social_account_updated.send(
sender=SocialLogin, request=context.request, sociallogin=self
)
# Update token
if app_settings.STORE_TOKENS and self.token:
assert not self.token.pk
try:
t = SocialToken.objects.get(
account=self.account, app=self.token.app
)
t.token = self.token.token
if self.token.token_secret:
# only update the refresh token if we got one
# many oauth2 providers do not resend the refresh token
t.token_secret = self.token.token_secret
t.expires_at = self.token.expires_at
t.save()
self.token = t
except SocialToken.DoesNotExist:
self.token.account = a
self.token.save()
return True
except SocialAccount.DoesNotExist:
pass