Turn off user social registration in django-allauth? - django

I noticed looking through the django-allauth templates there's a signup_closed.html users can be redirected to when user registration is closed or disabled. Does anyone who's familiar with that module know if there's a pre-configured setting that can be set in settings.py to turn off new user registration via existing social apps? Or do I need to configure that myself? I've read the full docs for allauth and I don't see any mention of it. Thanks.

Looks like you need to override is_open_for_signup on your adapter.
See the code.

There is no pre-configured setting but it's easy to make one (this is what I do).
# settings.py
# Point to custom account adapter.
ACCOUNT_ADAPTER = 'myproject.myapp.adapter.CustomAccountAdapter'
# A custom variable we created to tell the CustomAccountAdapter whether to
# allow signups.
ACCOUNT_ALLOW_SIGNUPS = False
# myapp/adapter.py
from django.conf import settings
from allauth.account.adapter import DefaultAccountAdapter
class CustomAccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request):
"""
Whether to allow sign ups.
"""
allow_signups = super(
CustomAccountAdapter, self).is_open_for_signup(request)
# Override with setting, otherwise default to super.
return getattr(settings, 'ACCOUNT_ALLOW_SIGNUPS', allow_signups)
This is flexible, especially if you have multiple environments (e.g. staging) and want to allow user registration in staging before setting it live in production.

More information at http://django-allauth.readthedocs.io/en/latest/advanced.html#custom-redirects.
You need to subclass allauth.account.adapter.DefaultAccountAdapter to override is_open_for_signup, and then set ACCOUNT_ADAPTER to your class in settings.py

Related

Extend django-invitations + django-allauth account adapter

I'm using django-allauth and django-invitations. Following django-allauth's docs, I have created a custom adapter so I can override the get_signup_redirect_url method
from allauth.account.adapter import DefaultAccountAdapter
class MyAccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
# custom redirects here
And then in my settings file I have
ACCOUNT_ADAPTER = 'accounts.adapter.MyAccountAdapter'
I'm also using django-invitations, whose docs say that for integrating with allauth, I need to set
ACCOUNT_ADAPTER = 'invitations.models.InvitationsAdapter'
This obviously causes a problem because then I'm no longer using my custom adapter.
How can I integrate django-allauth and django-invitations while also overriding the adapter get_login_redirect_url method?
This is how I solved the problem in django-invitations v1.9. Hopefully it's not relevant in the near future.
In my project, I have an "accounts" app where I put all my accounts related files. In the __init__.py for the app, I put the following delightful monkey patch 🙉
from invitations import adapters
def new_get_invitations_adapter():
from allauth.account.adapter import get_adapter
return get_adapter()
adapters.get_invitations_adapter = new_get_invitations_adapter
It just bypasses the django-invitations logic that works out which adapter to use by always returning the one django-allauth would use. And django-allauth returns the custom one, which I specified in the project settings.py.
ACCOUNT_ADAPTER = 'accounts.adapters.MyAccountAdapter'

Can Django set permissions on apps instead of models

I want to have authority control over my project. Some users can see some app's entries while others can't. I want to set permissions on apps instead of models, I have searched, but only found how to set permissions on models. So, I want to know how to set permissions on apps.
You can make decorators to selectively allow users to access the page
Make this decorator
def filter_users(func):
def checker(request,*args,**kwargs):
if some_condition: #This condition will tell you whether to allow this perticular user
return func(request,*args,**kwargs)
else:
return render('invalid.html') #return a page telling the user that he is not allowed
return checker
Now just apply this decorator to all the views that you want to prevent 'some' users from accessing.
Ex:
#filter_users
def some_view(request):
#Do Something...
Now only allowed users will be able to see the view, rest all will get the invalid page
You can apply this decorator to all the views of the perticular app that you want to restrict access to

Django multitenant: how to customize django setting "ACCOUNT_EMAIL_VERIFICATION" per tenant?

Django==1.11.7
django-tenant-schemas==1.8.0
django-allauth==0.34.0
Multi tenant site using django-tenant-schemas (postgres).
On different tenants, different settings are required.
More specifically, different setting is required for ACCOUNT_EMAIL_VERIFICATION
1 tenant needs ACCOUNT_EMAIL_VERIFICATION = "optional" while another one needs ACCOUNT_EMAIL_VERIFICATION ="mandatory"
Looking in the source code, the setting looks not customisable, it is fixed for the whole django site.
-> How can this be done?
You can compute the settings at runtime, since it's simply a python code.
Set that specific code programmatically, using your preferred way. One example:
# predefine the settings per tenant
ACCOUNT_EMAIL_VERIFICATION_PER_TENANT = {
"tenant_x": "mandatory",
"tenant_y": "optional",
}
# implement get_tenant
def get_tenant():
# here be tenant logic
pass
this_tenant = get_tenant()
ACCOUNT_EMAIL_VERIFICATION = ACCOUNT_EMAIL_VERIFICATION_PER_TENANT[get_tenant()]
Or you can have multiple settings files and join them as you wish. Here's how django does.
Oh and if you want to separate the logic from the settings file and have it run before evaluating the settings perhaps, you can inspect what is the trail of execution when you launch your server (e.g. starting from manage.py and insert your get_tenant logic somewhere in between). Most probably it will be somewhere starting from the wsgi.py file - where the application instance gets created and all the django fun begins.
When it comes to programming, you are always in control.
Solved in following way:
In settings.py:
try:
ACCOUNT_EMAIL_VERIFICATION = os.environ['ACCOUNT_EMAIL_VERIFICATION_OVERRIDE']
except KeyError:
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
In wsgi.py file of the tenant where e-mail verification is optional:
os.environ['ACCOUNT_EMAIL_VERIFICATION_OVERRIDE'] = 'optional'
wsgi files for the other tenants remain unchanged.
Gave the bounty to Adelin as he suggested to look into the wsgi file.
I stumbled upon this situation, and my dynamic solution is a middleware as follows without any hardcoding tenant's names
from django.conf import settings
from django.db import connection
from django_tenants.utils import get_public_schema_name, get_tenant_model
class TenantSettingsMiddleWare:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
self.request = request
self.overload_settings()
response = self.get_response(request)
return response
def overload_settings(self):
current_schema_obj = get_tenant_model().objects.get(schema_name=connection.schema_name)
settings.DEFAULT_FROM_EMAIL = 'admin#{}'.format(current_schema_obj.domains.last())
Cheers 🍻🍻🍻

