PKKI[!!flask_simplelogin/__init__.py"""Flask Simple Login - Login Extension for Flask""" __version__ = '0.0.2' import logging import os from functools import wraps from uuid import uuid4 from flask import (Blueprint, current_app, flash, redirect, render_template, request, session, url_for) from flask_wtf import FlaskForm from wtforms import PasswordField, StringField from wtforms.validators import DataRequired logger = logging.getLogger(__name__) class LoginForm(FlaskForm): "Default login form" username = StringField('name', validators=[DataRequired()]) password = PasswordField('password', validators=[DataRequired()]) def default_login_checker(user): """user must be a dictionary here default is checking username/password if login is ok returns True else False :param user: dict {'username':'', 'password': ''} """ username = user.get('username') password = user.get('password') the_username = os.environ.get( 'SIMPLELOGIN_USERNAME', current_app.config.get('SIMPLELOGIN_USERNAME', 'admin') ) the_password = os.environ.get( 'SIMPLELOGIN_PASSWORD', current_app.config.get('SIMPLELOGIN_PASSWORD', 'secret') ) if username == the_username and password == the_password: return True return False def is_logged_in(username=None): """Checks if user is logged in if `username` is passed check if specified user is logged in username can be a list""" if username: if not isinstance(username, (list, tuple)): username = [username] return 'simple_logged_in' in session and get_username() in username return 'simple_logged_in' in session def get_username(): """Get current logged in username""" return session.get('simple_username') def login_required(function=None, username=None, basic=False, must=None): """Decorate views to require login @login_required @login_required() @login_required(username='admin') @login_required(username=['admin', 'jon']) @login_required(basic=True) @login_required(must=[function, another_function]) """ if function and not callable(function): raise ValueError( 'Decorator receives only named arguments, ' 'try login_required(username="foo")' ) def check(validators): """Return in the first validation error, else return None""" if validators is None: return if not isinstance(validators, (list, tuple)): validators = [validators] for validator in validators: error = validator(get_username()) if error is not None: return 'Authentication Error: {0}'.format(error), 403 def dispatch(fun, *args, **kwargs): if basic and request.is_json: return dispatch_basic_auth(fun, *args, **kwargs) if is_logged_in(username=username): return check(must) or fun(*args, **kwargs) elif is_logged_in(): return 'Access Denied', 403 else: flash("You need to login first", 'danger') return redirect( url_for('simplelogin.login', next=request.path) ) def dispatch_basic_auth(fun, *args, **kwargs): simplelogin = current_app.extensions['simplelogin'] auth_response = simplelogin.basic_auth() if auth_response is True: return check(must) or fun(*args, **kwargs) else: return auth_response @wraps(function) def simple_decorator(*args, **kwargs): """This is for when decorator is @login_required""" return dispatch(function, *args, **kwargs) if function: return simple_decorator def decorator(f): """This is for when decorator is @login_required(...)""" @wraps(f) def wrap(*args, **kwargs): return dispatch(f, *args, **kwargs) return wrap return decorator class SimpleLogin(object): """Simple Flask Login""" def __init__(self, app=None, login_checker=None, login_form=None): self.config = { 'blueprint': 'simplelogin', 'login_url': '/login/', 'logout_url': '/logout/', 'home_url': '/' } self.app = None self._login_checker = login_checker or default_login_checker self._login_form = login_form or LoginForm if app is not None: self.init_app( app=app, login_checker=login_checker, login_form=login_form ) def login_checker(self, f): """To set login_checker as decorator: @simple.login_checher def foo(user): ... """ self._login_checker = f return f def init_app(self, app, login_checker=None, login_form=None): if login_checker: self._login_checker = login_checker if login_form: self._login_form = login_form self._register(app) self._load_config() self._set_default_secret() self._register_views() self._register_extras() def _register(self, app): if not hasattr(app, 'extensions'): app.extensions = {} if 'simplelogin' in app.extensions: raise RuntimeError("Flask extension already initialized") app.extensions['simplelogin'] = self self.app = app def _load_config(self): self.config.update( self.app.config.get_namespace( namespace='SIMPLE_LOGIN_', lowercase=True, trim_namespace=True ) ) def _set_default_secret(self): if self.app.config.get('SECRET_KEY') is None: secret_key = str(uuid4()) logger.warning(( 'Using random SECRET_KEY: {0}, ' 'please set it on your app.config["SECRET_KEY"]' ).format(secret_key)) self.app.config['SECRET_KEY'] = secret_key def _register_views(self): self.blueprint = Blueprint( self.config['blueprint'], __name__, template_folder='templates' ) self.blueprint.add_url_rule( self.config['login_url'], endpoint='login', view_func=self.login, methods=['GET', 'POST'] ) self.blueprint.add_url_rule( self.config['logout_url'], endpoint='logout', view_func=self.logout, methods=['GET'] ) self.app.register_blueprint(self.blueprint) def _register_extras(self): self.app.add_template_global(is_logged_in) self.app.add_template_global(get_username) def basic_auth(self, response=None): """Support basic_auth via /login or login_required(basic=True)""" auth = request.authorization if auth and self._login_checker({'username': auth.username, 'password': auth.password}): session['simple_logged_in'] = True session['simple_basic_auth'] = True session['simple_username'] = auth.username return response or True else: headers = {'WWW-Authenticate': 'Basic realm="Login Required"'} return 'Invalid credentials', 401, headers def login(self): destiny = request.args.get( 'next', default=request.form.get( 'next', default=self.config.get('home_url', '/') ) ) if is_logged_in(): flash('already logged in', 'primary') return redirect(destiny) if request.is_json: # recommended to use `login_required(basic=True)` instead this return self.basic_auth(destiny=redirect(destiny)) form = self._login_form() ret_code = 200 if form.validate_on_submit(): if self._login_checker(form.data): flash("login success!!", 'success') session['simple_logged_in'] = True session['simple_username'] = form.data.get('username') return redirect(destiny) else: flash('invalid credentials', 'danger') ret_code = 401 # <-- invalid credentials RFC7235 return render_template('login.html', form=form, next=destiny), ret_code def logout(self): session.clear() flash('Logged out!', 'primary') return redirect(self.config.get('home_url', '/')) PKK>;<<&flask_simplelogin/templates/login.html Flask Login {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% else %} {% endif %} {% endwith %} {%if form.errors %} {% endif %}
{{ form.csrf_token }} {{form.username.label}}
{{ form.username }}

{{form.password.label}}
{{ form.password }}

{% if next %}{% endif %} PKK,I,,)flask_simplelogin-0.0.2.dist-info/LICENSEMIT License Copyright (c) 2017 Bruno Rocha Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!H١Wd'flask_simplelogin-0.0.2.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,Q0343 /, (-JLR()*M ILR(4KM̫#DPK!H_& &*flask_simplelogin-0.0.2.dist-info/METADATAZms6_4wכXIĹOėd$$$5{vARԋNIdb tr\vިj7#Tƪ'&C4RīAv>c-{9Qs/TtJhz?XuR9)DziYfcL L؛r$ 猿߮8NPpjLL]A)Np'Q2yglyvśy7l f bScB!Pd 30iVVFJFbK 5FM@'j/Ҿ+bil'h=#Ĉ ;Nҥx?S2j pXwyjjz7ٴN`p$)6wұ lO FEKsma<`nW'97b1[b'fʄ:W3rvہL͇d.l 9[cneI8%.i/3ωl%'Tp)> TZTOɼJ38v,M Cшb,U6C,ώ-5CChTq{yw>xu׃3ıH4gx H(b`ܛ$ ̥Jb90 "A1%yfd/d$Sd"dc@/*fdM?뭟sY &6 d#˜cǮ;AGT-R 'Ϲ=,/pl}Ϋslej8s"mjj3iZ+Ӧk|&&>#`e5Qa>-0IHy3F2+~Qaf52o4*Z*9cgo:"}Ad=D^m'z:Hn C *dJǹ9&_ 1Mr.F琌GO_ ?my͎Ses{ً/s|:|}~ f g/_=se6L ιD;IkibfWJf֯+01s@䴴C>}3])s߉y\0ɚ0SљDt&t}{ĂdɜǏ.dx' dYU5ϟHK$ZθXCE1)}F/Y= %\6{91/%4ҫ̚טZ)r jW'9eWrMͳD kqQ,6hȮDf<`O,g+;5X6/恥/[`#D&IԺ;Y495oD*avD eTTwߝN#|{I H)u+UUYRrR{ݫJ7&^CR)"TVc퐪qBl( ;"5^wK7OnUSyw=떇.B84D!*z|! #:?a<K}4#82_LZT2Uz`MҨ[9ryod:ET^ĘMuK=YoE"rW58C@]qVZ~5NVI ~oCJ *;wF5VE5L,.FRYB(6|Ue6]"I}8S'Ѧ v5%N%5`@==zQڒnrdnUN[DIJTX,#>JF#MJ4vkmbU<)\cڥu.9ќ$tKr>y2׫W[ 22J!#Q;!U7Yq]X`cUN}im-DOR L&^9a<֨W$jV*1 9H,0$\YnAMZ-LCCɗT2k=b v[J[W{OZdwKY$}gGUuQ6,U..gCqW#yOn1vhr+LVJjW+<3Cb˓}?Wow] PI:o͚vXLbI_ xnվ޳aA]Trs^M- ly2if/X<#Ǎqg6Uhmōêdex̍HLj2fW5SNV9X[I~uM)NHr4^Ƶl&wW'l¹D )jp͸3G jHP lo9")@4뇃sAx$ga$ArVi;Ī2$vu\n?<j\\~=PE ǼNqʹ+\\i6ATy rovtot?[a~SW'u\^h; j U&R"X5E;:qUeNlh >ݐXqSBߛr5YhE fܑ.Ad[5N6w;^CCШ*<PԈTLnE7V6p6s=l1$ 0PKKI[!!flask_simplelogin/__init__.pyPKK>;<<&!flask_simplelogin/templates/login.htmlPKK,I,,)N(flask_simplelogin-0.0.2.dist-info/LICENSEPK!H١Wd',flask_simplelogin-0.0.2.dist-info/WHEELPK!H_& &*]-flask_simplelogin-0.0.2.dist-info/METADATAPK!H{(1<(:flask_simplelogin-0.0.2.dist-info/RECORDPKM<