I am using Flask App builder to make a basic webpage.
I want to change the default landing page based on the user logged in
e.g. user1 should be redirected to /home/user1 page and user2 should login to /home/general page etc after they logged in.
Below is my custom index view
class MyIndexView(IndexView):
index_template = 'index.html'
#expose('/')
def main(self):
return redirect(url_for('AuthDBView.login'))
#expose('/index')
def index(self):
return self.render_template('index.html', message="Welcome to my website")
and starting the app by calling
appbuilder = AppBuilder(app, db.session, indexview=MyIndexView)
I have not seen any example or documentation on how to achieve this. so appreciate any help
First off all, Flask-AppBuilder depends on Flask-login to manage users so you might want to read its documentation.
Besides that, Flask-AppBuilder injects the current_user(authenticated or anonymous) in Flask's g variable before each request, so all you have to do is get the user from g variable and do what you want with it.
Below is an example of an IndexView that redirects anonymous users(not logged in) to the login page.
If the user is not anonynous and its name is John, it is redirected to the HomeView.user endpoint.
If its name is not John, it is redirected to the HomeView.general endpoint.
index.py
from flask import g, url_for, redirect
from flask_appbuilder import IndexView, expose
class MyIndexView(IndexView):
#expose('/')
def index(self):
user = g.user
if user.is_anonymous:
return redirect(url_for('AuthDBView.login'))
else:
if user.first_name == 'John':
return redirect(url_for('HomeView.user'))
else:
return redirect(url_for('HomeView.general'))
Inside views.py
class HomeView(BaseView):
route_base = "/home"
#expose('/user/')
def user(self):
greeting = "Hello John"
return self.render_template('logged_user.html', greeting=greeting)
# expose('/general/')
def general(self):
greeting = "Hello ordinary user"
return self.render_template('logged_user.html', greeting=greeting)
appbuilder.add_view_no_menu(HomeView())
Related
I am using Flask-Login for the login handling. In addition I wrote myself a decorator to keep views visible just for role=admin.
def admin_required(f):
#wraps(f)
def wrap(*args, **kwargs):
if current_user.role.name == "admin":
return f(*args, **kwargs)
else:
flash("You need to be an admin to view this page.")
return redirect(url_for('home'))
return wrap
I can use that decorator for my routes. But I don't know how to use this decorator for my flask-admin panel.
The Flask-SQLAlchemy Models were added to the flask-admin view like:
from flask_admin.contrib.sqla import ModelView
from flask_admin import Admin
admin = Admin(app, name='admin', template_mode='bootstrap3')
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Role, db.session))
But I want if someone who is not an admin hits: 127.0.0.1:8000/admin do not get access and gets redirected to /home.
But I have no route for my /admin and when I implement one I do not know what html I should return?
Thank you
Use the built-in method is_accessible, by deafult, it returns True, which means that admin panel will be accessible by all, you can change it by overriding it like this:
class MyAdminViews(ModelView):
def is_accessible(self):
user = User.query.filter_by(role=current_user.role).first()
res = user.role == "admin"
return res
admin.add_view((MyAdminViews(User, db.session)))
admin.add_view((MyAdminViews(Post, db.session)))
Now, these admin views will be accessible if and only if the user possesses an admin role. Also, you might want to add a clause to make it accessible only when someone is logged in, else the browser will throw an internal server error. You can do it like this:
class MyAdminViews(ModelView):
def is_accessible(self):
if current_user.is_authenticated:
user = User.query.filter_by(role=current_user.role).first()
res = user.role == "admin"
return res
I am using flask-mwoauth to create a simple application in Flask using OAuth authentication on Mediawiki (and Wikipedia in particular).
flask-mwoauth is a blueprint that provides some convenience methods to interact with Mediawiki Extensions:OAuth and adds the following URIs:
/login - runs the OAuth handshake and returns the user to /
/login?next=/someurl will return the user to /someurl
/logout - clears the users' access tokens
/logout?next=/someurl will return the user to /someurl
/oauth-callback - callback from MW to finish the handshake
The users' OAuth key and secret are stored in the session.
I would like to be able to create custom responses for some of this custom URIs. Take for example /logout, the definition of the response in very simple (__init__.py#L56):
#self.bp.route('/logout')
def logout():
session['mwo_token'] = None
session['username'] = None
if 'next' in request.args:
return redirect(request.args['next'])
return "Logged out!"
I would like to define in my application the route /logout with a custom response (for example, rendering a template), however if I use the blueprint then the route #app.route("/logout") is ignored.
What I would like to know if it is possible to "extend" the blueprint in the sense that I can define a route /logout in my app, call the original method from the blueprint and then serve a customized response.
If you want to completely redefine behavior of route the best way is override MWOAuth class. Here an example which works:
import os
from flask import Flask, Blueprint
from flask_mwoauth import MWOAuth
app = Flask(__name__)
app.secret_key = os.urandom(24)
class MyMWOAuth(MWOAuth):
def __init__(self,
base_url='https://www.mediawiki.org/w',
clean_url="Deprecated",
default_return_to='index',
consumer_key=None,
consumer_secret=None,
name="Deprecated"):
# I skipped other rows. It's just an example
self.bp = Blueprint('mwoauth', __name__)
# By the way you can customize here login and oauth-callback
#self.bp.route('/logout')
def logout():
# your custom logic here...
return "My custom logout"
mwoauth = MyMWOAuth(consumer_key='test', consumer_secret='test')
app.register_blueprint(mwoauth.bp)
if __name__ == "__main__":
app.run(debug=True, threaded=True)
Let's open /logout. You will see My custom logout.
As you can see registration of BluePrint routes takes place in init method of MWOAuth.
The second way is to use request callbacks. Here an example which demonstrates the change in the body of the response after logout.
from flask import g, request
def after_this_request(f):
if not hasattr(g, 'after_request_callbacks'):
g.after_request_callbacks = []
g.after_request_callbacks.append(f)
return f
#app.after_request
def call_after_request_callbacks(r):
for callback in getattr(g, 'after_request_callbacks', ()):
callback(r)
return r
#app.before_request
def before_logout():
#after_this_request
def after_logout(response):
# check if called route == '/logout'
# in our case response.data == 'Logged out!'
# see: https://github.com/valhallasw/flask-mwoauth/blob/master/flask_mwoauth/__init__.py#L48
if request.url_rule.endpoint == 'mwoauth.logout':
# custom logic here...
# for example I change data in request
response.data = 'Data from after_logout'
Let's open /logout. You will see Data from after_logout.
Hope this helps.
Does Flask have any built-in support for user login/logout functionality? I've found this add-on project, but it only seems to provide the pieces to build your own login system. It doesn't seem to be a complete system.
I'm new to Flask, and I'm coming from Django where this is all built-in, so I'm finding it a little baffling that this basic functionality is missing.
Using some incomplete examples I've found, I'm trying to implement an index page that redirects to a login page for anonymous users, and after a successful login, redirects page to the index page. This is what I currently have implemented as a "hello world" login/logout example:
#!/usr/bin/env python
import flask
from flask import Flask, Response, render_template
from flask.ext.login import LoginManager, UserMixin, login_required, login_user, logout_user
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
class User(UserMixin):
# proxy for a database of users
user_database = {
"JohnDoe": ("JohnDoe", "John"),
"JaneDoe": ("JaneDoe", "Jane"),
}
def __init__(self, username, password):
self.id = username
self.password = password
#property
def name(self):
return self.id
#classmethod
def get(cls, id):
ret = cls.user_database.get(id)
if ret is not None:
return cls(*ret)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
#login_manager.request_loader
def load_user(request):
token = request.headers.get('Authorization')
if token is None:
token = request.args.get('token')
if token is not None:
username,password = token.split(":") # naive token
user_entry = User.get(username)
if (user_entry is not None):
user = User(user_entry[0],user_entry[1])
if (user.password == password):
return user
return None
#app.route("/",methods=["GET"])
#login_manager.request_loader
def index():
if load_user(flask.request):
return render_template('index.html')
else:
return flask.redirect(flask.url_for('login'))
#return Response(response="Hello World!",status=200)
#app.route('/login', methods=['GET', 'POST'])
def login():
if flask.request.method == 'GET':
return '''
<form action='login' method='POST'>
<input type='text' name='email' id='email' placeholder='email'></input>
<input type='password' name='pw' id='pw' placeholder='password'></input>
<input type='submit' name='submit'></input>
</form>
'''
email = flask.request.form['email']
user = User.get(email)
if user and flask.request.form['pw'] == user.password:
login_user(user)
return flask.redirect(flask.url_for('index'))
return 'Bad login'
#app.route("/logout")
#login_required
def logout():
logout_user()
return flask.redirect(flask.url_for('index'))
if __name__ == '__main__':
app.config["SECRET_KEY"] = "ITSASECRET"
app.run(port=5000, debug=True)
However, it doesn't work, because even though it seems to login successfully, when it redirects to the index page, it can't lookup the user from the session and redirects back to the login page. What am I doing wrong?
Flask-Login is a very basic login manager that is built upon with a few other user management frameworks. I have been using Flask-User in production for over 1.5 years with about 30K users and have not had any problems with it (which uses flask-login under the hood). The maintainer is active and has responded to my issues in a timely manner.
It handles user login, registration, lost password, and even email confirmations if so desired. It comes with some pre-built forms if you don't want to mess with that but is easily customizable if you do.
Sounds like maybe when login_user gets called the login is not being persisted. I would make sure however you're doing your sessions is actually storing the login.
I am new to Flask and have been working on an existing app for its login module. It has to be delivered asap. The login credentials validates by Active Directory authentication, which is working as expected. I am referring this flask link which says
url_for('main', page=2): return the internal URL /?page=2. All optional keyword arguments are treated as GET parameters.
My index page is loaded from here (working perfectly):
#app.route("/", methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
userName = request.form['username']
pwd = request.form['password']
try:
ldapobject = auth.bind(userName,pwd)
return redirect(url_for('scheduler')) ----> NEED TO PASS USER AND PWD TO scheduler function using post here
except ldap.LDAPError, error_message:
error = 'Invalid Credentials. Please try again'
return render_template('login.html', error=error)
My scheduler function:
#app.route("/home", methods=['POST'])
def scheduler():
# some code here
# WANT TO HAVE USER CREDENTIALS IN THIS FUNCTION FROM login page
return render_template("scheduler.html", scheduler=form)
Problem: How to pass user's credentials to scheduler method as a POST call
If you want to persist user data between your application pages - you will need to store some session data on user side, e.g. cookies.
flask-login module (https://flask-login.readthedocs.org/en/) can do all the dirty work for you.
In short you need to create User class with several required fields and methods (https://flask-login.readthedocs.org/en/latest/#your-user-class) and specify several methods with decorators to load and check users that are being authenticated:
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app=app)
#login_manager.user_loader
def load_user(username):
return User(username)
#login_manager.request_loader
def load_from_request(request):
return User.auth_is_valid(request.authorization)
After that you can mark any routes that need user credentials with #login_requied decorator and access logged in user via current_user variable:
from flask_login import login_required, current_user
#app.route("/user_needed")
#login_required
def some_function():
print(current_user)
I have created a blueprint that handles authenticating. This blue print uses Flask-Login. And has the following, as well as more code not shown.
In the blueprint I have the following:
from flask.ext.login import LoginManager
from flask.ext.login import UserMixin
from flask.ext.login import current_user
from flask.ext.login import login_required
from flask.ext.login import login_user
from flask.ext.login import logout_user
auth_print = Blueprint('auth_print', __name__)
login_manager = LoginManager()
login_manager.login_view = '/login'
class User(UserMixin):
user_store = {} # Stores the users that are already logged in.
def __init__(self, user_id):
self.user_store[user_id] = self # add the user to the user_store
self.username = user_id # the user_id is in fact the username
self.id = unicode(user_id)
def sign_out(self):
logout_user()
try:
del self.user_store[self.id]
except KeyError:
pass
#classmethod
def get(cls, user_id):
return cls.user_store.get(user_id)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
def get_current_user():
return current_user
#login_required
#auth_print.route('/')
def user():
return "Welcome, and thanks for logging in."
Then I have a small app I would like to add authentication to.
Small App
import the_above_module
app.register_blueprint(the_above_module.auth_print) # register the blueprint
#the_above_module.login_required
#app.route('/profile')
def protected():
name = the_above_module.get_current_user().username
return "Thank you for logging in."
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
Now I know the blueprint's #login_required is working because if I open the browser and go to localhost:8000/ I have to sign in.
However if I go to localhost:8000/profile the login_required decorator never gets triggered. I thus get an error because there is no current user.
Why would #login_required work in the blueprint and not in the app, even when im sure to maintain the same name spaces?
You have to change the order of the decorators. Quoting the Flask documentation:
So how would you use that decorator now? Apply it as innermost
decorator to a view function. When applying further decorators, always
remember that the route() decorator is the outermost:
#app.route('/secret_page')
#login_required
def secret_page():
pass
When we want the user not to access the private page or the page which requires login for that case flask provides decorators.
#app.route("/welcome")
#login_required # If the user is not logged in then it will redirected to unauthorized_handler
def welcome_page():
return """<h1> welcome user</h1>"""
#login_manager.unauthorized_handler # In unauthorized_handler we have a callback URL
def unauthorized_callback(): # In call back url we can specify where we want to
return redirect(url_for('login')) # redirect the user in my case it is login page!
I hope your problem is solved !!!
#login_manager.unauthorized_handler
def unauthorized_callback():
return redirect(url_for('website.index'))