django-allauth: Only allow users from a specific google apps domain

I am a newbie at Django. Using django-allauth I have set up single click sign in. I obtained my domain credentials ( client_id and secret_key) from google api console. But the problem is django-allauth is letting me login from any google account while I want the email addresses to be restricted to my domain ( #example.com instead of #gmail.com)
django-social-auth has the white listed domains parameter for this, how do I include this information in allauth?
I found django-allauth much easier to set up after spending hours on django-social-auth
Any help would be much appreciated.
Answering my own question-
What you want to do is stall the login after a user has been authenticated by a social account provider and before they can proceed to their profile page. You can do this with the
pre_social_login method of the DefaultSocialAccountAdapter class in allauth/socialaccount/adaptor.py
Invoked just after a user successfully authenticates via a
social provider, but before the login is actually processed
(and before the pre_social_login signal is emitted).
You can use this hook to intervene, e.g. abort the login by
raising an ImmediateHttpResponse
Why both an adapter hook and the signal? Intervening in
e.g. the flow from within a signal handler is bad -- multiple
handlers may be active and are executed in undetermined order.
Do something like
from allauth.socialaccount.adaptor import DefaultSocialAccountAdapter
class MySocialAccount(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
u = sociallogin.account.user
if not u.email.split('#')[1] == "example.com"
raise ImmediateHttpResponse(render_to_response('error.html'))
This is not an exact implementation but something like this works.
Here's an alternate solution:
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class CustomAccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request):
return False # No email/password signups allowed
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
def is_open_for_signup(self, request, sociallogin):
u = sociallogin.user
# Optionally, set as staff now as well.
# This is useful if you are using this for the Django Admin login.
# Be careful with the staff setting, as some providers don't verify
# email address, so that could be considered a security flaw.
#u.is_staff = u.email.split('#')[1] == "customdomain.com"
return u.email.split('#')[1] == "customdomain.com"
This code can live anywhere, but assuming it's in mysite/adapters.py, you'll also need the following in your settings.py:
ACCOUNT_ADAPTER = 'mysite.adapters.CustomAccountAdapter'
SOCIALACCOUNT_ADAPTER = 'mysite.adapters.CustomSocialAccountAdapter'
You could do something in the line of overriding allauth's allauth.socialaccount.forms.SignupForm and checking the domain during the signup process.
Discalmer: this is all written without testing, but something in the line of that should work.
# settings.py
# not necesarry, but it would be a smart way to go instead of hardcoding it
ALLOWED_DOMAIN = 'example.com'
.
# forms.py
from django.conf import settings
from allauth.socialaccount.forms import SignupForm
class MySignupForm(SignupForm):
def clean_email(self):
data = self.cleaned_data['email']
if data.split('#')[1].lower() == settings.ALLOWED_DOMAIN:
raise forms.ValidationError(_(u'domena!'))
return data
in your urls override allauth defaults (put this before the include of django-allauth)
# urls.py
from allauth.socialaccount.views import SignupView
from .forms import MySignupForm
urlpatterns = patterns('',
# ...
url(r"^social/signup/$", SignupView.as_view(form_class=MySignupForm), name="account_signup"),
# ...
)
I'm not sure for the "^social/signup/$", recheck that.

add auth validation in Django

I have a django application and I want to add my own auth validation and check if the user is expired (check the expiration date from some of my models). I want to raise a ValidationError in the login page with appropriate message if user is expired. What's the best way to do that?
Thanks, Alex
If you REALLY want to do your own custom authentication, you should read custom backends in the Django Documentation.
You probably don't want to do your own though. It sucks. Really. Unless there is a really really good reason, you should avoid your own authentication. The main reason being, that many django apps stop working if you don't use the built in User model. If you need to authenticate against an existing source, that's a valid reason for creating your own backend. But there are pitfalls, and you still probably want to use the built in User model for your custom backend.
You should tell us why you want to do your own custom authentication, and perhaps we can help you achieve your requirement, without writing a custom backend.
Edit
Ok, I think I understand what you mean now. What (I think) you want, is a custom authentication form. We currently use custom form (though we have a different unavoidable backend), so you should be able to use the following quite easily.
from django.contrib.auth.forms import AuthenticationForm
from django import forms
from myproject.myapp.models import MyClass
class CustomAuthForm(AuthenticationForm):
def clean(self):
cleaned_data = super(CustomAuthForm, self).clean()
user = self.user_cache # set by super class
if user.my_class.expired:
raise forms.ValidationError('This User has Expired!')
return cleaned_data
Then, to use this custom authentication form, you need a URL in your urls.py:
from myproject.myapp.forms import CustomAuthForm
url(r'^login/$', 'django.contrib.auth.views.login', name='login',
kwargs={'template_name':'youproject/login.html', 'authentication_form':CustomAuthForm}),
I see now that your question originally stated you wanted custom validation, not authentication. My apology for not reading your question correctly.