diff --git a/auth.py b/auth.py new file mode 100644 index 0000000..678c2e5 --- /dev/null +++ b/auth.py @@ -0,0 +1,54 @@ +from functools import wraps +from flask import ( + request, Response, session, flash, redirect, url_for, abort +) +from settings import app_password, app_user +import random +import string + + +def csrf_token_generator(size=40, chars=string.ascii_uppercase + string.digits): + return ''.join(random.choice(chars) for _ in range(size)) + + +def check_basic_auth(user, passwd): + if user != app_user or passwd != app_password: + return False + else: + return True + + +def authenticate(): + """Sends a 401 response that enables basic auth""" + return Response( + 'Could not verify your access level for that URL.\n' + 'You have to login with proper credentials', 401, + {'WWW-Authenticate': 'Basic realm="Login Required"'} + ) + + +def requires_auth(f): + ''' + REQUEST.json + only gets basic authentication + REQUEST.get + redirect to login page + ''' + @wraps(f) + def decorated(*args, **kwargs): + if request.json: + auth = request.headers.get('Authorization') + if auth.startswith('Basic'): + basic_auth = request.authorization + if not check_basic_auth(basic_auth.username, basic_auth.password): + abort(401) + else: + abort(401) + return f(*args, **kwargs) + + auth = session.get('logged_in') + if not auth: + flash('You are not authorized') + return redirect(url_for('hello_world')) + return f(*args, **kwargs) + return decorated diff --git a/flasky.py b/flasky.py index 4710a66..823c79d 100644 --- a/flasky.py +++ b/flasky.py @@ -1,12 +1,17 @@ -from flask import Flask, make_response, request, current_app, abort +# -*- coding: utf-8 -*- +from flask import ( + Flask, make_response, request, current_app, abort, session, flash, + redirect, url_for, render_template +) from simplejson import dumps from pymongo import MongoClient, DESCENDING # ASCENDING import datetime import dateutil.parser import bson -from settings import mongo_config +from settings import mongo_config, app_password, app_user from datetime import timedelta from functools import update_wrapper +from auth import requires_auth, csrf_token_generator def crossdomain(origin=None, methods=None, headers=None, @@ -51,7 +56,22 @@ def crossdomain(origin=None, methods=None, headers=None, return decorator +def generate_csrf_token(): + if '_csrf_token' not in session: + session['_csrf_token'] = csrf_token_generator() + return session['_csrf_token'] + + app = Flask(__name__) +# Load default config and override config from an environment variable +app.config.update(dict( + DEBUG=True, + SECRET_KEY='development key', + USERNAME=app_user, + PASSWORD=app_password, +)) +app.config.from_envvar('FLASKR_SETTINGS', silent=True) +app.jinja_env.globals['csrf_token'] = generate_csrf_token client = MongoClient(mongo_config) db = client.showtimes @@ -61,26 +81,51 @@ miscObjHandler = lambda obj: ( else str(obj) if isinstance(obj, bson.objectid.ObjectId) else None) +@app.before_request +def csrf_protect(): + ''' + Skip CSRF-token for RESTful service + ref: http://flask.pocoo.org/snippets/3/ + ''' + if request.method == "POST" and not request.json: + token = session.pop('_csrf_token', None) + if not token or token != request.form.get('_csrf_token'): + abort(403) + + +@app.route('/') @app.route('/flask/') @crossdomain(origin='*') def hello_world(): - return 'This comes from Flask ^_^' + # return 'This comes from Flask ^_^' + return render_template('layout.html') -@app.route('/movies/', methods=['GET'], defaults={'lang': 'en'}) -@app.route('/movies//', methods=['GET']) +@app.route('/movies/', methods=['GET'], defaults={'option': 'nowshowing'}) +@app.route('/movies/