Assign user role on signup flask-security-too

226 Views Asked by At

I'm new to flask so bear with me. I'm creating a sign-up flow via flask-security-too. How can I assign a user role on sign-up? I'm following the second (SQLAcademy + session) example from here. I have tried to update

class ExtendedRegisterForm(RegisterForm):
    first_name = StringField('First Name', [DataRequired()])
    last_name = StringField('Last Name', [DataRequired()])
    roles = SelectField('Roles', choices=["user"]) #<- updated here

and also created a new "register_user.html" which adds a new field for role "user":

{% from "security/_macros.html" import render_field_with_errors, render_field %}
{% include "security/_messages.html" %}
<h1>Registrer</h1>
<form action="{{ url_for_security('register') }}" method="POST" name="register_user_form">
  {{ register_user_form.hidden_tag() }}
  {{ render_field_with_errors(register_user_form.email) }}
  {{ render_field_with_errors(register_user_form.password) }}
  {% if register_user_form.password_confirm %}
    {{ render_field_with_errors(register_user_form.password_confirm) }}
  {% endif %}
  {{ render_field_with_errors(register_user_form.first_name) }}
  {{ render_field_with_errors(register_user_form.last_name) }}
  {{ render_field_with_errors(register_user_form.roles) }} <!-- updated mainly here -->

  {{ render_field(register_user_form.submit) }}
</form>
{% include "security/_menu.html" %}

Models and database looks like as specified in the example above. I have also set

app.config["SECURITY_REGISTERABLE"] = True
app.config["SECURITY_RECOVERABLE"] = True

And set-up e-mail. However, when I run this, I get this error:

Traceback (most recent call last):
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask/app.py", line 2213, in __call__
    return self.wsgi_app(environ, start_response)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask/app.py", line 2193, in wsgi_app
    response = self.handle_exception(e)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask/app.py", line 2190, in wsgi_app
    response = self.full_dispatch_request()
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask/app.py", line 1486, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask/app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask/app.py", line 1469, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask_security/decorators.py", line 632, in wrapper
    return f(*args, **kwargs)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask_security/views.py", line 299, in register
    user = register_user(form)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask_security/registerable.py", line 46, in register_user
    user = _datastore.create_user(**user_model_kwargs)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask_security/datastore.py", line 470, in create_user
    kwargs = self._prepare_create_user_args(**kwargs)
  File "/opt/homebrew/Caskroom/miniforge/base/envs/flask_security_too/lib/python3.10/site-packages/flask_security/datastore.py", line 214, in _prepare_create_user_args
    roles[i] = self.find_role(rn)
TypeError: 'str' object does not support item assignment

And the role "user" is not assigned. Why?

2

There are 2 best solutions below

0
jwag On

If I read your question correctly - you want to administratively add the “user” role to all newly registered users. If so - don’t add things to the form - since that is really meant for user input. For this use case - register a signal handler. See the docs at: https://flask-security-too.readthedocs.io/en/stable/api.html#signals

You want the user_registered signal - that will pass you the completely filled in user model - which you can then use userdatastore.add_role_to_user(user, “user”)

0
Mominur Rahman On

You can use user_registered signal to assign role to a user as suggested @jwag

This is how I used

from flask_security.signals import user_registered

USERS_ROLES  = {
    'USER': {
        'name': 'User',
        'permissions': { 'user-read', 'user-write' }
    },
    'ADMIN': {
        'name': 'Admin',
        'permissions': { 'admin-read', 'admin-write' }
    },
}


user_datastore = SQLAlchemyUserDatastore(db, User, Role)
app.security = Security(app, user_datastore)

@user_registered.connect_via(app)
def user_registered_sighandler(sender, user, **extra):
    default_role = app.security.datastore.find_or_create_role(
        name=Config.USERS_ROLES['USER']['name'], permissions=Config.USERS_ROLES['USER']['permissions']
    )
    user_datastore.add_role_to_user(user, default_role)