Flask login using Linux System Credentials - linux

I am creating a simple web app using flask. I will be hosting it on my linux server.
The web-app does multiple user specific things. Like list directories in Users home, add ssh-keys for user and stuff like that.
I would like to know if there is a way for flask to open a login page, and the user-name and password be validated based on system user-name and password. (i.e that users system credentials). If yes, then how. If no then what else can I do?

Using ’simplepam’ python package you can authenticate against PAM system on linux. Here is flask basic example which I have modifed to use simplepam:
from flask import Flask, session, redirect, url_for, escape, request
from simplepam import authenticate
app = Flask(__name__)
#app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if authenticate(str(username), str(password)):
session['username'] = request.form['username']
return redirect(url_for('index'))
else:
return 'Invalid username/password'
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=password name=password>
<p><input type=submit value=Login>
</form>
'''
#app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
if __name__ == '__main__':
app.run(debug='True')

Related

REMEMBER_COOKIE_DURATION in flask is not working

I am trying to configure my application to expire cookies after some amount of time however they are not working as expected, my cookies don't get expired at all.
I have the following configuration in my app.py file:
from flask_login import LoginManager
from datetime import timedelta
app.config['REMEMBER_COOKIE_DURATION'] = timedelta(seconds=30)
login = LoginManager(app)
login.init_app(app)
login.login_view = 'login'
The following is my remember_me input:
<input type="checkbox" class="form-check-input" id="remember_me" name="remember_me">
<label class="form-check-label" for="remember_me">Remember me for 1 week</label>
Finally the following is my view function:
#app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return flask.redirect(flask.url_for('home'))
if flask.request.method == 'POST':
member_username = flask.request.form['member_username']
member_password = flask.request.form['member_password']
remember_me = flask.request.form.get('remember_me')
existing_user = Users.query.filter_by(username=member_username).first()
if existing_user is None or not existing_user.check_password(member_password):
flask.flash('Invalid username or password')
return flask.redirect(flask.url_for('login'))
if existing_user:
login_user(existing_user, remember=remember_me)
next_page = flask.request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = flask.url_for('home')
return flask.redirect(next_page)
return flask.render_template('login.html')
The resources I have already followed:
https://code.luasoftware.com/tutorials/flask/how-to-configure-flask-login/
https://www.youtube.com/watch?v=CRvV9nFKoPI
flask-login: Chrome ignoring cookie expiration?
What am I missing?
I had the same question and adding PERMANENT_SESSION_LIFETIME worked for me. My understanding is that both REMEMBER_COOKIE_DURATION and PERMANENT_SESSION_LIFETIME have effect on when the session expires.
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(seconds=30)

#flask_login.login_required not working properly

I am using flask_login for implementing auth in my flask application
Here are the unauthorized_handler and login_required enabled method
#login_manager.unauthorized_handler
def unauthorized_handler():
return redirect(url_for('login'))
# use decorators to link the function to a url
#app.route('/profile', methods=['GET'])
#flask_login.login_required
def profile():
return render_template('profile.html')
I am using firebase as my backend service
#app.route('/login', methods=['GET', 'POST'])
def login():
auth = firebase.auth()
if request.method == 'POST':
try:
user = auth.sign_in_with_email_and_password(request.form['email'], request.form['password'])
if user != None:
return redirect(url_for('profile'))
except requests.exceptions.HTTPError as e:
response = e.args[0].response
error = response.json()['error']['code']
return redirect(url_for('home'))
return render_template('login.html')
The problem is that after I login(which is successfull) the app is automatically redirected to the /login url instead of /profle.
I tried turning my debug mode off and on still not working.
It can be a case of a double redirect happening, where you first get redirected to profile, and then the login_required decorator kicks in redirecting you back to login, because from the point of view of flask_login you are not logged in yet.
You might have retrieved user credentials from firebse, but with flask_login you have to also call login_user function. (Also see Login Example of flask login's page)

How to redirect users to the required html page only after successful login (Flask Python App)?

I am trying to set up login page before accessing any other page. So as of now the code is working fine but if I access '/home' its taking directly to that page without login required page.
I tried to understand online solutions like login-required with SQLAlchemy but nothing seems working and I got totally lost. This does not require login-required I guess.
Here's my code:
from dbscriptdeploy import dbupdate
from flask.templating import render_template
from flask import Flask, request, url_for, redirect
DBupdate = dbupdate()
app = Flask(__name__) #create the Flask app
#app.route('/success')
def deploy_success():
return render_template("success.html")
def deploy_script(sql):
res = DBupdate.dbvalues_get(sql)
return res
#app.route('/', methods=['GET', 'POST'])
def login_page():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or request.form['password'] != 'admin':
error = 'Invalid Credentials. Please try again.'
else:
return redirect(url_for('form_example'))
return render_template('login.html', error=error)
#app.route('/home', methods=['GET', 'POST'])
def form_example():
res = None
error = ''
try:
if request.method == 'POST':
sql = request.form.get('sql')
res = deploy_script(sql)
if res:
return redirect(url_for('deploy_success'))
else:
error = "*Invalid Value. Please try again."
return render_template('home.html', error=error)
except Exception as e:
return render_template('home.html', error=error)
if __name__ == '__main__':
app.run(debug=True) #run app in debug mode on port 5000
Login.html Code:
<html>
<head>
<title>Flask Intro - login page</title>
</head>
<body>
<div class="container">
<h1>Please login</h1>
<br>
<form action="" method="post">
<input type="text" placeholder="Username" name="username" value="{{
request.form.username }}">
<input type="password" placeholder="Password" name="password" value="{{
request.form.password }}">
<input class="btn btn-default" type="submit" value="Login">
</form>
{% if error %}
<p class="error"><strong>Error:</strong> {{ error }}
{% endif %}
</div>
</body>
</html>
So what I expect is after successful login only then they should be directed to \home page else they should be directed again to login page.
If I were you, I'd use flask-login. However, if you want to develop a simple solution by your own, you should save some var in the user's session.
Also in your login method there is a bug. I'd do:
from flask import session
#app.route('/', methods=['GET', 'POST'])
def login_page():
error = None
if request.method == 'POST':
if request.form['username'] == 'admin' and request.form['password'] == 'admin':
session['admin'] = request.form['username']
return redirect(url_for('form_example'))
else:
error = 'Invalid Credentials. Please try again.'
return render_template('login.html', error=error)
Then, in your form_example view you should check that admin is in session, else you can redirect to your login view:
#app.route('/home', methods=['GET', 'POST'])
def form_example():
if 'admin' not in session:
return redirect(url_for('login_page'))
else:
# Your code here
If you really want to go down the path of developing your own login/authentication solution, check out the documentation for flash_message
http://flask.pocoo.org/docs/1.0/patterns/flashing/. This tutorial has the exact problem you're tackling.
The re-routing of a successful login attempt occurs with the redirect call
return redirect(url_for('homepage'))

flask session not working if I use mkdtemp() for SESSION_FILE_DIR

I have a flask application in what I'm trying to create a session but only for the admin routes, so anywhere else can be accessible except admin routes, the thing is in my localhost everything works fine, but, in the live server, the session get created when I log in, but when I try to check if the session exist calling session.get("user_id"), it doesn't. Is like if the session is not created persistent. I have been reading for 10 hours today and I finally found why is not working, but I really don't undestand why it happens. The problem i'm facing is located in app.config["SESSION_FILE_DIR"] = mkdtemp(), if I use this in the localhost works, but not in the live server, and if I omite that line, then it works in the live server. Here is my code:
from flask import Flask, flash, redirect, render_template, request, session, jsonify, url_for
from flask_session import Session
from functools import wraps
from werkzeug.security import check_password_hash, generate_password_hash
from tempfile import mkdtemp # used to create a temp directory
from helpers import login_required
import os
# Configure application as flask app
app = Flask(__name__)
# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.secret_key = 'superkey'
# function to login admin
#app.route("/login", methods=["GET", "POST"])
def login():
"""Log admin in"""
# Forget any user_id
session.clear()
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# Ensure username was submitted
if not request.form.get("username"):
flash ("must provide username")
return render_template('/login.html')
# Ensure password was submitted
elif not request.form.get("password"):
flash ("must provide password")
return render_template('/login.html')
# Query database for username
username = request.form.get("username")
username = str.lower(username)
db = dbConnection()
db.execute("SELECT * FROM identities WHERE user_name = ?", [username])
rows = db.fetchall()
# Ensure username exists and password is correct
if len(rows) == 1:
uid, name, pass_hash = rows[0]
if len(rows) != 1 or not check_password_hash(pass_hash, request.form.get("password")):
flash ("invalid username and/or password")
return render_template('/login.html')
# Remember which user has logged in
session["user_id"] = uid
# Redirect user to home page
return redirect("/adminForms")
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("login.html")
# function as decorator to ensure the user was logged in before
# can go to a protected page
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
# if session don't return a value, then the user needs to log in
if session.get("user_id") is None:
return redirect("/login")
return f(*args, **kwargs)
return decorated_function

Flask LDAP3 Auth Blueprint fails with Uninitialized ASN.1 value

I'm trying to put together a flask blueprint for LDAP3 auth. If I use the same code as a standard flask app, everything works as expected, the bind is successful and the user auth also succeeds:
DEBUG:root:Validating LDAPLoginForm against LDAP
DEBUG:flask_ldap3_login:Opening connection with bind user 'XXXX#XXXX.COM'
DEBUG:flask_ldap3_login:Successfully bound to LDAP as 'XXXX#XXXX.COM' for search_bind method
DEBUG:flask_ldap3_login:Performing an LDAP Search using filter '(&(objectclass=person)(sAMAccountName=YYYY))', base 'ou=Users,ou=XXXX,dc=XXXX,dc=COM', and scope 'SUBTREE'
DEBUG:flask_ldap3_login:Opening connection with bind user 'CN=YYYY,OU=Admin Users,OU=Users,OU=XXXX,DC=XXXX,DC=COM'
DEBUG:flask_ldap3_login:Directly binding a connection to a server with user:'CN=YYYY,OU=Admin Users,OU=Users,OU=XXXX,DC=XXXX,DC=COM'
DEBUG:flask_ldap3_login:Authentication was successful for user 'YYYY'
but as soon as I turn it into a blueprint:
DEBUG:root:Validating LDAPLoginForm against LDAP
DEBUG:flask_ldap3_login:Opening connection with bind user 'XXXX#XXXX.COM'
DEBUG:flask_ldap3_login:Destroying connection at <0x7f181f9ee2b0>
ERROR:flask_ldap3_login:Uninitialized ASN.1 value ("__len__" attribute looked up)
My init.py is as follows
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
from app.ldauth.views import auth_blueprint
app.register_blueprint(auth_blueprint)
And app/ldauth/views.py:
from flask import Flask, Blueprint, url_for
from flask_ldap3_login import LDAP3LoginManager
from flask_login import LoginManager, login_user, UserMixin, current_user
from flask import render_template_string, render_template, redirect
from flask_ldap3_login.forms import LDAPLoginForm
from app import app
auth_blueprint = Blueprint('ldauth',__name__,template_folder='templates')
login_manager = LoginManager(app) # Setup a Flask-Login Manager
ldap_manager = LDAP3LoginManager(app) # Setup a LDAP3 Login Manager.
login_manager.login_view = "auth.login"
users = {}
class User(UserMixin):
def __init__(self, dn, username, data):
self.dn = dn
self.username = username
self.data = data
def __repr__(self):
return self.dn
def get_id(self):
return self.dn
#login_manager.user_loader
def load_user(id):
if id in users:
return users[id]
return None
#ldap_manager.save_user
def save_user(dn, username, data, memberships):
user = User(dn, username, data)
users[dn] = user
return user
#auth_blueprint.route('/login', methods=['GET', 'POST'])
def login():
template = """
{{ get_flashed_messages() }}
{{ form.errors }}
<form method="POST">
<label>Username{{ form.username() }}</label>
<label>Password{{ form.password() }}</label>
{{ form.submit() }}
{{ form.hidden_tag() }}
</form>
"""
# Instantiate a LDAPLoginForm which has a validator to check if the user
# exists in LDAP.
form = LDAPLoginForm()
if form.validate_on_submit():
# Successfully logged in, We can now access the saved user object
# via form.user.
login_user(form.user) # Tell flask-login to log them in.
# TODO: Validate next to ensure it is safe!
return redirect(next) # Send them home
return render_template_string(template,form=form)
pip freeze:
Babel==2.5.1
blinker==1.4
click==6.7
Flask==0.12.2
Flask-BabelEx==0.9.3
flask-ldap3-login==0.9.13
Flask-Login==0.4.0
Flask-Mail==0.9.1
Flask-Principal==0.4.0
Flask-Security==3.0.0
Flask-SQLAlchemy==2.3.2
Flask-WTF==0.14.2
itsdangerous==0.24
Jinja2==2.10
ldap3==2.3
MarkupSafe==1.0
passlib==1.7.1
pkg-resources==0.0.0
pyasn1==0.3.7
pyasn1-modules==0.1.5
python3-ldap==0.9.8.4
pytz==2017.3
speaklater==1.3
SQLAlchemy==1.1.15
Werkzeug==0.12.2
WTForms==2.1
I'm clearly missing something here, any ideas?
Turns out this was some issue with the virtualenv. Created a new virtualenv this morning and moved the code into it, works as expected.

Resources