How to disable email activation in django-registration app?
In the current tip versions of django-registration there is a simple backend with no email and you can write your own backend to specify the workflow you want. See here https://bitbucket.org/ubernostrum/django-registration/src/fad7080fe769/docs/backend-api.rst for documentation.
To use the simple one without email, just change your urls.py to point to this backend, eg.
(r'^accounts/', include('registration.backends.simple.urls')),
Why not use this method and just use the simple backend that comes with django-registration instead of the default backend?
The new work flow would be...
1. A user signs up by filling out a registration form.
2. The user’s account is created and is active immediately, with no intermediate confirmation or activation step.
3. The new user is logged in immediately.
so on your main urls.py you would change:
url(r'^accounts/', include('registration.backends.default.urls')),
to
url(r'^accounts/', include('registration.backends.simple.urls')),
Better to fix the problem at the root than bandage it by calling commands to automatically activate the user.
Add this method to registration models.py:
def create_active_user(self, username, email, password,
site):
"""
Create a new, active ``User``, generate a
``RegistrationProfile`` and email its activation key to the
``User``, returning the new ``User``.
"""
new_user = User.objects.create_user(username, email, password)
new_user.is_active = True
new_user.save()
registration_profile = self.create_profile(new_user)
return new_user
create_active_user = transaction.commit_on_success(create_active_user)
Then, edit registration/backend/defaults/init.py and find the register() method.
Change the following to call your new method:
#new_user = RegistrationProfile.objects.create_inactive_user(username, email,
#password, site)
new_user = RegistrationProfile.objects.create_active_user(username, email,
password, site)
You could always modify this line to:
new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
password=self.cleaned_data['password1'],
email=self.cleaned_data['email'],
profile_callback=profile_callback,
send_email = False)
Or you could change this line to:
def create_inactive_user(self, username, password, email,
send_email=False, profile_callback=None):
Rather than modifying the registration app, why not just activate the user the same way django-registration does:
user.is_active = True
user.save()
profile.activation_key = "ALREADY_ACTIVATED"
profile.save()
After looking at it even more... I think what you want is to use both solutions. Probably add the above code, just after the change suggested by Dominic (though I would suggest using signals, or subclassing the form if possible)
OK final Answer:
new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
password=self.cleaned_data['password1'],
email=self.cleaned_data['email'],
profile_callback=profile_callback,
send_email = False)
RegistrationProfile.objects.activate_user(new_user.activation_key)
Related
I am using DRF with auth toolkit and it is working fine. However, I want to have a second login api so a user can log in using username and pin number. It is cos we have a USSD application and it is easier to give them a pin based login system.
Currently, I have the following URL that, when called, generates token:
url(r'^signin/', include('oauth2_provider.urls', namespace='oauth2_provider')),
For the ussd app, I want something like that but the auth2 should check pin field, defined in a separate model defined as follows:
class Members(models.Model):
pin=models.IntegerField()
user=models.ForeignKey(User)
I am a little lost where to start.
Using this answer as a base to answer this question, and Django's documentation.
I would say you'd want to create a custom authentication backend, and you'd want a custom user model with two passwords, or using a one-to-one relationship to add the additional password field, something like so:
from django.contrib.auth.models import AbstractBaseUser
class UserExtension(AbstractBaseUser):
user = models.OneToOneField(User)
...
Inheriting from the AbstractBaseUser should add a password field like the user model, (although I haven't tried this). If you prefer the custom user approach, I actually have a github repo that has a custom user app, so if you'd like to get any ideas of how to achieve this check it out.
Or have a look through the documentation.
Either way, once you've got your two passwords, you need to decide which one to use as the pin. If you're using oauth for the pin field and the web applicaiton with the password, I would probably use the standard user password for the pin login, as that way you don't need to change the oauth package to work with your new password. Then for your web application build a custom login. To do this create a custom authentication backend along the lines of:
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
class AuthBackend(object):
supports_object_permissions = True
supports_anonymous_user = False
supports_inactive_user = False
def get_user(self, user_id):
return User.objects.filter(pk=user_id).first()
def authenticate(self, username, password):
user = User.objects.filter(username=username).first()
if not user:
return None
# this is checking the password provided against the secondary password field
return user if check_password(password, user.userextension.password) else None
Then you need to add this authentication backend to your settings:
AUTHENTICATION_BACKENDS = ('myapp.backends.AuthBackend',)
Then create the web application login (as per the stackoverflow answer above):
from django.contrib.auth import authenticate, login
def my_login_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
else:
# Return a 'disabled account' error message
...
else:
# Return an 'invalid login' error message.
...
You should now have a custom authentication login for the web application using your password2 field, and you can use the oauth authentication to work with the standard Django password in which you're going to store the pin. Which I think is what you're trying to do?
NOTE: All of the above I haven't tested, so this may not work perfectly, but it should hopefully be able to at least point you in the right direction and give you a few ideas. If I'm understanding your problem correctly, this is the sort of approach that I would take to tackle the problem.
I have implemented login form for username/password method, and that works perfect.
I want user to be also able to login using their social accounts.
I am using django-allauth to map social users to django-users.
Now I want to allow only those social accounts to login, that are mapped to django-users and not everyone.
Is there a way to override callback view? or something else can be done?
To simply disable registration, you have to overwrite the default account adaptor. If you also want to support social login, you also need to overwrite the default soculaaccount adapter. Add the following code somewhere in one of your apps (e.g. adapter.py):
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.exceptions import ImmediateHttpResponse
class NoNewUsersAccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request):
return False
class SocialAccountWhitelist(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
u = sociallogin.user
print('User {0} is trying to login'.format(u.email))
# Write code here to check your whitelist
if not_in_your_list(u):
raise ImmediateHttpResponse(HttpResponseRedirect('/account/login'))
and then add the following to your settings:
ACCOUNT_ADAPTER = 'path_to_app.adapter.NoNewUsersAccountAdapter'
SOCIALACCOUNT_ADAPTER = 'path_to_app.adapters.SocialAccountWhitelist'
After that, all you need to do is manually create an Account from the Admin pages, and manually create an EmailAddress. For the social login, you will need to write code to somehow check if the email is allowed
I would recommend you add a Staff-Only form to make this easy on you, where you can ask for username, email (and even password) and then do
new_user = Account.objects.create_user(email=email, username=username, password=password)
EmailAddress.objects.create(email=email, user=new_user, verified=True, primary=True)
You can also develop an Invitation scheme, but that is a lot more complicated but quickly googled and found the following project, which I have not personally used, but looks like what you need:
https://github.com/bee-keeper/django-invitations
Finally After reading the documents thoroughly and doing a lot of trials and errors I got to what I was looking for.
I had to set following parameters as a part of configuration specified in docs.
ACCOUNT_EMAIL_REQUIRED (=False)
The user is required to hand over an e-mail address when signing up.
and
SOCIALACCOUNT_QUERY_EMAIL (=ACCOUNT_EMAIL_REQUIRED)
Request e-mail address from 3rd party account provider? E.g. using OpenID AX, or the Facebook “email” permission.
I had to set ACCOUNT_EMAIL_REQUIRED = True as it was required to check if that email id is already registerd with us.
and then finally I overridden pre_social_login like below.
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class NoNewSocialLogin(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
try:
cr_user = auth_user.objects.get(email=sociallogin.user.email)
if cr_user and cr_user.is_active:
user_login = login(request, cr_user, 'django.contrib.auth.backends.ModelBackend')
raise ImmediateHttpResponse(HttpResponseRedirect(reverse('protect')))
else:
raise ImmediateHttpResponse(render_to_response("account/authentication_error.html"))
except ObjectDoesNotExist as e:
raise ImmediateHttpResponse(render_to_response("socialaccount/authentication_error.html"))
except Exception as e:
raise ImmediateHttpResponse(HttpResponseRedirect(reverse('protect')))
I have an edit view for editing models objects. I want to make it password protected with a password I generate and put in my model. I don't want to use django Users
Let's say I have something like this:
models.py
class Job(models.Model):
......
job_uuid = models.CharField("UUID", max_length=36, default=make_uuid)
password = models.CharField("password", max_length=36)
views.py
def edit_job(request, job_uuid):
job = get_object_or_404(Job, job_uuid=job_uuid)
job_form = EditJobForm(instance=job)
c = {'job_form': job_form,
}
c.update(csrf(request))
return render_to_response('jobs/edit_job.html', c, context_instance=RequestContext(request))
I wanted to protect this URL with the password from the object. I found this app django-password-required but this app uses one password you set in the settings.py
Any easy package or way to do this?
You can write a decorator that will check if password access is required for edit_job view.
Then decorator can use password from session/cookies to verify the access.
If password does not match or not present, you can redirect to different view to show password form, and store it in session.
Hope this helps.
I am trying to make a website, where people only put their email addresses and they are logged in with cookies and all. At a later stage, i will ask them provide password and names, but NO username will be used. I am trying to do this with django-registraition, but i get errors and i have a few problems.
First to disable usernames as a login feature, i put str(time()) instead of username - i was looking for something that will change every time.
However, when I skip the authentication (which i currently don't need) i get error:
'RegistrationProfile' object has no attribute 'backend'
Alternatively, i can leave the authentication but then i don't know how to authenticate it only with email and no password. Also, i don't know how to make the next line work:
auth.login(request, ProfileUser)
If anyone can get me out of here, it would be awesome. Here is some code:
my form Class:
class RegistrationFormCustom(forms.Form):
email = forms.EmailField()
def do_save(self):
new_u = User(username=str(time()),email= self.cleaned_data.get('email'),)
new_u.save()
new_p = Profile.objects.create_profile(new_u)
new_p.save()
return new_p
my view:
def registerCustom(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None,
initial={}):
form = RegistrationFormCustom(initial=initial)
if request.method == 'POST':
form = RegistrationFormCustom(initial=initial, data=request.POST)
if form.is_valid():
profile = form.do_save()
profile = auth.authenticate(username = profile.user.email, password = form.cleaned_data.get('pass1'))
print(profile)
auth.login(request, profile)
return redirect('/')
else:
pass
return render_jinja(request, 'registration/registration_form.html',
type="register",
form = form
)
and i will post any other snipped required happily
You're getting the 'RegistrationProfile' object has no attribute 'backend' error because the user is not yet authenticated. To log someone in, you have to call the authenticate method first, which requires a password. So, what you can do instead, is this:
from django.contrib.auth import load_backend, login, logout
from django.conf import settings
def _login_user(request, user):
"""
Log in a user without requiring credentials (using ``login`` from
``django.contrib.auth``, first finding a matching backend).
"""
if not hasattr(user, 'backend'):
for backend in settings.AUTHENTICATION_BACKENDS:
if user == load_backend(backend).get_user(user.pk):
user.backend = backend
break
if hasattr(user, 'backend'):
return login(request, user)
Then, to log someone in, just call the _login_user function with the request and User model. (This will be profile.user in your case, probably) Do this instead of calling auth.login. I'm not sure on how you're going to determine whether this is a valid user or not, without a password or username, but I'll leave that to you. If you still have trouble, let me know.
Short Explanation:
What basically happens here is that Django requires a user to be authenticated in order to be logged in via the login function. That authentication is usually done by the authenticate function, which requires a username and password, and checks whether the supplied password matches the hashed version in the database. If it does, it adds an authentication backend to the User model.
So, since you don't have a password and username, you just have to write your own method for adding the authentication backend to the User model. And that's what my _login_user) function does - if the user is already authenticated, it just calls login, otherwise, it first adds the default backend to the User model, without checking for a correct username and password (like authenticate does).
For others reading this thread, I got a similar error message when I was using User.objects.create() instead of User.objects.create_user(). Basically, the first method was setting a clear password whereas create_user encrypts the password. Clear passwords will fail to authenticate. Check your database, if you have passwords set in the clear, then it's likely you need to use create_user() instead.
The author's request could be fixed by simply setting a default user and password using create_user() instead of just user.save().
You can create a known password (put it in settings.py ) and use that as though the user entered it. Create the user with this and authenticate the user with this.
Basically, I currently have login/ in urls.py redirect to the django.contrib.auth.views.login and that seems to work out fine.
However I'm porting over passwords from a legacy mysql/php site and I believe I should just create a new model profile per http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users. This new model/table will have the old md5 password column, and I'll port over the usernames to the main users table.
How could I change the login method such that I first check if the user has a password in the auth_user table, and if not then md5 the POST password field and attempt to match it to my new profile password column, if so save the password in the new auth_user table by SHA1 encrypting like how the admin does it?
I would create a new view that does something along the following:
from django.contrib.auth.models import User, check_password
import hashlib
def login_with_lookup(request):
if request.POST: # If you want to restrict to POST
username = request.POST['username']
password = request.POST['password']
user = User.objects.get(username=username)
profile = user.get_profile()
if profile.old_password != '' and profile.old_password == hashlib.md5(password).hexdigest():
user.set_password(password)
profile.old_password = ''
user.save() # Might need to save profile as well, not sure how it works
if check_password(password, user.password):
login(request, user)
#Whatever else you want to do, followed by a render to template or redirect
This is un-tested, so it will need a bit of clean-up. It will also need error checking at different points to handle failure cases (this example assumes success).
Write a custom authentication backend:
http://docs.djangoproject.com/en/1.1/topics/auth/#writing-an-authentication-backend