@@ -251,7 +251,316 @@ def configure_migrations(app):
251251```
252252
253253
254- ## Example 3 from flask-restx
254+ ## Example 3 from flask-login
255+ [ Flask-Login] ( https://github.com/maxcountryman/flask-login )
256+ ([ project documentation] ( https://flask-login.readthedocs.io/en/latest/ )
257+ and [ PyPI package] ( https://pypi.org/project/Flask-Login/ ) )
258+ is a [ Flask] ( /flask.html ) extension that provides user session
259+ management, which handles common tasks such as logging in
260+ and out of a [ web application] ( /web-development.html ) and
261+ managing associated user session data. Flask-Login is
262+ open sourced under the
263+ [ MIT license] ( https://github.com/maxcountryman/flask-login/blob/master/LICENSE ) .
264+
265+ [ ** flask-login / flask_login / utils.py** ] ( https://github.com/maxcountryman/flask-login/blob/master/flask_login/./utils.py )
266+
267+ ``` python
268+ # utils.py
269+ # -*- coding: utf-8 -*-
270+ '''
271+ flask_login.utils
272+ -----------------
273+ General utilities.
274+ '''
275+
276+
277+ import hmac
278+ from hashlib import sha512
279+ from functools import wraps
280+ from werkzeug.local import LocalProxy
281+ from werkzeug.security import safe_str_cmp
282+ from werkzeug.urls import url_decode, url_encode
283+
284+ ~~ from flask import (_request_ctx_stack, current_app, request, session, url_for,
285+ has_request_context)
286+
287+ from ._compat import text_type, urlparse, urlunparse
288+ from .config import COOKIE_NAME , EXEMPT_METHODS
289+ from .signals import user_logged_in, user_logged_out, user_login_confirmed
290+
291+
292+ # : A proxy for the current user. If no user is logged in, this will be an
293+ # : anonymous user
294+ current_user = LocalProxy(lambda : _get_user())
295+
296+
297+ def encode_cookie (payload , key = None ):
298+ '''
299+ This will encode a ``unicode`` value into a cookie, and sign that cookie
300+ with the app's secret key.
301+
302+ :param payload: The value to encode, as `unicode`.
303+ :type payload: unicode
304+
305+ :param key: The key to use when creating the cookie digest. If not
306+ specified, the SECRET_KEY value from app config will be used.
307+ :type key: str
308+ '''
309+
310+
311+ # # ... source file abbreviated to get to request examples ...
312+
313+
314+ l_url = urlparse(login_url)
315+ c_url = urlparse(current_url)
316+
317+ if (not l_url.scheme or l_url.scheme == c_url.scheme) and \
318+ (not l_url.netloc or l_url.netloc == c_url.netloc):
319+ return urlunparse((' ' , ' ' , c_url.path, c_url.params, c_url.query, ' ' ))
320+ return current_url
321+
322+
323+ def expand_login_view (login_view ):
324+ '''
325+ Returns the url for the login view, expanding the view name to a url if
326+ needed.
327+
328+ :param login_view: The name of the login view or a URL for the login view.
329+ :type login_view: str
330+ '''
331+ if login_view.startswith((' https://' , ' http://' , ' /' )):
332+ return login_view
333+ else :
334+ ~~ if request.view_args is None :
335+ return url_for(login_view)
336+ else :
337+ ~~ return url_for(login_view, ** request.view_args)
338+
339+
340+ def login_url (login_view , next_url = None , next_field = ' next' ):
341+ '''
342+ Creates a URL for redirecting to a login page. If only `login_view` is
343+ provided, this will just return the URL for it. If `next_url` is provided,
344+ however, this will append a ``next=URL`` parameter to the query string
345+ so that the login view can redirect back to that URL. Flask-Login's default
346+ unauthorized handler uses this function when redirecting to your login url.
347+ To force the host name used, set `FORCE_HOST_FOR_REDIRECTS` to a host. This
348+ prevents from redirecting to external sites if request headers Host or
349+ X-Forwarded-For are present.
350+
351+ :param login_view: The name of the login view. (Alternately, the actual
352+ URL to the login view.)
353+ :type login_view: str
354+ :param next_url: The URL to give the login view for redirection.
355+ :type next_url: str
356+ :param next_field: What field to store the next URL in. (It defaults to
357+ ``next``.)
358+ :type next_field: str
359+ '''
360+ base = expand_login_view(login_view)
361+
362+
363+
364+ # # ... source file abbreviated to get to request examples ...
365+
366+
367+
368+
369+ def logout_user ():
370+ '''
371+ Logs a user out. (You do not need to pass the actual user.) This will
372+ also clean up the remember me cookie if it exists.
373+ '''
374+
375+ user = _get_user()
376+
377+ if ' _user_id' in session:
378+ session.pop(' _user_id' )
379+
380+ if ' _fresh' in session:
381+ session.pop(' _fresh' )
382+
383+ if ' _id' in session:
384+ session.pop(' _id' )
385+
386+ cookie_name = current_app.config.get(' REMEMBER_COOKIE_NAME' , COOKIE_NAME )
387+ ~~ if cookie_name in request.cookies:
388+ session[' _remember' ] = ' clear'
389+ if ' _remember_seconds' in session:
390+ session.pop(' _remember_seconds' )
391+
392+ user_logged_out.send(current_app._get_current_object(), user = user)
393+
394+ current_app.login_manager._update_request_context_with_user()
395+ return True
396+
397+
398+ def confirm_login ():
399+ '''
400+ This sets the current session as fresh. Sessions become stale when they
401+ are reloaded from a cookie.
402+ '''
403+ session[' _fresh' ] = True
404+ session[' _id' ] = current_app.login_manager._session_identifier_generator()
405+ user_login_confirmed.send(current_app._get_current_object())
406+
407+
408+ def login_required (func ):
409+ '''
410+ If you decorate a view with this, it will ensure that the current user is
411+ logged in and authenticated before calling the actual view. (If they are
412+
413+
414+ ## ... source file abbreviated to get to request examples ...
415+
416+
417+ if not current_user.is_authenticated:
418+ return current_app.login_manager.unauthorized()
419+
420+ ...which is essentially the code that this function adds to your views.
421+
422+ It can be convenient to globally turn off authentication when unit testing.
423+ To enable this, if the application configuration variable `LOGIN_DISABLED`
424+ is set to `True`, this decorator will be ignored.
425+
426+ .. Note ::
427+
428+ Per `W3 guidelines for CORS preflight requests
429+ <http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0>`_,
430+ HTTP ``OPTIONS`` requests are exempt from login checks.
431+
432+ :param func: The view function to decorate.
433+ :type func: function
434+ '''
435+ @wraps (func)
436+ def decorated_view (* args , ** kwargs ):
437+ ~~ if request.method in EXEMPT_METHODS :
438+ return func(* args, ** kwargs)
439+ elif current_app.config.get(' LOGIN_DISABLED' ):
440+ return func(* args, ** kwargs)
441+ elif not current_user.is_authenticated:
442+ return current_app.login_manager.unauthorized()
443+ return func(* args, ** kwargs)
444+ return decorated_view
445+
446+
447+ def fresh_login_required (func ):
448+ '''
449+ If you decorate a view with this, it will ensure that the current user's
450+ login is fresh - i.e. their session was not restored from a 'remember me'
451+ cookie. Sensitive operations, like changing a password or e-mail, should
452+ be protected with this, to impede the efforts of cookie thieves.
453+
454+ If the user is not authenticated, :meth:`LoginManager.unauthorized` is
455+ called as normal. If they are authenticated, but their session is not
456+ fresh, it will call :meth:`LoginManager.needs_refresh` instead. (In that
457+ case, you will need to provide a :attr:`LoginManager.refresh_view`.)
458+
459+ Behaves identically to the :func:`login_required` decorator with respect
460+ to configuration variables.
461+
462+ .. Note ::
463+
464+ Per `W3 guidelines for CORS preflight requests
465+ <http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0>`_,
466+ HTTP ``OPTIONS`` requests are exempt from login checks.
467+
468+ :param func: The view function to decorate.
469+ :type func: function
470+ '''
471+ @wraps (func)
472+ def decorated_view (* args , ** kwargs ):
473+ ~~ if request.method in EXEMPT_METHODS :
474+ return func(* args, ** kwargs)
475+ elif current_app.config.get(' LOGIN_DISABLED' ):
476+ return func(* args, ** kwargs)
477+ elif not current_user.is_authenticated:
478+ return current_app.login_manager.unauthorized()
479+ elif not login_fresh():
480+ return current_app.login_manager.needs_refresh()
481+ return func(* args, ** kwargs)
482+ return decorated_view
483+
484+
485+ def set_login_view (login_view , blueprint = None ):
486+ '''
487+ Sets the login view for the app or blueprint. If a blueprint is passed,
488+ the login view is set for this blueprint on ``blueprint_login_views``.
489+
490+ :param login_view: The user object to log in.
491+ :type login_view: str
492+ :param blueprint: The blueprint which this login view should be set on.
493+ Defaults to ``None``.
494+ :type blueprint: object
495+ '''
496+
497+ num_login_views = len (current_app.login_manager.blueprint_login_views)
498+
499+
500+ # # ... source file abbreviated to get to request examples ...
501+
502+
503+
504+ current_app.login_manager.login_view = None
505+ else :
506+ current_app.login_manager.login_view = login_view
507+
508+
509+ def _get_user ():
510+ if has_request_context() and not hasattr (_request_ctx_stack.top, ' user' ):
511+ current_app.login_manager._load_user()
512+
513+ return getattr (_request_ctx_stack.top, ' user' , None )
514+
515+
516+ def _cookie_digest (payload , key = None ):
517+ key = _secret_key(key)
518+
519+ return hmac.new(key, payload.encode(' utf-8' ), sha512).hexdigest()
520+
521+
522+ def _get_remote_addr ():
523+ ~~ address = request.headers.get(' X-Forwarded-For' , request.remote_addr)
524+ if address is not None :
525+ # An 'X-Forwarded-For' header includes a comma separated list of the
526+ # addresses, the first address being the actual remote address.
527+ address = address.encode(' utf-8' ).split(b ' ,' )[0 ].strip()
528+ return address
529+
530+
531+ def _create_identifier ():
532+ ~~ user_agent = request.headers.get(' User-Agent' )
533+ if user_agent is not None :
534+ user_agent = user_agent.encode(' utf-8' )
535+ base = ' {0} |{1} ' .format(_get_remote_addr(), user_agent)
536+ if str is bytes :
537+ base = text_type(base, ' utf-8' , errors = ' replace' ) # pragma: no cover
538+ h = sha512()
539+ h.update(base.encode(' utf8' ))
540+ return h.hexdigest()
541+
542+
543+ def _user_context_processor ():
544+ return dict (current_user = _get_user())
545+
546+
547+ def _secret_key (key = None ):
548+ if key is None :
549+ key = current_app.config[' SECRET_KEY' ]
550+
551+ if isinstance (key, text_type): # pragma: no cover
552+ key = key.encode(' latin1' ) # ensure bytes
553+
554+ return key
555+
556+
557+ # # ... source file continues with no further request examples...
558+
559+
560+ ```
561+
562+
563+ ## Example 4 from flask-restx
255564[ Flask RESTX] ( https://github.com/python-restx/flask-restx ) is an
256565extension that makes it easier to build
257566[ RESTful APIs] ( /application-programming-interfaces.html ) into
@@ -356,7 +665,7 @@ class marshal_with_field(object):
356665```
357666
358667
359- ## Example 4 from flask-sqlalchemy
668+ ## Example 5 from flask-sqlalchemy
360669[ flask-sqlalchemy] ( https://github.com/pallets/flask-sqlalchemy )
361670([ project documentation] ( https://flask-sqlalchemy.palletsprojects.com/en/2.x/ )
362671and
@@ -482,7 +791,7 @@ def _make_table(db):
482791```
483792
484793
485- ## Example 5 from Flask-WTF
794+ ## Example 6 from Flask-WTF
486795[Flask-WTF](https://github.com/lepture/flask-wtf)
487796([project documentation](https://flask-wtf.readthedocs.io/en/stable/)
488797and
@@ -644,7 +953,7 @@ def generate_csrf(secret_key=None, token_key=None):
644953```
645954
646955
647- ## Example 6 from flaskSaaS
956+ ## Example 7 from flaskSaaS
648957[flaskSaas](https://github.com/alectrocute/flaskSaaS) is a boilerplate
649958starter project to build a software-as-a-service (SaaS) web application
650959in [Flask](/flask.html), with [Stripe](/stripe.html) for billing. The
@@ -697,7 +1006,7 @@ admin.add_view(FileAdmin(path, '/static/', name='Static'))
6971006```
6981007
6991008
700- ## Example 7 from newspie
1009+ ## Example 8 from newspie
7011010[NewsPie](https://github.com/skamieniarz/newspie) is a minimalistic news
7021011aggregator created with [Flask](/flask.html) and the
7031012[News API](https://newsapi.org/).
@@ -906,7 +1215,7 @@ if __name__ == '__main__':
9061215```
9071216
9081217
909- ## Example 8 from sandman2
1218+ ## Example 9 from sandman2
9101219[sandman2](https://github.com/jeffknupp/sandman2)
9111220([project documentation](https://sandman2.readthedocs.io/en/latest/)
9121221and
0 commit comments