current authenticated python flask restful-authentication flask-security flask-restless

python - authenticated - flask login admin



Combinando Flask-inquless, Flask-security y peticiones regulares de Python (2)

Mi objetivo es proporcionar una API REST a mi aplicación web. Utilizando:

  • Python 2.7.5
  • Frasco == 0.10.1
  • Flask-Restless == 0.13.1
  • Flask-Security == 1.7.3

Necesito asegurar el acceso a mis datos para acceso web y REST. Sin embargo, no puedo obtener ninguna request python que tenga éxito al intentar conectarme a la API segura.

Las siguientes salidas se obtienen utilizando el módulo completamente funcional proporcionado al final de esta pregunta.

Logro obtener una respuesta correcta cuando uso http://127.0.0.1:5000/api/v1/free_stuff :

>>> import requests >>> r=requests.get(''http://127.0.0.1:5000/api/v1/free_stuff'') >>> print ''status:'', r.status_code status: 200 # all is fine

Al intentar la autenticación con http://127.0.0.1:5000/api/v1/protected_stuff :

>>> from requests.auth import HTTPBasicAuth, HTTPDigestAuth >>> r=requests.get(''http://127.0.0.1:5000/api/v1/protected_stuff'', auth=HTTPBasicAuth(''test'', ''test'')) # the same with ``HTTPDigestAuth`` >>> print ''status:'', r.status_code status: 401 >>> r.json() # failed! {u''message'': u''401: Unauthorized''}

Aquí hay un módulo funcional ficticio utilizado para producir los resultados anteriores:

from flask import Flask, render_template, url_for, redirect from flask.ext.sqlalchemy import SQLAlchemy from flask.ext.security import Security, SQLAlchemyUserDatastore, / UserMixin, RoleMixin, login_required, current_user from flask.ext.restless import APIManager from flask.ext.restless import ProcessingException # Create app app = Flask(__name__) app.config[''DEBUG''] = True app.config[''SECRET_KEY''] = ''super-secret'' app.config[''SQLALCHEMY_DATABASE_URI''] = ''sqlite://'' # Create database connection object db = SQLAlchemy(app) # Define Flask-security models roles_users = db.Table(''roles_users'', db.Column(''user_id'', db.Integer(), db.ForeignKey(''user.id'')), db.Column(''role_id'', db.Integer(), db.ForeignKey(''role.id''))) class Role(db.Model, RoleMixin): id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(80), unique=True) description = db.Column(db.String(255)) class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) roles = db.relationship(''Role'', secondary=roles_users, backref=db.backref(''users'', lazy=''dynamic'')) #Some additional stuff to query over... class SomeStuff(db.Model): __tablename__ = ''somestuff'' id = db.Column(db.Integer, primary_key=True) data1 = db.Column(db.Integer) data2 = db.Column(db.String(10)) user_id = db.Column(db.Integer, db.ForeignKey(''user.id''), nullable=True) user = db.relationship(User, lazy=''joined'', join_depth=1, viewonly=True) # Setup Flask-Security user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) # API def auth_func(**kw): #import ipdb; ipdb.set_trace() if not current_user.is_authenticated(): raise ProcessingException(description=''Not authenticated!'', code=401) return True apimanager = APIManager(app, flask_sqlalchemy_db=db) apimanager.create_api(SomeStuff, methods=[''GET'', ''POST'', ''DELETE'', ''PUT''], url_prefix=''/api/v1'', collection_name=''free_stuff'', include_columns=[''data1'', ''data2'', ''user_id'']) apimanager.create_api(SomeStuff, methods=[''GET'', ''POST'', ''DELETE'', ''PUT''], url_prefix=''/api/v1'', preprocessors=dict(GET_SINGLE=[auth_func], GET_MANY=[auth_func]), collection_name=''protected_stuff'', include_columns=[''data1'', ''data2'', ''user_id'']) # Create a user to test with @app.before_first_request def create_user(): db.create_all() user_datastore.create_user(email=''test'', password=''test'') user_datastore.create_user(email=''test2'', password=''test2'') ### stuff = SomeStuff(data1=2, data2=''toto'', user_id=1) db.session.add(stuff) stuff = SomeStuff(data1=5, data2=''titi'', user_id=1) db.session.add(stuff) db.session.commit() # Views @app.route(''/'') @login_required def home(): return render_template(''index.html'') @app.route(''/logout/'') def log_out(): logout_user() return redirect(request.args.get(''next'') or ''/'') if __name__ == ''__main__'': app.run()

¿Alguna idea?

[edit] para que sea completamente funcional a través de la interfaz web, debe tener una subcarpeta de templates tenga al menos el siguiente archivo login.html :

{% block body %} <form action="" method=post class="form-horizontal"> <h2>Signin to FlaskLogin(Todo) Application </h2> <div class="control-group"> <div class="controls"> <input type="text" id="username" name="username" class="input-xlarge" placeholder="Enter Username" required> </div> </div> <div class="control-group"> <div class="controls"> <input type="password" id="password" name="password" class="input-xlarge" placeholder="Enter Password" required> </div> </div> <div class="control-group"> <div class="controls"> <button type="submit" class="btn btn-success">Signin</button> </div> </div> </form> {% endblock %}


Finalmente fui a Flask-JWT ( https://pypi.python.org/pypi/Flask-JWT/0.1.0 )

Aquí está mi ejemplo mínimo modificado:

from flask import Flask, render_template, request, url_for, redirect from flask.ext.sqlalchemy import SQLAlchemy from flask.ext.security import Security, SQLAlchemyUserDatastore, / UserMixin, RoleMixin, login_required, current_user, logout_user from flask.ext.restless import APIManager from flask.ext.restless import ProcessingException from flask.ext.login import user_logged_in # JWT imports from datetime import timedelta from flask_jwt import JWT, jwt_required # Create app app = Flask(__name__) app.config[''DEBUG''] = True app.config[''SECRET_KEY''] = ''super-secret'' app.config[''SQLALCHEMY_DATABASE_URI''] = ''sqlite://'' # expiration delay for tokens (here is one minute) app.config[''JWT_EXPIRATION_DELTA''] = timedelta(seconds=60) # Create database connection object db = SQLAlchemy(app) # creates the JWT Token authentication ====================================== jwt = JWT(app) @jwt.authentication_handler def authenticate(username, password): user = user_datastore.find_user(email=username) print ''%s vs. %s'' % (username, user.email) if username == user.email and password == user.password: return user return None @jwt.user_handler def load_user(payload): user = user_datastore.find_user(id=payload[''user_id'']) return user # Define Flask-security models =============================================== roles_users = db.Table(''roles_users'', db.Column(''user_id'', db.Integer(), db.ForeignKey(''user.id'')), db.Column(''role_id'', db.Integer(), db.ForeignKey(''role.id''))) class Role(db.Model, RoleMixin): id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(80), unique=True) description = db.Column(db.String(255)) class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) roles = db.relationship(''Role'', secondary=roles_users, backref=db.backref(''users'', lazy=''dynamic'')) #Some additional stuff to query over... class SomeStuff(db.Model): __tablename__ = ''somestuff'' id = db.Column(db.Integer, primary_key=True) data1 = db.Column(db.Integer) data2 = db.Column(db.String(10)) user_id = db.Column(db.Integer, db.ForeignKey(''user.id''), nullable=True) user = db.relationship(User, lazy=''joined'', join_depth=1, viewonly=True) # Setup Flask-Security user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) # Flask-Restless API ========================================================== @jwt_required() def auth_func(**kw): return True apimanager = APIManager(app, flask_sqlalchemy_db=db) apimanager.create_api(SomeStuff, methods=[''GET'', ''POST'', ''DELETE'', ''PUT''], url_prefix=''/api/v1'', collection_name=''free_stuff'', include_columns=[''data1'', ''data2'', ''user_id'']) apimanager.create_api(SomeStuff, methods=[''GET'', ''POST'', ''DELETE'', ''PUT''], url_prefix=''/api/v1'', preprocessors=dict(GET_SINGLE=[auth_func], GET_MANY=[auth_func]), collection_name=''protected_stuff'', include_columns=[''data1'', ''data2'', ''user_id'']) # Create some users to test with @app.before_first_request def create_user(): db.create_all() user_datastore.create_user(email=''test'', password=''test'') user_datastore.create_user(email=''test2'', password=''test2'') ### stuff = SomeStuff(data1=2, data2=''toto'', user_id=1) db.session.add(stuff) stuff = SomeStuff(data1=5, data2=''titi'', user_id=1) db.session.add(stuff) db.session.commit() # Views @app.route(''/'') @login_required def home(): print(request.headers) return render_template(''index.html'') @app.route(''/logout/'') def log_out(): logout_user() return redirect(request.args.get(''next'') or ''/'') if __name__ == ''__main__'': app.run()

Luego, para interactuar con él a través de requests :

>>> import requests, json >>> r=requests.get(''http://127.0.0.1:5000/api/v1/free_stuff'') # this is OK >>> print ''status:'', r.status_code status: 200 >>> r=requests.get(''http://127.0.0.1:5000/api/v1/protected_stuff'') # this should fail >>> print ''status:'', r.status_code status: 401 >>> print r.json() {u''status_code'': 401, u''description'': u''Authorization header was missing'', u''error'': u''Authorization Required''} >>> # Authenticate and retrieve Token >>> r = requests.post(''http://127.0.0.1:5000/auth'', ...: data=json.dumps({''username'': ''test'', ''password'': ''test''}), ...: headers={''content-type'': ''application/json''} ...: ) >>> print ''status:'', r.status_code status: 200 >>> token = r.json()[''token''] >>> # now we have the token, we can navigate to restricted area: >>> r = requests.get(''http://127.0.0.1:5000/api/v1/protected_stuff'', ...: headers={''Authorization'': ''Bearer %s'' % token}) >>> print ''status:'', r.status_code status: 200


Su consulta original (de usar el módulo de solicitudes de Python) me ayudó a despegar :) No hice nada diferente.

No estoy usando Flask-Restless (todavía)

FWIW: Pude obtener el token de autenticación con "solo" Flask-Security (es decir, sin tener que usar Flask-jwt)

Mira aquí para más detalles