Flask-Security user_registered Signal Not Received in Python 3.3, but works in 2.7
Asked Answered
A

2

12

I'm trying to use the user_registered signal in order to set up default roles for users when they register using flask-security as in the following link: Setting Default Role in Flask Security

In my searches I can see that there was a bug that was already addressed for this in flask-security: Not getting signal from flask-security, Fix - user_registered signal problem

I've tried the following to prove if the signal is received by the handler without any luck:

@user_registered.connect_via(app)
def user_registered_sighandler(sender, **extra):
    print("print-user_registered_sighandler:", extra)

This, however, never gets called even though the user gets registered and the signal should be sent.

If it helps I've set the flask-security configuration as follows:

app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_CONFIRMABLE'] = False
app.config['SECURITY_SEND_REGISTER_EMAIL'] = False
app.config['SECURITY_CHANGEABLE'] = True
app.config['SECURITY_SEND_PASSWORD_CHANGE_EMAIL'] = False

Signals from Flask-Login and Flask-Principal are working for me as I managed to confirm that the following code snippets successfully print when the signals are sent:

@user_logged_out.connect_via(app)
def on_user_logged_out(sender, user):
    print('USER LOG OUT: made it in',user)

@identity_changed.connect_via(app)
def identity_changed_ok(sender,identity):
    print('Identity changed:',identity)

For my setup I am using python 3.3 (anaconda) and using the following: Flask==0.10.1,flask-login==0.2.11,flask-principal==0.4.0,flask-security==1.7.4,blinker==1.3. Having looked at the signals in both flask-login and flask-security I'm not sure why the flask-security signals would not be working.

EDIT:

If I add print(user_registered.receivers) to a route in my app it will show that I have a receiver: {139923381372400: <function user_registered_sighandler at 0x7f42737145f0>}. If I put this same print statement within the registerable.py of flask-security just before the user_registered.send(app._get_current_object(),user=user, confirm_token=token) then it lists no receivers: {}

EDIT2:

Problem appears to be related to using python 3.3. I created a python 2.7 environment and the user_registered code worked as expected.

Full code to reproduce:

from flask import Flask,render_template
from playhouse.flask_utils import FlaskDB
import os
from flask.ext.security import Security, PeeweeUserDatastore
from flask.ext.security.signals import user_registered
from flask.ext.login import user_logged_out
from peewee import *
from playhouse.signals import Model
from flask.ext.security import UserMixin,RoleMixin

app = Flask(__name__)

app.config['ADMIN_PASSWORD']='secret'
app.config['APP_DIR']=os.path.dirname(os.path.realpath(__file__))
app.config['DATABASE']='sqliteext:///%s' % os.path.join(app.config['APP_DIR'], 'blog.db')
app.config['SECRET_KEY'] = 'shhh, secret!'
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_CONFIRMABLE'] = False
app.config['SECURITY_SEND_REGISTER_EMAIL'] = False

flask_db = FlaskDB()
flask_db.init_app(app)
database = flask_db.database

class BaseModel(Model):
    class Meta:
        database=flask_db.database


class User(BaseModel, UserMixin):
    email=CharField()
    password=CharField()
    active = BooleanField(default=True)
    confirmed_at = DateTimeField(null=True)

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def is_authenticated(self):
        return True


class Role(BaseModel, RoleMixin):
    name = CharField(unique=True)
    description = TextField(null=True)


class UserRoles(BaseModel):
    user = ForeignKeyField(User, related_name='roles')
    role = ForeignKeyField(Role, related_name='users')
    name = property(lambda self: self.role.name)
    description = property(lambda self: self.role.description)


user_datastore = PeeweeUserDatastore(database, User, Role, UserRoles)
security = Security(app, user_datastore)

@user_registered.connect_via(app)
def user_registered_sighandler(sender,**extra):
    print("print-user_registered_sighandler")

@user_logged_out.connect_via(app)
def on_user_logged_out(sender, user):
    print('USER LOG OUT: made it in',user)

@app.route('/')
def index():
    print(user_registered.receivers)
    return render_template('base.html')

database.create_tables([User,Role,UserRoles], safe=True)
app.run(debug=True)

base.html template:

<!doctype html>
<html>
  <head>
    <title>Blog</title>
  </head>
  <body>
          <ul>
            {% if current_user.is_authenticated() %}
              <li><a href="{{ url_for('security.logout',next='/') }}">Log out</a></li>
              <li><a href="{{ url_for('security.register') }}">Register</a></li>
        {% else %}
        <li><a href="{{ url_for('security.login',next='/') }}">Login</a></li>
        <li><a href="{{ url_for('security.register') }}">Register</a></li>
            {% endif %}
            {% block extra_header %}{% endblock %}
          </ul>
  </body>
</html>
Aldarcy answered 19/6, 2015 at 18:52 Comment(0)
A
0

If the import for user_registered is changed to the following:

from flask.ext.security import user_registered

then the signal will work as expected in python 3.3 and call the user_registered_sighandler function when a user is registered.

The user_registered signal is imported within the __init__.py file for flask-security so it is also available at the top level of the package.

Aldarcy answered 8/9, 2015 at 13:23 Comment(1)
Update: it's now from flask_security import user_registeredCorinecorinna
A
1

I was able to do this with something like:

security = Security(app, user_datastore,
         register_form=ExtendedRegisterForm)


@user_registered.connect_via(app)
def user_registered_sighandler(app, user, confirm_token):
    default_role = user_datastore.find_role("Pending")
    user_datastore.add_role_to_user(user, default_role)
    db.session.commit()
Anatol answered 5/9, 2015 at 1:32 Comment(3)
Thanks for the answer @BrandonRose. This does not solve my problem, however. What version of python and the associated modules are you using? Even if I add an extended register form and use the function parameters you have used my function does not get called.Aldarcy
2.7, though looking back over it, I'm actually not seeing in your code where you define the default role. If it's working in 2.7 can you use 2.7 to run the app? If you use supervisord you can specify your pythonpath for just the app, even if you're running python 3 for other tasks.Anatol
Defining the default role was not my issue. Issue was/is that the signal was never received. I've been using the peewee playhouse signals as a workaround.Aldarcy
A
0

If the import for user_registered is changed to the following:

from flask.ext.security import user_registered

then the signal will work as expected in python 3.3 and call the user_registered_sighandler function when a user is registered.

The user_registered signal is imported within the __init__.py file for flask-security so it is also available at the top level of the package.

Aldarcy answered 8/9, 2015 at 13:23 Comment(1)
Update: it's now from flask_security import user_registeredCorinecorinna

© 2022 - 2024 — McMap. All rights reserved.