Присоединение пользователя по электронной почте
Я хочу переопределить конвейер, чтобы связать пользователя с его электронной почтой для учетных записей, которые только активны. Но мне нужен бэкенд для обычного входа в систему. AUTHENTICATION_BACKENDS(django.contrib.auth.backends.AllowAllUsersModelBackend) позволяет аутентифицировать всех пользователей, но я хочу, чтобы только определенные пользователи с параметром is_active аутентифицировались с помощью Google Login.
settings.py
AUTHENTICATION_BACKENDS = ['social_core.backends.google.GoogleOAuth2','django.contrib.auth.backends.AllowAllUsersModelBackend',]
# Extends default user with additional fields
AUTH_USER_MODEL = 'pages.Profile'
SOCIAL_AUTH_USER_MODEL = 'pages.Profile'
# social auth configs for google
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = config('GOOGLE_OAUTH2_KEY')
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = config('GOOGLE_OAUTH2_SECRET')
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['https://www.googleapis.com/auth/calendar']
SOCIAL_AUTH_JSONFIELD_ENABLED = True
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'access_type': 'offline','approval_prompt':'force'}
SESSION_COOKIE_SAMESITE = None
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.social_auth.associate_by_email', # <--- enable this one
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
'pages.pipeline.save_token'
)
views.py
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request.POST)
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user:
if user.is_active:
auth_login(request, user)
return redirect('home')
else:
messages.error(request,'User blocked',extra_tags='login')
return redirect('login')
else:
messages.error(request,'username or password not correct',extra_tags='login')
return redirect('login')
else:
form = AuthenticationForm()
return render(request, 'registration/login.html',{'form':form})
Чтобы отменить исходную частичную,
В вашем pipeline.py (создайте его, если у вас нет его в каталоге приложения), определите метод:
from social_core.pipeline.partial import partial
from social_core.exceptions import AuthAlreadyAssociated, AuthException, AuthForbidden
@partial
def associate_by_email(backend, details, user=None, *args, **kwargs):
# No user
if user:
return None
email = details.get('email')
if email:
# Try to associate accounts registered with the same email address,
# only if it's a single object. AuthException is raised if multiple
# objects are returned.
users = list(backend.strategy.storage.user.get_users_by_email(email))
#That's the line you want to add
active_users = [user for user in users if user.is_active]
if len(active_users) == 0:
return None
elif len(active_users) > 1:
raise AuthException(
backend,
'The given email address is associated with another account'
)
else:
return {'user': active_users[0],
'is_new': False}
А затем в вашем settings.py замените строку
SOCIAL_AUTH_PIPELINE = (
...
'social_core.pipeline.social_auth.associate_by_email', # <--- enable this
...
)
с:
SOCIAL_AUTH_PIPELINE = (
...
'your_app.pipeline.associate_by_email', # <--- enable this one
...
)
Убедитесь в том, что вы позиционируете частицу конвейера там, где она находится вместо исходной.
Оригинальный метод не выполняет проверку, поэтому, чтобы добавить это поведение, вы должны написать свой собственный partial, который переопределит это поведение.
Предположим, что у вас есть приложение Django (здесь для простоты оно называется ваше_приложение - просто обновите имена соответствующим образом.
Оригинальный код для частичного associate_by_email можно найти здесь.
Если вы хотите добавить эту проверку к простой аутентификации, вы можете предоставить свой собственный бэкенд в файле your_app/backends.py:
from django.core.exceptions import ObjectDoesNotExist
from settings import settings
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend
class LocalActiveBackend(object):
def authenticate(self, username=None, password=None):
try:
# Add extra check for is_active
user = User.objects.get(username=username, is_active=True)
except User.DoesNotExist:
return None
pwd_valid = user.check_password(password)
if pwd_valid:
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
А затем добавьте этот новый бэкенд в список доступных бэкендов:
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # default
...
'your_app.backends.LocalActiveBackend',
)
Это позволит вам использовать его с простой аутентификацией по паролю и имени пользователя, если это необходимо.