Flask App not calling Babel localeselector - python

I am adding localisation to my Flask app with Flask-Babel, and I cannot seem to change the language. I followed all the Babel instructions for creating the po/mo files - I don't believe my issue is related to the translation data... its that I don't know how to change languages... which seems like it would/should be obvious.
My debugging shows that babel.localeselector is not being called. My implementation calls refresh(), which I think should call babel.localeselector (somehow... because I don't see how it works, as the refresh() command seems to just delete some keys from the app context... I don't know how that triggers the call to babel.localeselector)
app = Flask(__name__)
babel = Babel(app)
#babel.localeselector
def get_locale():
# if a user is logged in, use the locale from the user settings
user = User.get_by_id(session['user_id'])
if user is not None: return user.lang
return 'en'
and, when the user logs in, this function is called,
from flask_babel import refresh
def login(user_id):
# Gets called when user successfully logs in
refresh()
I have confirmed refresh() is being called.
But the language is not changed.
UPDATED:
Also tried this,
from flask import current_app
from common.models import User
from flask_babel import refresh
def login(user_id):
# Gets called when user successfully logs in
user = User.get_by_id(user_id)
current_app.config['BABEL_DEFAULT_LOCALE'] = user.lang
refresh()

Figured out the problem. I was using,
import gettext
_ = gettext.gettext
instead of,
from flask_babel import gettext as _

Related

Is form/login still required when using FlaskLDAP3Login for Single Sign-On purpose?

Simply saying, I am developing an app using Flask. For this app I am trying to implement a Single Sign-On, so a user never needs to enter credentials, e.g. username and password.
The authentication and authorization in this case will go through Kerberos together with LDAPS. The Kerberos part is not done yet, however, Kerberos is intended to get a "username" via a middleware currently logged into a system (Windows), when requesting an app's link. Afterwards this variable i.e. "username" will be proceeded with LDAPS, to check whether a user belongs to Active Directory or not. If yes - provide access and permission to a web site, if no - forbid.
However, since my user wont type anything, I do not understand whether I need to use either the Flask Form (flask-wtf) or the Flask Login (flask-login e.g. UserMixin) as well as how shall I provide an access to my user?
I was able to set up the FlaskLDAP3Login in 'config.py' and than run the '__init__.py'
from flask import Flask
from config import Config
from flask_login import LoginManager
from flask_ldap3_login import LDAP3LoginManager
app = Flask(__name__)
app.config.from_object(Config)
login_manager = LoginManager(app) # Setup a Flask-Login Manager
ldap_manager = LDAP3LoginManager(app) # Setup a LDAP3 Login Manager
from app import routes
than I got the following exception:
Exception: Missing user_loader or request_loader. Refer to
http://flask-login.readthedocs.io/#how-it-works for more info.
than I found this answer, but using this decorator #login_manager.user_loader is probably not enough, is not it?
My assumption is to create a decorator, similar to this one that will allow/forbid an access to a user:
import getpass
from flask import Flask, request, make_response
from functools import wraps
def auth_required(f):
#wraps(f)
def decorated(*args, **kwargs):
current_user = getpass.getuser() # current_user will be later acquired via Kerberos
auth = ldap_manager.get_user_info_for_username(current_user)
if auth:
return f(*args, **kwargs)
return make_response('Could not verify your login!', 401, {'WWW-Authenticate': 'Basic realm="You are not our user!"'})
return decorated
Also, I cannot find a similar or even related thread e.g.
github/flask-ldap3-login/flask_ldap3_login/forms.py
Authenticate with Flask-LDAP3-Login based on group membership
Flask Authentication With LDAP
Integrate LDAP Authentication with Flask
The Flask Mega-Tutorial Part V: User Logins

Flask-Babel localeselector is not being called

Flask-babel doesn't call its localeselector even once. I'm using app factory to init my app. The translations folder is within my app, It was created by babel, according to the docs. I've also tried moving the translations dir to the folder containing the run.py, which calls the factory, but to no effect.
from flask import Flask, session, request
from myapp.text_fields import next_month
from myapp.config import Config
from flask_babel import Babel
babel = Babel()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
babel.init_app(app)
from myapp.views import user
from myapp.errors import errors
app.register_blueprint(user)
app.register_blueprint(errors)
#babel.localeselector
def get_locale():
try:
language = session['language']
except KeyError:
language = None
if language is not None:
return language
return request.accept_languages.best_match(app.config['LANGUAGES'])
#app.context_processor
def inject_conf_var():
return dict(
MONTH=next_month(),
AVAILABLE_LANGUAGES=app.config['LANGUAGES'],
CURRENT_LANGUAGE=session.get('language',
request.accept_languages.best_match(app.config['LANGUAGES'])))
return app
Here's the config part concerning babel:
BABEL_DEFAULT_LOCALE = 'pl'
LANGUAGES = ['pl', 'ua', 'ru', 'en']
So far I've complied only EN, tried to change the default, but it doens't do anything either. Seems like babel is not able to locate the translations folder, I'm not sure how to fix this.
Solved it, turned out I had to move localeselector out of the factory. Since most of my text fields are generated on the service side rather than forntend, I also had to use lazy_text on all of them, as they are called by endpoints rather than defined within them.

Signout after user disconnects on Flask

