Flask LDAP3 Auth Blueprint fails with Uninitialized ASN.1 value - python-3.x

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.

Related

How to redirect a page from one blueprint view to another?

I am using Flask along with the Blueprint module. In my application, I am trying to redirect the page to page/home after a successful login (user/login) using LDAP but the redirect takes forever without throwing any error.
I tried a couple of different variations of redirect(url_for('page.home')), redirect(url_for('page/home.html')). But each of these commands do not redirect. I am not sure what I am doing wrong. Kindly help.
Folder structure:
user/views.py:
from flask import Flask, Blueprint, request, render_template, redirect, url_for, session
from ldap3 import Server, Connection, ALL, NTLM
from snakeeyes.blueprints.page.views import page
import config.settings as p
user = Blueprint('user', __name__, template_folder='templates')
user.secret_key = 'dev key'
# #user.route('/')
# #user.route('/login')
# def login():
# return render_template('user/login.html')
def connect_ldap(username, password):
if not username or not password:
return False
# try:
# from ldap3 import Server, Connection, ALL, NTLM
# except ImportError as importException:
# print("LDAP3 import not found, run 'sudo pip install ldap3 && sudo pip3 install ldap3'")
# print(importException)
# return False
# define the server
server = Server('us01ds', port=389, get_info=ALL)
# define the connection
user = 'uid=%s,ou=people,ou=users,dc=global,dc=COMPANY,dc=com' % username
conn = Connection(server, user, password, auto_bind=True)
# perform the Bind operation
if not conn.bind():
print('error in bind', conn.result)
return False
else:
return True
#user.route('/', methods=['GET', 'POST'])
#user.route('/login/', methods=['GET', 'POST'])
def login():
# global username
# username = None
# If POST, redirect to dashboard
if request.method == 'POST':
username = request.form['username'].encode('utf8').decode("utf-8")
password = request.form['password'].encode('utf8').decode("utf-8")
# Try to login using ldap
test = connect_ldap(username, password)
# Invalid credentials
if not test:
return render_template(
'login.html',
isinvalid='is-invalid',
error='Username or Password is incorrect'
)
else:
# session['user_id'] = request.form['username']
print('redict to home page')
return redirect(url_for('page.home'))
# If GET, render the login page
else:
return render_template('user/login.html')
page/views.py:
from flask import Blueprint, render_template
page = Blueprint('page', __name__, template_folder='templates')
#page.route('/home')
def home():
return render_template('page/home.html')
#page.route('/terms')
def terms():
return render_template('page/terms.html')
#page.route('/privacy')
def privacy():
return render_template('page/privacy.html')
I found a fix for this problem.
In order to better facilitate the generation of URLs that make use of an HTTPS URL
scheme this patch adds a parameter with this specific purpose in mind. To
achieve this we explicitly pass in a param, _scheme='https', and then set the
url_scheme attribute of our MapAdapter instance appropriately.
Importantly, _external=True must be set in order for this to work properly.
As such, failure to do so results in a ValueError being raised.
So, I just replace return redirect(url_for('page.home')) => return redirect(url_for('page.home', _external=True, _scheme='https'))
Reference: https://github.com/pallets/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7

Flask-Login using Radius

I am trying to setup Flask-Login where the user database is on a RADIUS server.
I can validate credentials just fine against it, but most online examples use SQLAlchemy. Do I still need to use SQLAlchemy if I am not using my own database for Flask?
I am getting errors when the User(Mixin) class is ran.
AttributeError: 'str' object has no attribute 'is_active'
My User class is simple since I do not need have my own DB, just need the 4 methods from the docs.
class User(UserMixin):
pass
Here is some code for trying this just with a dictionary as the DB:
from flask import Flask, url_for, render_template, redirect
from forms import LoginForm
from flask_login import LoginManager, UserMixin, login_required, login_user
users_db = {'user1': 'pass1'}
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.user_loader
def load_user(user_id):
if user_id in users_db:
print('user found in DB')
return user_id
else:
return None
class User(UserMixin):
pass
app.config['SECRET_KEY'] = 'abc123'
#app.route('/', methods=('GET', 'POST'))
def Login():
form = LoginForm()
if form.validate_on_submit():
print('validated')
username = form.username.data
login_user(username)
next = flask.request.args.get('next')
if not is_safe_url(next):
return flask.abort(400)
return flask.redirect(next or flask.url_for('hi'))
return render_template('login.html', form=form)
#app.route('/hi')
def hi():
return """<h1>Hi</h1>
{% if current_user.is_authenticated %}
Hi {{ current_user.name }}!
{% endif %}"""
#app.route('/secure')
#login_required
def secure():
return "This is a secure page."
#app.route("/logout")
#login_required
def logout():
logout_user()
return redirect('https://cisco.com')
What would be obviously wrong here, or what could I be conceptually not understanding?
Thanks!!!
Your user_db only contains strings. You have to create an instance of the User class and return the instance on login.
See https://flask-login.readthedocs.io/en/latest/
PS I am on mobile, if this is not enough info, I'll update my answer later.
This article also introduces the login mechanism
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins

Certificate verify failed due to recaptcha

I registered recaptcha for my WTForms, and This error **
SSL: CERTIFICATE_VERIFY_FAILED pops up
when i submitted the form.
I registered recaptcha on google with label = localhost and domains = localhost and 127.0.0.1
When I submitted the form after verifying that I'm not a robot, I got redirected to the page with following error:
urllib.error.URLError urllib.error.URLError:
urlopen error [SSL:
CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)
If I set app.config['TESTING'] to True, the form gets submitted just fine.
Here's what my app.py looks like:
from flask import Flask, render_template
from flask_wtf import FlaskForm, RecaptchaField
from wtforms import StringField, PasswordField
from wtforms.validators import InputRequired, Length, AnyOf
app = Flask(__name__)
app.config['SECRET_KEY'] = 'Thisissecret!'
app.config['RECAPTCHA_PUBLIC_KEY'] = '6LdUxpsUAAAAAEKf7HmevAB9-ntHArEovS2pDmOP'
app.config['RECAPTCHA_PRIVATE_KEY'] = '6LdUxpsUAAAAACtXnVVOYTX1M9Hv83jkhzx4KxX1'
app.config['TESTING'] = False
class LoginForm(FlaskForm):
username = StringField('username', validators=[InputRequired(message='A username is required!'), Length(min=5, max=10, message='Must be between 5 and 10 characters')])
password = PasswordField('password', validators=[InputRequired(message='Password is required!'), AnyOf(values=['password', 'secret'])])
recaptcha = RecaptchaField()
#app.route('/form', methods=['GET', 'POST'])
def form():
form = LoginForm()
if form.validate_on_submit():
return '<h1>The username is {}. The password is {}.</h1>'.format(form.username.data, form.password.data)
return render_template('form.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
In my form.html, I have included {{ form.recaptcha }} in the body section.
I expect that the form would submit just fine after the recaptcha validation.

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 login using Linux System Credentials

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')

Resources