Skip to content

Commit 334b206

Browse files
committed
add flask-login to flask extensions examples
1 parent f978222 commit 334b206

File tree

6 files changed

+850
-13
lines changed

6 files changed

+850
-13
lines changed

content/pages/examples/flask/flask-extensions-plug-ins.markdown

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ flask-base's code is open sourced
5555
[under the MIT license](https://github.com/hack4impact/flask-base/blob/master/LICENSE.md).
5656

5757

58+
### Flask-Login
59+
[Flask-Login](https://github.com/maxcountryman/flask-login)
60+
([project documentation](https://flask-login.readthedocs.io/en/latest/)
61+
and [PyPI package](https://pypi.org/project/Flask-Login/))
62+
is a [Flask](/flask.html) extension that provides user session
63+
management, which handles common tasks such as logging in
64+
and out of a [web application](/web-development.html) and
65+
managing associated user session data. Flask-Login is
66+
open sourced under the
67+
[MIT license](https://github.com/maxcountryman/flask-login/blob/master/LICENSE).
68+
69+
5870
### Flask RESTX
5971
[Flask RESTX](https://github.com/python-restx/flask-restx) is an
6072
extension that makes it easier to build

content/pages/examples/flask/flask-globals-request.markdown

Lines changed: 315 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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
256565
extension 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/)
362671
and
@@ -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/)
488797
and
@@ -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
649958
starter project to build a software-as-a-service (SaaS) web application
650959
in [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
7021011
aggregator 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/)
9121221
and

0 commit comments

Comments
 (0)