I am currently trying to use Flask-LDAP for authentication purposes for Sandman. Unlike normal python LDAP where the documentation is pretty straight forward, this is pretty offbase.
Example from documentation.
from flask import Flask
from flask.ext.ldap import LDAP, login_required
from flask.ext.pymongo import PyMongo
app = Flask(__name__)
app.debug = True
app.config['LDAP_HOST'] = 'ldap.example.com'
app.config['LDAP_DOMAIN'] = 'example.com'
app.config['LDAP_SEARCH_BASE'] = 'OU=Domain Users,DC=example,DC=com'
app.config['LDAP_LOGIN_VIEW'] = 'custom_login'
app.config['MONGO_DBNAME'] = 'simpledb'
mongo = PyMongo(app, config_prefix='MONGO')
ldap = LDAP(app, mongo)
app.secret_key = "welfhwdlhwdlfhwelfhwlehfwlehfelwehflwefwlehflwefhlwefhlewjfhwelfjhweflhweflhwel"
app.add_url_rule('/login', 'login', ldap.login, methods=['GET', 'POST'])
This does not tell me where I can place my dn or password. So I am completely confused as to how this thing actually works.
Has anyone effectively used Flask-LDAP and if so how did you set it up? Or is there a better product out there like flask-login that I should use?
Thanks,
Flask-login is okay and the de-facto lib for session management so you'll be needing that.
As for flask-ldap, yeah it's not well documented...
But this might be useful to you (it seems like a full feature ldap lib for flask including login helpers):
flask_ldap_login at https://github.com/ContinuumIO/flask-ldap-login
Related
I'm working on updating an existing university department internal website to run using Flask. Before we completely release the project where it will use shibboleth to manage authentication, I've been trying to setup authentication for testing using htpassword using this tutorial. I'm able to get authentication using htpasswords working for a single file, but I can't figure out how to add the authentication to separate files. Currently, the project looks like this:
This is my main file called app.py:
#app.py
from flask import Flask
from flask_htpasswd import HtPasswdAuth
app = Flask(__name__)
app.config['FLASK_HTPASSWD_PATH'] = '.htpasswd'
app.config['FLASK_SECRET'] = "Super secret key that I will change later"
htpasswd = HtPasswdAuth(app)
import exampleRoute
#app.route('/')
#htpasswd.required
def home(user):
return "This is the homepage"
This is an example of one of my route files called exampleRoute.py:
# exampleRoute.py
from app import app
from flask import Flask
#app.route('/exampleRoute')
def exampleRoute():
return "This is an example route in another file"
When I place the #htpassword.required decorator in front of exampleRoute(), I get an error saying that htpassword is not defined (which makes sense). I've tried to import the app configuration a few different ways and at best, I end up with a circular dependency. I know that I could place the authentication code in a separate file and then import that into all my endpoints, but I didn't think this was possible since this method is incorporated into the app configuration.
I ended up getting an answer from the reddit user alexisprince. The solution was to use Blueprints that import htpasswd from another file (called extensions.py).
I'm new to python and Flask, and I have a project from the production ENV which I'm trying to run on my local.
I was able to install all the packages and bring them up on http://127.0.0.1:5000, but the problem is that is the only page that actually works on my local. and when I try to do Authorization or even simple post, it does not do anything on my local ( I put some print on the other files and none of them get fire) so I assume they keep going to production as it does have some APIs as well.
Here is the main page (application.py) which is working on my local.
import os
import jwt
import logging
from datetime import datetime, timedelta
from http import HTTPStatus
from pydantic import BaseModel
from passlib.context import CryptContext
from flask import Flask, request, jsonify
from flask_restplus import Api, Resource, fields
from werkzeug.middleware.proxy_fix import ProxyFix
from applicationinsights.flask.ext import AppInsights
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
api = Api(app, doc='/')
ns = api.namespace(name='Room Parsing', path='/')
swaggerTokenParser = api.parser()
swaggerTokenParser.add_argument('username', location='form')
swaggerTokenParser.add_argument('password', location='form')
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
rtp = RoomTitleParser(room_prototype_XX_path)
ALGORITHM = "HS256"
app.config["SECRET_KEY"] = os.getenv('SECRET_KEY')
app.config["APPINSIGHTS_INSTRUMENTATIONKEY"] = os.getenv('APPINSIGHTS_INSTRUMENTATIONKEY')
appinsights = AppInsights(app)
app.logger.setLevel(level=logging.INFO)
logger = app.logger
#ns.route("/api/room/parser")
class RoomParser(Resource):
#api.expect(swaggerRoom)
#api.doc(description='Process a JSON object with a room description and a unique identifier in order to run it through the parser. This will result in a list of keywords which will be extracted from the description. The result will be returned in a JSON format')
def post(self):
try:
room_desc = "deluxe suite queen ocean view"
room_id = "ID123"
print('11111111111')
if not room_desc or not room_id:
return make_json_error_message("Please send a a room with description and id",HTTPStatus.BAD_REQUEST)
room_dict = dict(Room(description=room_desc, id=room_id))
print(parsed)
parsed = rtp.parse_title(room_dict)
print(parsed)
return jsonify(parsed['room'])
except Exception as e:
logger.error("Error parsing a room: " + repr(e))
return make_json_error_message("We have encountered an error. Please try again later",HTTPStatus.BAD_REQUEST)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
As you can see I have some print statements and all of them are working on my local consol. But when I track down the code for Example this line,
parsed = rtp.parse_title(room_dict)
and put some print command inside the parse_title() function which is located in another file, I do NOT see any output in the console as well as webpage!
Why? I have no idea!!! LOL and that is why I'm here.
I believe it might be related to the #ns.route("/api/room/parser") that I have on top of the class, but not sure.
Can you guys please drop some knowledge here so I can learn and get this code to work on my local completely?
Thanks for your help!
With what you've provided, there doesn't appear to be any reference to the production environment.
The only thing that sticks out to me is
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
The Werkzeug Documentation states that this middleware can set REMOTE_ADDR, HTTP_HOST from X-Forwarded headers. You might try removing that for a bit and see if that helps. There might be some reference to production in that proxy. I don't know enough about that middleware to know for sure however.
It might be helpful to know of any other configuration information or environment you have setup.
It tuned out to be related to my conda env,
So I uninstalled the Anaconda and then installed the plain python and installed pyCharm as well and setup a new env in pycharm. Then it worked like a charm!
Thanks
What is the easiest way to have a server-side session variable in Flask?
Variable value:
A simple string
Not visible to the client (browser)
Not persisted to a DB -- simply vanishes when the session is gone
There is a built-in Flask session, but it sends the session data to the client:
session["secret"] = "I can see you"
The data is Base64 encoded and sent in a cryptographically signed cookie, but it is still trivial to read on the client.
In many frameworks, creating a server-side session variable is a one-liner, such as:
session.secret = "You can't see this"
The Flask solutions I have found so far are pretty cumbersome and geared towards handling large chunks of data. Is there a simple lightweight solution?
I think the Flask-Session extension is what you are looking for.
Flask-Session is an extension for Flask that adds support for Server-side Session to your application.
From the linked website:
from flask import Flask, session
from flask_session import Session # new style
# from flask.ext.session import Session # old style
app = Flask(__name__)
# Check Configuration section for more details
SESSION_TYPE = 'redis'
app.config.from_object(__name__)
Session(app)
#app.route('/set/')
def set():
session['key'] = 'value'
return 'ok'
#app.route('/get/')
def get():
return session.get('key', 'not set')
This answer is from June 2020 for flask-session 0.3.2.
The documentation is here.
There are several available SESSION_TYPESs. filesystem is the most straightforward while you're testing. The expectation is you already have a Redis, database, etc. setup if you are going to use the other SESSION_TYPEs. Section on SESSION_TYPE and requirements
null: NullSessionInterface (default)
Redis: RedisSessionInterface
Memcached: MemcachedSessionInterface
filesystem: FileSystemSessionInterface
MongoDB: MongoDBSessionInterface
SQLAlchemy: SqlAlchemySessionInterface
Code example from the documentation. If you go to /set/ then the session['key'] is populated with the word 'value'. But if you go to /get/ first, then `session['key'] will not exist and it will return 'not set'.
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
#personal style preference compared to the first answer
Session(app)
#app.route('/set/')
def set():
session['key'] = 'value'
return 'ok'
#app.route('/get/')
def get():
return session.get('key', 'not set')
I am looking to implement a SAML 2.0 based service provider in Python.
My web apps are currently all Flask applications. I plan to make a Flask blueprint/decorator that allows me to drop single sign-on capabilities into preexisting applications.
I have looked into python-saml extensively and unfortunately there are dependency issues that are not worth resolving, as I have too many preexisting servers/apps whos environments won't be compatible.
PySAML2 looks like it could work, however there is little documentation, and what documentation is available I have trouble comprehending. There are no examples of PySAML2 used in a Flask app.
The Identity Provider I have is Okta. I have Okta set up so that after I login at Okta, I am redirected to my app.
Can anyone offer any advice on using PySAML2, or perhaps advice on how to best authenticate a user using SAML 2.0 who is visiting my application?
Update: A detailed explanation on using PySAML2 with Okta is now on developer.okta.com.
Below is some sample code for implementing a SAML SP in Python/Flask. This sample code demonstrates several things:
Supporting multiple IdPs.
Using Flask-Login for user management.
Using the "SSO URL" as the audience restriction (to simplify configuration on the IdP).
Just in time provisioning of users ("SAML JIT")
Passing additional user information in Attribute Statements.
What is not demonstrated is doing SP initiated authentication requests - I'll followup with that later.
At some point, I hope to create a wrapper around pysaml2 that has opinionated defaults.
Lastly, like python-saml, the pysaml2 library makes use of the xmlsec1 binary. This might also cause dependency issues in your server environments. If that's the case, you'll want to look into replacing xmlsec1 with the signxml library.
Everything in the sample below should work with the following setup:
$ virtualenv venv
$ source venv/bin/activate
$ pip install flask flask-login pysaml2
Finally, you'll need to do to things on the Okta side for this to work.
First: In the General tab of your Okta application configuration, configure the application to send the "FirstName" and "LastName" Attribute Statements.
Second: In the Single Sign On tab of your Okta application configuration, take of the url and put them in a file named example.okta.com.metadata. You can do this with a command like the one below.
$ curl [the metadata url for your Okta application] > example.okta.com.metadata
Here is what you'll need for your Python/Flask application to handle IdP initiated SAML requests:
# -*- coding: utf-8 -*-
import base64
import logging
import os
import urllib
import uuid
import zlib
from flask import Flask
from flask import redirect
from flask import request
from flask import url_for
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 saml2 import BINDING_HTTP_POST
from saml2 import BINDING_HTTP_REDIRECT
from saml2 import entity
from saml2.client import Saml2Client
from saml2.config import Config as Saml2Config
# PER APPLICATION configuration settings.
# Each SAML service that you support will have different values here.
idp_settings = {
u'example.okta.com': {
u"metadata": {
"local": [u'./example.okta.com.metadata']
}
},
}
app = Flask(__name__)
app.secret_key = str(uuid.uuid4()) # Replace with your secret key
login_manager = LoginManager()
login_manager.setup_app(app)
logging.basicConfig(level=logging.DEBUG)
# Replace this with your own user store
user_store = {}
class User(UserMixin):
def __init__(self, user_id):
user = {}
self.id = None
self.first_name = None
self.last_name = None
try:
user = user_store[user_id]
self.id = unicode(user_id)
self.first_name = user['first_name']
self.last_name = user['last_name']
except:
pass
#login_manager.user_loader
def load_user(user_id):
return User(user_id)
#app.route("/")
def main_page():
return "Hello"
#app.route("/saml/sso/<idp_name>", methods=['POST'])
def idp_initiated(idp_name):
settings = idp_settings[idp_name]
settings['service'] = {
'sp': {
'endpoints': {
'assertion_consumer_service': [
(request.url, BINDING_HTTP_REDIRECT),
(request.url, BINDING_HTTP_POST)
],
},
# Don't verify that the incoming requests originate from us via
# the built-in cache for authn request ids in pysaml2
'allow_unsolicited': True,
'authn_requests_signed': False,
'logout_requests_signed': True,
'want_assertions_signed': True,
'want_response_signed': False,
},
}
spConfig = Saml2Config()
spConfig.load(settings)
spConfig.allow_unknown_attributes = True
cli = Saml2Client(config=spConfig)
try:
authn_response = cli.parse_authn_request_response(
request.form['SAMLResponse'],
entity.BINDING_HTTP_POST)
authn_response.get_identity()
user_info = authn_response.get_subject()
username = user_info.text
valid = True
except Exception as e:
logging.error(e)
valid = False
return str(e), 401
# "JIT provisioning"
if username not in user_store:
user_store[username] = {
'first_name': authn_response.ava['FirstName'][0],
'last_name': authn_response.ava['LastName'][0],
}
user = User(username)
login_user(user)
# TODO: If it exists, redirect to request.form['RelayState']
return redirect(url_for('user'))
#app.route("/user")
#login_required
def user():
msg = u"Hello {user.first_name} {user.last_name}".format(user=current_user)
return msg
if __name__ == "__main__":
port = int(os.environ.get('PORT', 5000))
if port == 5000:
app.debug = True
app.run(host='0.0.0.0', port=port)
So, following the examples for swagger ui usage with flask blueprints (https://github.com/rantav/flask-restful-swagger/blob/master/examples/blueprints.py), I have the following code:
app = Flask(__name__)
test_blueprint = Blueprint('tests', __name__)
test_api = swagger.docs(restful.Api(test_blueprint), apiVersion='0.1',
basePath='http://localhost:5000',
produces=["application/json", "text/html"],
api_spec_url='/api/spec')
# Operation TestOp defined here
test_api.add_resource(TestOp, '/')
if __name__ == "__main__":
app.register_blueprint(test_blueprint, url_prefix='/test')
app.run(debug=True)
However, when I try to access the api spec docs, the URL cannot be located.
I've tried...
localhost:5000/api/spec
localhost:5000/test_api/api/spec
localhost:5000/test_api
...all of which return a 404. I've also tried creating the app without blueprints, creating the docs with
swagger.docs(restful.Api(app)...)
instead. When there this is done and no blueprints are involved, I can reach the docs at
localhost:5000/api/spec
So, am I creating my application incorrectly with blueprints, or am I just not hitting the right URL to get access to the docs?
I know this thread is old, but I ran into this exact problem today, trying to use flask-restful-swagger with my (somewhat) modern flask + python3 app that uses blueprints. Same problem, no errors, just no spec available no matter what i tried.
I finally abandoned this package (as it seems like it hasn't been very active anyway), even though I like the markup better with this package.
I chose Flasgger, which seemed to be updated more recently. In 10 minutes I had it up and running. Code and short tutorial are here: https://github.com/rochacbruno/flasgger
It looks like you're just not hitting the right URL. Because your blueprint url_prefix is "/test", the Swagger spec url should be at:
localhost:5000/test/api/spec
something wrong in swagger.docs()
from flask_restful import Api
test_blueprint = Blueprint('tests', __name__)
test_api = swagger.docs(Api(test_blueprint)...)
app.register_blueprint(test_blueprint)
what'more
main_blueprint = Blueprint('api', __name__, url_prefix='/demo')