For those of you who have used flask-login will know the decorator fresh_login_required. I am trying to implement something similar by myself.
It doesn't have to be a decorator. I just want to be able to do some works after the user has been totally disconnected (example: closed the browser).
And is it a good idea to have a global integer variable to count the total active/online users?
Thanks in advance :)
Flask sessions are handled by flask session interface. Flask-Login just check if the current session is fresh.
from flask import session
def login_fresh():
'''
This returns ``True`` if the current login is fresh.
'''
return session.get('_fresh', False)
def login_user():
'''
Your login code here
'''
session['_fresh'] = True
def logout_user():
'''
Your logout code here
'''
if '_fresh' in session:
session.pop('_fresh')
The code snippet above is from Flask-Login source code,that can be accessed here. If you needs the user to login again, just check if his login is fresh.
You should be able to do it with:
#app.before_request
def make_session_permanent():
session.permanent = False
From the docs:
http://flask.pocoo.org/docs/0.11/api/#flask.session.permanent

login_required decorator doesnt work, flask-Login permits anonymous users

I decorated a method with login_required, but I am surprised that its not executing at all, allowing in anonymous users. Printing the current_user within the method returns this:
<flask_login.AnonymousUserMixin object at 0xb67dbd4c>
Is it not supposed to reject users which return false in user.is_autheticated()? What did I do wrong?
I have setup FL this way:
lm = LoginManager(app)
lm.login_view = 'root'
in views.py:
#lm.user_loader
def load_user(id):
return User.query.get(int(id))
the actual view:
#login_required
#app.route("/messages")
def messages():
print "current user", current_user
return "hello world"
Serendipity gave me this:
When applying further decorators, always remember that the route() decorator is the outermost:
I wrote it the wrong way (route not the outermost).
PDB can execute your suspect method in debug mode, to inspect the local state.
Flask-Login is present in GitHub anyway and the source of login_required is simple enough to understand.
Everything looks OK, which probably means the problem is somewhere else.
What is the configuration you are using? If LOGIN_DISABLED or TESTING is set to true, authentication is disabled.
If your configuration is fine, set a breakpoint inside login_required decorator and check why it lets anonymous user in.

Testing Flask login and authentication?

I'm developing a Flask application and using Flask-security for user authentication (which in turn uses Flask-login underneath).
I have a route which requires authentication, /user. I'm trying to write a unit test which tests that, for an authenticated user, this returns the appropriate response.
In my unittest I'm creating a user and logging as that user like so:
from unittest import TestCase
from app import app, db
from models import User
from flask_security.utils import login_user
class UserTest(TestCase):
def setUp(self):
self.app = app
self.client = self.app.test_client()
self._ctx = self.app.test_request_context()
self._ctx.push()
db.create_all()
def tearDown(self):
if self._ctx is not None:
self._ctx.pop()
db.session.remove()
db.drop_all()
def test_user_authentication():
# (the test case is within a test request context)
user = User(active=True)
db.session.add(user)
db.session.commit()
login_user(user)
# current_user here is the user
print(current_user)
# current_user within this request is an anonymous user
r = test_client.get('/user')
Within the test current_user returns the correct user. However, the requested view always returns an AnonymousUser as the current_user.
The /user route is defined as:
class CurrentUser(Resource):
def get(self):
return current_user # returns an AnonymousUser
I'm fairly certain I'm just not fully understanding how testing Flask request contexts work. I've read this Flask Request Context documentation a bunch but am still not understanding how to approach this particular unit test.
The problem is different request contexts.
In your normal Flask application, each request creates a new context which will be reused through the whole chain until creating the final response and sending it back to the browser.
When you create and run Flask tests and execute a request (e.g. self.client.post(...)) the context is discarded after receiving the response. Therefore, the current_user is always an AnonymousUser.
To fix this, we have to tell Flask to reuse the same context for the whole test. You can do that by simply wrapping your code with:
with self.client:
You can read more about this topic in the following wonderful article:
https://realpython.com/blog/python/python-web-applications-with-flask-part-iii/
Example
Before:
def test_that_something_works():
response = self.client.post('login', { username: 'James', password: '007' })
# this will fail, because current_user is an AnonymousUser
assertEquals(current_user.username, 'James')
After:
def test_that_something_works():
with self.client:
response = self.client.post('login', { username: 'James', password: '007' })
# success
assertEquals(current_user.username, 'James')
The problem is that the test_client.get() call causes a new request context to be pushed, so the one you pushed in your the setUp() method of your test case is not the one that the /user handler sees.
I think the approach shown in the Logging In and Out and Test Adding Messages sections of the documentation is the best approach for testing logins. The idea is to send the login request through the application, like a regular client would. This will take care of registering the logged in user in the user session of the test client.
I didn't much like the other solution shown, mainly because you have to keep your password in a unit test file (and I'm using Flask-LDAP-Login, so it's nonobvious to add a dummy user, etc.), so I hacked around it:
In the place where I set up my test app, I added:
#app.route('/auto_login')
def auto_login():
user = ( models.User
.query
.filter_by(username="Test User")
.first() )
login_user(user, remember=True)
return "ok"
However, I am making quite a lot of changes to the test instance of the flask app, like using a different DB, where I construct it, so adding a route doesn't make the code noticeably messier. Obv this route doesn't exist in the real app.
Then I do:
def login(self):
response = self.app.test_client.get("/auto_login")
Anything done after that with test_client should be logged in.
From the docs: https://flask-login.readthedocs.io/en/latest/
It can be convenient to globally turn off authentication when unit testing. To enable this, if the application configuration variable LOGIN_DISABLED is set to True, this decorator will be ignored.

Categories

Resources