Как использовать oidc (объект OpenIDConnect) в flask.Blueprint при использовании flask.create_app? - PullRequest
0 голосов
/ 04 июня 2019

Я хочу использовать @oidc.require_login для перенаправления запроса на вход в okta.Я получаю ошибку AttributeError: '_AppCtxGlobals' object has no attribute 'oidc_id_token', которую не могу устранить

1) Я создал приложение на основе учебника Flaskr , в котором демонстрируется использование метода изготовления колб create_app.

2) Я создал класс okta.py следующим образом:

from oauth2client.client import OAuth2Credentials
from flask_oidc import OpenIDConnect
from okta import UsersClient

import click
from flask import current_app, g, session
from flask.cli import with_appcontext

oidc = OpenIDConnect()


def get_oidc():
    """
    Connect to okta
    """
    if 'oidc' not in g:
        print('okta: get_oidc call')
        g.oidc = OpenIDConnect(current_app)
        g.okta_client = UsersClient("<okta-server-url>", "<secret>")
        # fixing global oidc problem for decorator in rooms
        oidc = g.oidc
    return g.oidc


def close_oidc(app):
    """ Release okta connection
    """
    oidc = g.pop('oidc',None)
    if oidc is not None:
        oidc.logout()

    # with app.app_context():
    #     session.clear()


def init_okta():
    """Connect to existing table"""
    oidc = get_oidc()
    """ Can do additional initialization if required """


@click.command('init-okta')
@with_appcontext
def init_okta_command():
    """Connect to existing oidc"""
    init_okta()
    click.echo (get_oidc())
    click.echo('Initialized Okta.')
    print('Initialized Okta.')


def init_app(app):
    """Register okta functions with the Flask app. This is called by
    the application factory.
    """
    app.teardown_appcontext(close_oidc)
    app.cli.add_command(init_okta_command)

3) Моя цель - использовать okta login для просмотра списков комнат

from flask import (
    Blueprint, flash, g, redirect, render_template, 
    request, url_for, current_app, session, jsonify
)
from werkzeug.exceptions import abort

...
from hotel.okta import oidc, get_oidc, init_app

bp = Blueprint('rooms', __name__)
...
@bp.route('/login', methods=['GET', 'POST'])
@oidc.require_login
def login():
    """
    Force the user to login, then redirect them to the get_books.
    Currently this code DOES NOT work
    Problem:
        * oidc global object is not available to pass request to okta
    Resolution:
        * redirecting to rooms.calendar
    """
    # info = oidc.user_getinfo(['preferred_username', 'email', 'sub'])
    # id_token = OAuth2Credentials.from_json(oidc.credentials_store[info.get('sub')]).token_response['id_token']

    return redirect(url_for("rooms.calendar"))

4) Мой __init__.py выглядит следующим образом

import os

from flask import Flask
from flask_oidc import OpenIDConnect
from okta import UsersClient

# This is a factory method for productive deployment
# Use app specific configuration
# For any app local files, use /instnce Folder
def create_app(test_config=None):
    """Create and configure an instance of the Flask application."""
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_mapping(
        # a default secret that should be overridden by instance config
        SECRET_KEY='dev',
        # store the database in the instance folder
        DATABASE=os.path.join(app.instance_path, 'hotel.sqlite'),
        OIDC_CLIENT_SECRETS=os.path.join(app.instance_path, 'client_secrets.json'),
        OIDC_COOKIE_SECURE=False,
        OIDC_CALLBACK_ROUTE= '/oidc/callback',
        OIDC_SCOPES=["openid", "email", "profile"],
        OIDC_ID_TOKEN_COOKIE_NAME = 'oidc_token',
    )

    if test_config is None:
        # load the instance config, if it exists, when not testing
        app.config.from_pyfile('config.py', silent=True)
    else:
        # load the test config if passed in
        app.config.update(test_config)

    # ensure the instance folder exists
    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    # # register the database commands
    from hotel import db
    db.init_app(app)

    # apply the blueprints to the app
    from hotel import rooms
    app.register_blueprint(rooms.bp)

    # for Okta
    # Ref: https://www.fullstackpython.com/blog/add-user-authentication-flask-apps-okta.html

    from hotel import okta
    with app.app_context():
        okta.init_app(app)



    @app.route('/hello') # For testing factory method
    def hello():
        return 'Hello, World!'


    # make url_for('index') == url_for('blog.index')
    # in another app, you might define a separate main index here with
    # app.route, while giving the blog blueprint a url_prefix, but for
    # the tutorial the blog will be the main index
    app.add_url_rule('/', endpoint='index')

    return app

5) Вот фрагмент кода rooms.before_request

@bp.before_request
def before_request():
    print ('rooms.before_request call reached')
    with current_app.app_context():
        print ('rooms.before_request in app_context',g)
        oidc = g.pop('oidc',None)
        okta_client = g.pop('okta_client',None)
        if oidc is not None and okta_client is not None:
            print ('rooms.before_request g.oidc and g.okta_client available')
            if oidc.user_loggedin:
                # OpenID Token as 
                g.user = okta_client.get_user(oidc.user_getfield("sub"))
                g.oidc_id_token = OAuth2Credentials.from_json(g.oidc.credentials_store[info.get('sub')]).token_response['id_token']
            else:
                g.user = None
        else:
            print('rooms.beforerequest No user logged in')
            g.user = None

Мой анализ:

  • *Ожидается, что 1028 * будет использовать client_secrets.json в папке /instance для подключения к Okta.
  • Чтобы заставить @oidc.require_login работать, я выставил oidc в okta.get_oidc и импортировал это в мой код rooms.py
  • Однако это не работает :(! StackСлед:
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2328, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2314, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1760, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/_compat.py", line 36, in reraise
    raise value
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2311, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1834, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1737, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/_compat.py", line 36, in reraise
    raise value
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1832, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1818, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask_oidc/__init__.py", line 485, in decorated
    if g.oidc_id_token is None:
  File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/werkzeug/local.py", line 348, in __getattr__
    return getattr(self._get_current_object(), name)
AttributeError: '_AppCtxGlobals' object has no attribute 'oidc_id_token'

Мои вопросы:

  1. Как я могу позвонить @oidc.require_login в методе фабрики приложений колбу?
  2. Это способ, которым яИнициализировать правильное соединение okta?
  3. В любом случае можно ли вручную вызывать внешний сервер авторизации без использования декоратора? Как?
  4. Является ли установка переменных стека колбы лучшим способом продвижения вперед? Если да, то как?
  5. Попытка установить g в before_request, похоже, не работает. Какие еще варианты работают в подходе Flask's App Factory?

Любые идеи будут высоко оценены!

Спасибо! Юва

...