Flask - Decorator Login
Getting READY!

Decorators

Flask References:



Sessions

From the tutorial - Sessions:
def login_required(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        if 'logged_in' in session:
            return f(*args, **kwargs)
        flash("You need to login first")
        return redirect(url_for('login_page'))
    return wrap

Notes:

Flash

To use flash, put the following code into the common web page (base.html or index.html, the page that will be extended):

{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul class=flashes>
    {% for message in messages %}
      <li>{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

And you can use this by a simple call to the flash function, for example:

if PWDS.get(n) == None:
   flash ("User name not found")
   return redirect ("/login")

Global

From the flaskr example - g, global:
def login_required(view):
    """View decorator that redirects anonymous users to the login page."""
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            return redirect(url_for("auth.login"))
        return view(**kwargs)
    return wrapped_view
Notes;

Minimal Session Code

Notes about the code below.
  1. Recognized URL's are highlighted in green
    1. / - the home page
    2. /a - a page that is protected by the login_required decorator
    3. /b - this sets the flag that the decorator checks, ie, session['logged_in'] dictionary entry exists
    4. /c - this clears the flag, using session.clear()
  2. s is a string of links, one for each of the pages.
  3. the returns are minimal html strings, which include the page.
    1. the {session} shows the contents of the session dictionary, effectively the current login status
  4. Yellow highlighting - the login_required decorator
    1. DO NOT add parentheses to when used: @login_require, not @login_required()
    2. needs imports: wraps, session, redirect, url_for
    3. session requires app.secret_key
      1. used to encrypt a cookie on the client browser
    4. Logic - in either case, return a string which will be interpreted as the html of a web page, and sent to the browser:
      if 'logged_in' is a key in the session dictionary
          continue
      else
          return string from redirected to 'reply' function
  5. Variables in decorator (yellow)
    1. f - the function after the decorator (eventually if there is more than one decorator)
    2. wraps - another decorator, see Python documention
    3. wrap - NAME of a function, returns the result of f or the redirect
      1. called by the last line of the decorator
    4. session -
      1. automatically created by Flask when a secret key has be specified
      2. each browser will have its own
      3. based on an encrypted cookie in the browser
    5. reply
      1. NAME of a function associated with an @app.route decorator
A minimal program using sessions:
"""
File: lab7flask01.py
VersionC
Date: Oct 9, 2021
Author: Nicholas Duchon
Purpose: Lab 7, flask session, login_required decorator
"""

from functools import wraps
from flask import Flask
from flask import session
from flask import redirect
from flask import url_for

app = Flask (__name__)

app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

def login_required(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        if 'logged_in' in session:
            return f(*args, **kwargs)
        print ("You need to login first")
        return redirect(url_for('reply'))
    return wrap


s = \
"""
<ul>
<li><a href=/ >home</a>
<li><a href=/a>go to a - login required</a>
<li><a href=/b>go to b - login</a>
<li><a href=/c>go to c - logout, login required</a>
</ul>
"""

@app.route ("/")
def reply () :
   return f'route / : session {str(session).replace ("<", "< ")}\n{s}'

@app.route ("/a")
@login_required

def replyA () :
   return f'route /a : session {str(session).replace ("<", "< ")}\n{s}'

@app.route ("/b")
def replyB () :
   session['logged_in'] = True
   return f'route /b : session {str(session).replace ("<", "< ")}\n{s}'

@app.route ("/c")
@login_required

def replyC () :
   session.clear()
   return f'route /c : session {str(session).replace ("<", "< ")}\n{s}'

if __name__ == "__main__":
   app.run (debug=True)

(end program)

Minimal g code

Notes:

  1. g seems to pretty much behave the same as session, IF g is initialized as a dictionary, ie, {}.

"""
File: lab7flask01.py
VersionD
Date: Oct 10, 2021
Author: Nicholas Duchon
Purpose: Lab 7, flask g, login_required decorator
uses flask.g rather than flask.session
"""

from functools import wraps
from flask import Flask
from flask import g
from flask import redirect
from flask import url_for

app = Flask (__name__)

app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

def login_required(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        if 'logged_in' in g:
            return f(*args, **kwargs)
        print ("You need to login first")
        return redirect(url_for('reply'))
    return wrap

s = \
"""
<ul>
<li><a href=/ >home</a>
<li><a href=/a>go to a - login required</a>
<li><a href=/b>go to b - login</a>
<li><a href=/c>go to c - logout, login required</a>
</ul>
"""

g = {}

@app.route ("/")
def reply () :
   return f'route / : g {g}\n{s}'

@app.route ("/a")
@login_required
def replyA () :
   return f'route /a : g {g}\n{s}'

@app.route ("/b")
def replyB () :
   g['logged_in'] = True
   return f'route /b : g {g}\n{s}'

@app.route ("/c")
@login_required
def replyC () :
   g.clear()
   return f'route /c : g {g}\n{s}'

if __name__ == "__main__":
   app.run (debug=True)

(end)


(end)