Admin(only) registration of users, Flask-Security

家住魔仙堡 提交于 2020-01-13 13:31:13

问题


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:

  1. 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'))
    
  2. 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.


回答1:


I ended up using a work around as follows:

  1. I enabled registration but limited registration view to users with an admin role.

  2. I used del form.password in views -> register to no longer send the form with a password field.

  3. I did the following in .registerable, generating a random password to fill the table.

    kwargs['password'] = encrypt_password(os.urandom(24))

  4. 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.




回答2:


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.




回答3:


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)



回答4:


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)


来源:https://stackoverflow.com/questions/31114392/adminonly-registration-of-users-flask-security

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!