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 working on openedx(works on django) and i need to create an api to register the user coming from a particular site, i am being given a hashed password not a normal one and i need to save it as so.
The problem here is that the openedx's registration function hashes the password that is being passed into it.
so is there a way in django to store a password/register a user without hashing the password.
Should i go for updating the user's credentials directly using
raw()
any help would be appreciated, thanks.
I would suggest to override method set set_password in user_model.
class MyUser(AbstractBaseUser):
# if you need to hash passwords for some users.
is_password_hashed = models.BooleanField(default=True)
...
def set_password(self, raw_password):
if self.is_password_hashed:
super(MyUser, self).set_password(raw_password)
else:
self.password = raw_password
If you want to store only non-hashed passwords:
class MyUser(AbstractBaseUser):
...
def set_password(self, raw_password):
self.password = raw_password
Or even override default user model set_password method.
It's as simple as:
from django.contrib.auth.models import User
User.objects.filter(username="myuser").update(password=hashed_password)
(remember passwords are stored as hashed values in the database)
The Open edX manage_user management command was recently updated to support this use case when creating a new user.
Example:
./manage.py lms --settings=devstack manage_user jane jane#example.com --initial-password-hash 'pbkdf2_sha256$20000$mRxYkenyBiH6$yIk8aZYmWisW2voX5qP+cAr+i7R/IrZoohGsRK2fy4E='
However, that command requires a very recent version of Open edX and it will not have any effect if the user account already exists.
As an alternative, you could set up SSO between the external app and Open edX using OAuth2, in which case there's no need for Open edX to store any password at all.
Well, this is a bit complicated, but let me explain myself. I want to create a RESTful service. This service will contain users, but not in a classic way.
I want to create users based on random hashes, I will use uuid to do that. And the most important thing is that I will not need username, password, email or full_name (?). This type of user will authenticate via a GET parameter on a view, only using its username, not anything else.
I read some articles on extending Django user, yet I couldn't find satisfying explanation especially for this case.
Further Explanations
Now, I can hear questions like "Why would anyone ever need especially passwordless User model, and especially thinking that it is quite insecure.". So, this part is especially for the ones who needs a logical explanation to understand such a request.
In service, I want to have three group of users:
anonymous users: the ones who do request some data on server
uuid users: the ones who have a unique id. Why do I need this type? Because I want to track those users' requests and response special data for them. These kind of users will also be removed if they are inactive for specific several days. I think I can do it by cron jobs.
admin: This is me, reaching admin panel. That is all.
I think this explains enough.
Environment
django 1.9.5
python 3.5.1
Django supports multiple backend authentications. As Luis Masuelli suggested you can extend the model to add more fields. But in your scenario, specifically you want to implement a custom authentication method. I woudl go about treating uuid as username and setting password as None.
So, in my settings.py:
AUTH_USER_MODEL = 'app_name.MyUUIDModel'
# REMOVE ALL PASSWORD Validators
In my app_name/models.py:
from django.contrib.auth.models import BaseUserManager
class MyUUIDManager(BaseUserManager):
def create_user(self, uuid):
user = self.model(
uuid=self.normalize_email(email),
)
user.set_password(None)
user.save(using=self._db)
return user
def create_superuser(self, uuid):
user = self.create_user(uuid)
user.save(using=self._db)
return user
def get_by_natural_key(self, email):
return self.get(**{self.model.USERNAME_FIELD: email})
class MyUUIDModel(AbstractBaseUser):
uuid = models.CharField(max_length=36, unique=True)
USERNAME_FIELD = 'uuid'
objects = UUIDModelManager()
def save(self, *args, **kwargs):
super(MyUUIDModel, self).save(*args, **kwargs)
At this point, if you run createuser or createsuperuser Django command, you may be able to create the user. The next bit is where the authentication needs to be done. You can simply check if the UUID exists in your DB and return true when authenticate() is called from the view.
Add authentication backend in the settings.py file:
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'app_name.auth.MyBackend'
]
Create a file app_name/auth.py with contents SIMILAR to below:
class MyBackend(object):
def authenticate(self, username=None, password=None):
# Check if username i.e. UUID exists
try:
my_uuid = MyUUIDModel.objects.get(uuid=username)
except MyUUIDModel.DoesNotExist:
return None
return my_uuid
More more details refer to: https://docs.djangoproject.com/en/1.9/topics/auth/customizing/
I'm currently building a login for a webapp using Flask-Security (which includes Flask-WTForms, Flask-SQLalchemy and Flask-Login). I've been able to fairly painlessly set up the majority of my login flow, including forgotten password; however I want to make it so that the only way users can be registered is through a page only accessible to the admins. I've managed to configure Roles (Admin, User) and set up the following view:
#app.route('/adminregister')
#roles_accepted('admin')
def adminregister():
return render_template('*')
This creates the portal that is successfully limited to accounts with an Admin role. I'm unsure how to proceed for here however, as Flask-security has no built in means to enable what I'm trying to do.
I've overridden RegisterForm already to enforce password rules through a regexp:
# fixed register form
class ExtendedRegisterForm(RegisterForm):
password = TextField('Password', [validators.Required(), validators.Regexp(r'(?=.*?[0-9])(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[$-/:-?{-~!"^_`\[\]])')])
Basically I want a form, located at /adminregister, that when visited by an admin allows for the entry of an email address, at which point first the user is created in the database with a random and secure password, and then a similar process to a forgotten password happens and a 1 time password code is created to reset the password.
Useful things I've looked at:
Within flask-security/views.py there is the forgotten passsword code:
def forgot_password():
"""View function that handles a forgotten password request."""
form_class = _security.forgot_password_form
if request.json:
form = form_class(MultiDict(request.json))
else:
form = form_class()
if form.validate_on_submit():
send_reset_password_instructions(form.user)
if request.json is None:
do_flash(*get_message('PASSWORD_RESET_REQUEST', email=form.user.email))
if request.json:
return _render_json(form, include_user=False)
return _security.render_template(config_value('FORGOT_PASSWORD_TEMPLATE'),
forgot_password_form=form,
**_ctx('forgot_password'))
Within flask_security/registerable.py there is the code for register_user
def register_user(**kwargs):
confirmation_link, token = None, None
kwargs['password'] = encrypt_password(kwargs['password'])
user = _datastore.create_user(**kwargs)
_datastore.commit()
if _security.confirmable:
confirmation_link, token = generate_confirmation_link(user)
do_flash(*get_message('CONFIRM_REGISTRATION', email=user.email))
user_registered.send(app._get_current_object(),
user=user, confirm_token=token)
if config_value('SEND_REGISTER_EMAIL'):
send_mail(config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome',
user=user, confirmation_link=confirmation_link)
return user
I want to somehow combine these two, so that upon submission of a form with the sole field "Email" at '/adminregister' the email is added with a secure, random password in the database and the email address is sent an email with a link to change there password (and ideally a message explaining). I'm not even sure where I would add such code, as there is nothing to specifically override, especially as I can't find a way to override RegisterForm to have FEWER fields and the same functionality.
The structure of my code is in line with the flask-security documentation's quickstart.
Thank you in advance for any guidance you can offer.
I ended up using a work around as follows:
I enabled registration but limited registration view to users with an admin role.
I used del form.password in views -> register to no longer send the form with a password field.
I did the following in .registerable, generating a random password to fill the table.
kwargs['password'] = encrypt_password(os.urandom(24))
Upon admin entry of an email in the registration form, I had confimable enabled. This means the user would immediatly get an email to confirm their account and explaining they'd been registered. Upon confirmation they are redirected to the forgotten password page and asked to change their password (which is limited based on security).
If anyone comes up with a more direct way I'd appreciate it. I'm leaving this here in case anyone has the same problem.
The register process creates a signal with blinker that you can access like this:
from flask.ext.security.signals import user_registered
#user_registered.connect_via(app)
def user_registered_sighandler(app, user, confirm_token):
user_datastore.deactivate_user(user)
db.session.commit()
Which will deactivate any newly registered users.
I know this is an ancient question, but I think I have an elegant answer.
first import register_user
from flask_security.registerable import register_user
Then since you do not want just anyone to register ensure registerable is disabled (though disabled is the default so you can omit this) and since you want to send confirmation email, enable confirmable, and changeable for users to change their paswords
app.config['SECURITY_CONFIRMABLE'] = True
app.config['SECURITY_REGISTERABLE'] = False
app.config['SECURITY_RECOVERABLE'] = True
Then, you can do your create your user registration view and decorate it with role required. I have used my own custom registration form so I have had to go an extra mile to check if user already exists and return an error accourdingly
#app.route('/admin/create/user', methods=['GET', 'POST'])
#roles_required('admin')
def admin_create_user():
form = RegistrationForm(request.form)
if request.method == 'POST' and form.validate_on_submit():
email = form.email.data
password = form.password.data
user_exists = session.query(User).filter_by(email=email).first()
if user_exists:
form.email.errors.append(email + ' is already associated with another user')
form.email.data = email
email = ''
return render_template('create-user.html', form = form)
else:
register_user(
email=email,
password = password)
flash('User added successfully')
return render_template('create-user.html', form = form)
Also see flask-security - admin create user, force user to choose password
Here's another solution I found after poking through flask-security-too. I made an admin create user form, and simply add the following code after creating the user in the database:
from flask_security.recoverable import send_reset_password_instructions
# my code is maintains self.created_id after creating the user record
# this is due to some complex class involved which handles my crudapi stuff
# your code may vary
user = User.query.filter_by(id=self.created_id).one()
send_reset_password_instructions(user)
I did not find anything on the web and so I'm asking here.
Is there a way to create a custom auth wich only requires a username? That means to login to a specific subpage one has only to enter a username, no email and no password etc.?
Or is there a better way to do this? E.g. a subpage can only be accessed if the username (or similar) exists in a db table?
Yes you could do something like this:
(in models file, or at the top of the controller(s), or even better make a function decorator)
Check session.logged_in_user to see if it's None, if None, redirect to /default/login where you present the user with a form:
form = FORM(Field('username'), requires=IS_IN_DB(db, db.users.username))
On form submission (see web2py manual for form processing), if valid (e.g. if username exists in db.users table), set session.logged_in_user = request.vars.username
Here's a completish example (untested):
models/Auth.py
# Could also check whether session.logged_in_user exists in DB, but probably not needed
# If so though, should be renamed zAuth or something to come after db.py file
if not session or not session.logged_in_user:
redirect(URL('default','login', vars={'next':request.vars.url}))
controllers/default.py
#in file: controllers/default.py
...
def login():
form = FORM(Field('username', requires=IS_IN_DB(db, db.users.username))
if form.process().accepted:
session.logged_in_user = form.vars.username
redirect(request.vars.next)
elif form.errors:
session.logged_in_user = None # not necessary, but oh well
response.flash = "Please enter a valid username"
return dict(form=form)
views/default/login.html
{{ extend 'layout.html' }}
{{ =form }}
By placing code in a models file, you can ensure it is executed on every page request.
This will not allow you to use web2py's authentication mechanism (i.e. auth = AUTH()), but I'm not sure that you'd want it for this anyway unless you're interested in using groups and permissions, etc. But if that's the case, adding passwords (even if it's a generic password or a blank one) seems like it wouldn't be too much trouble.
web2py by default allows blank passwords. So simply hide the password fields in the login and registration forms using CSS. You should be able to use the default auth.
if you want to sign-in with only your unique username and password go to db.py and write this code:
auth.define_tables(username=True,signature=True)
db.auth_user.username.requires = IS_NOT_IN_DB(db, 'auth_user.username')
db.auth_user.email.readable = False
db.auth_user.email.writable = False
db.auth_user.first_name.readable = False
db.auth_user.first_name.writable = False
db.auth_user.last_name.readable = False
db.auth_user.last_name.writable = False
for me it worked
I use the backend solution from django. I just want to get a username from the cookie or the session_key to get to know the user. How I can do it?
from django.contrib.auth.models import User
from django.contrib.sessions.models import Session
def start(request, template_name="registration/my_account.html"):
user_id = request.session.get('session_key')
if user_id:
name = request.user.username
return render_to_response(template_name, locals())
else:
return render_to_response('account/noauth.html')
Only else is coming up. What am I doing wrong?
Am I right then that authenticated means he is logged in?
--> Okay this I got!
Firstly, if you have some clarification to a question, update the question, don't post an answer or (even worse) another question, as you have done. Secondly, if the user is logged out, by definition he doesn't have a username.
I mean the advantage of Cookies is to identify a user again. I just want to place his name on the webpage. Even if he is logged out. Or isnt't it possible?
You can check if a user is authenticated by calling the, apptly named, is_authenticated method. Your code would then look somewhat like this:
def start(request, template_name="registration/my_account.html"):
if request.user.is_authenticated():
name = request.user.username
return render_to_response(template_name, locals())
else:
return render_to_response('account/noauth.html')
No need to access the session yourself, Django handles all of that automatically (provided you use both django.contrib.sessions and django.contrib.auth).
/edit: in order to have a user's username, he needs to be authenticated. There's no good way around that.
piquadrat has absolutely the right answer, but if for some reason you do need to get the user from the session, you call get_decoded() on the session object:
session_data = request.session.get_decoded()
user_id = session_data['_auth_user_id']
You need to enable the AuthenticationMiddleware and SessionMiddleware in your MIDDLEWARE_CLASSES setting in your settings.py to access request.user in your views.
http://docs.djangoproject.com/en/1.2/topics/auth/#authentication-in-web-requests
You can then access the username using request.user.username