change to hydra as oauth backend
This commit is contained in:
parent
157bf65635
commit
38932aef44
|
@ -6,6 +6,7 @@ PREFERRED_URL_SCHEME = 'https'
|
|||
DATA_FOLDER = "./data"
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = f'sqlite:///{DATA_FOLDER}/db.sqlite'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS=False
|
||||
|
||||
LDAP_URL = 'ldaps://ldap.example.org'
|
||||
LDAP_BASE_DN = 'dc=example,dc=com'
|
||||
|
@ -17,6 +18,15 @@ PKI_PATH = f'{DATA_FOLDER}/pki'
|
|||
DOMAIN = 'example.com'
|
||||
SERVER_NAME = f'account.{ DOMAIN }:9090'
|
||||
|
||||
HYDRA_REQUEST_TIMEOUT_SECONDS = 3
|
||||
HYDRA_ADMIN_URL = 'http://127.0.0.1:4445'
|
||||
HYDRA_PUBLIC_URL = 'http://127.0.0.1:4444'
|
||||
SUBJECT_PREFIX = 'something random'
|
||||
|
||||
OAUTH_ID = 'identiy_provider'
|
||||
OAUTH_SECRET = 'ThisIsNotSafe'
|
||||
|
||||
|
||||
|
||||
LENTICULAR_CLOUD_SERVICES = {
|
||||
'jabber': {
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
from flask.app import Flask
|
||||
from flask import g, redirect
|
||||
from flask import g, redirect, request
|
||||
from flask.helpers import url_for
|
||||
from jwkest.jwk import RSAKey, rsa_load
|
||||
from flask_babel import Babel
|
||||
from flask_login import LoginManager
|
||||
import time
|
||||
import subprocess
|
||||
import ory_hydra_client as hydra
|
||||
|
||||
from pyop.authz_state import AuthorizationState
|
||||
from pyop.provider import Provider
|
||||
from pyop.subject_identifier import HashBasedSubjectIdentifierFactory
|
||||
from pyop.userinfo import Userinfo as _Userinfo
|
||||
from ldap3 import Connection, Server, ALL
|
||||
|
||||
from . import model
|
||||
|
@ -23,112 +20,13 @@ def get_git_hash():
|
|||
except Exception:
|
||||
return ''
|
||||
|
||||
def init_oidc_provider(app):
|
||||
with app.app_context():
|
||||
issuer = url_for('frontend.index')[:-1]
|
||||
authentication_endpoint = url_for('oidc_provider.authentication_endpoint')
|
||||
jwks_uri = url_for('oidc_provider.jwks_uri')
|
||||
token_endpoint = url_for('oidc_provider.token_endpoint')
|
||||
userinfo_endpoint = url_for('oidc_provider.userinfo_endpoint')
|
||||
registration_endpoint = url_for('oidc_provider.registration_endpoint')
|
||||
end_session_endpoint = url_for('auth.logout')
|
||||
|
||||
configuration_information = {
|
||||
'issuer': issuer,
|
||||
'authorization_endpoint': authentication_endpoint,
|
||||
'jwks_uri': jwks_uri,
|
||||
'token_endpoint': token_endpoint,
|
||||
'userinfo_endpoint': userinfo_endpoint,
|
||||
'registration_endpoint': registration_endpoint,
|
||||
'end_session_endpoint': end_session_endpoint,
|
||||
'scopes_supported': ['openid', 'profile'],
|
||||
'response_types_supported': ['code', 'code id_token', 'code token', 'code id_token token'], # code and hybrid
|
||||
'response_modes_supported': ['query', 'fragment'],
|
||||
'grant_types_supported': ['authorization_code', 'implicit'],
|
||||
'subject_types_supported': ['pairwise'],
|
||||
'token_endpoint_auth_methods_supported': ['client_secret_basic', 'client_secret_post'],
|
||||
'claims_parameter_supported': True
|
||||
}
|
||||
def init_oauth2(app):
|
||||
pass
|
||||
|
||||
from .model_db import db, Client, AuthzCode, AccessToken, RefreshToken, SubjectIdentifier
|
||||
from .model import User
|
||||
import json
|
||||
db.init_app(app)
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
class SqlAlchemyWrapper(object):
|
||||
def __init__(self, cls):
|
||||
self._cls = cls
|
||||
pass
|
||||
|
||||
def __getitem__(self, item):
|
||||
o = self._cls.query.get(item)
|
||||
if o is not None:
|
||||
return json.loads(o.value)
|
||||
else:
|
||||
raise KeyError()
|
||||
|
||||
def __setitem__(self, item, value):
|
||||
o = self._cls.query.get(item)
|
||||
if o is None:
|
||||
o = self._cls(key=item)
|
||||
db.session.add(o)
|
||||
o.value = json.dumps(value)
|
||||
db.session.commit()
|
||||
|
||||
def items(self):
|
||||
aa = self._cls.query.all()
|
||||
return [(a.key, json.loads(a.value)) for a in aa]
|
||||
|
||||
def __contains__(self, item):
|
||||
return self._cls.query.get(item) is not None
|
||||
|
||||
class Userinfo(_Userinfo):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __getitem__(self, item):
|
||||
return User.query().by_username(item)
|
||||
|
||||
def __contains__(self, item):
|
||||
return User.query().by_username(item) is not None
|
||||
|
||||
def get_claims_for(self, user_id, requested_claims):
|
||||
user = self[user_id]
|
||||
print(f'user {user.username} {requested_claims}')
|
||||
claims = {}
|
||||
for claim in requested_claims:
|
||||
if claim == 'name':
|
||||
claims[claim] = str(user.username)
|
||||
elif claim == 'email':
|
||||
claims[claim] = str(user.mail)
|
||||
elif claim == 'email_verified':
|
||||
claims[claim] = True
|
||||
else:
|
||||
print(f'claim not found {claim}')
|
||||
return claims
|
||||
|
||||
client_db = SqlAlchemyWrapper(Client)
|
||||
|
||||
userinfo_db = Userinfo()
|
||||
signing_key = RSAKey(key=rsa_load('signing_key.pem'), alg='RS256')
|
||||
provider = Provider(
|
||||
signing_key,
|
||||
configuration_information,
|
||||
AuthorizationState(
|
||||
HashBasedSubjectIdentifierFactory(app.config['SUBJECT_ID_HASH_SALT']),
|
||||
SqlAlchemyWrapper(AuthzCode),
|
||||
SqlAlchemyWrapper(AccessToken),
|
||||
SqlAlchemyWrapper(RefreshToken),
|
||||
SqlAlchemyWrapper(SubjectIdentifier)
|
||||
),
|
||||
client_db,
|
||||
userinfo_db)
|
||||
|
||||
return provider
|
||||
|
||||
def oidc_provider_init_app(name=None):
|
||||
def init_app(name=None):
|
||||
name = name or __name__
|
||||
app = Flask(name)
|
||||
app.config.from_pyfile('application.cfg')
|
||||
|
@ -142,23 +40,31 @@ def oidc_provider_init_app(name=None):
|
|||
model.ldap_conn = app.ldap_conn
|
||||
model.base_dn = app.config['LDAP_BASE_DN']
|
||||
|
||||
from .model_db import db
|
||||
db.init_app(app)
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
app.babel = Babel(app)
|
||||
init_oauth2(app)
|
||||
app.login_manager = LoginManager(app)
|
||||
|
||||
from .views import oidc_provider_views, auth_views, frontend_views, init_login_manager
|
||||
#init hydra admin api
|
||||
hydra_config = hydra.Configuration(app.config['HYDRA_ADMIN_URL'])
|
||||
hydra_client = hydra.ApiClient(hydra_config)
|
||||
app.hydra_api = hydra.AdminApi(hydra_client)
|
||||
|
||||
from .views import auth_views, frontend_views, init_login_manager, api_views
|
||||
init_login_manager(app)
|
||||
app.register_blueprint(oidc_provider_views)
|
||||
app.register_blueprint(auth_views)
|
||||
app.register_blueprint(frontend_views)
|
||||
app.register_blueprint(api_views)
|
||||
|
||||
@app.before_request
|
||||
def befor_request():
|
||||
request_start_time = time.time()
|
||||
g.request_time = lambda: "%.5fs" % (time.time() - request_start_time)
|
||||
|
||||
# Initialize the oidc_provider after views to be able to set correct urls
|
||||
app.provider = init_oidc_provider(app)
|
||||
|
||||
from .translations import init_babel
|
||||
|
||||
init_babel(app)
|
||||
|
|
|
@ -32,8 +32,12 @@ class LdapAuthProvider(AuthProvider):
|
|||
|
||||
@staticmethod
|
||||
def check_auth(user, form):
|
||||
return LdapAuthProvider.check_auth_internal(user, form.data['password'])
|
||||
|
||||
@staticmethod
|
||||
def check_auth_internal(user, password):
|
||||
server = Server(current_app.config['LDAP_URL'])
|
||||
ldap_conn = Connection(server, user.entry_dn, form.data['password'])
|
||||
ldap_conn = Connection(server, user.entry_dn, password)
|
||||
try:
|
||||
return ldap_conn.bind()
|
||||
except LDAPException:
|
||||
|
|
|
@ -80,6 +80,9 @@ class EntryBase(object):
|
|||
def __init__(self, clazz):
|
||||
self._class = clazz
|
||||
|
||||
def _mapping(self, ldap_object):
|
||||
return ldap_object
|
||||
|
||||
def _query(self, ldap_filter: str):
|
||||
reader = Reader(ldap_conn, self._class.get_object_def(), self._class.get_base(), ldap_filter)
|
||||
try:
|
||||
|
@ -87,7 +90,7 @@ class EntryBase(object):
|
|||
except LDAPSessionTerminatedByServerError:
|
||||
ldap_conn.bind()
|
||||
reader.search()
|
||||
return list(reader)
|
||||
return [self._mapping(entry) for entry in reader]
|
||||
|
||||
def all(self):
|
||||
return self._query(None)
|
||||
|
@ -277,7 +280,7 @@ class User(EntryBase):
|
|||
return self._ldap_object.surname
|
||||
|
||||
@property
|
||||
def mail(self):
|
||||
def email(self):
|
||||
return self._ldap_object.mail
|
||||
|
||||
@property
|
||||
|
@ -297,10 +300,14 @@ class User(EntryBase):
|
|||
return self._totp_list
|
||||
|
||||
class _query(EntryBase._query):
|
||||
|
||||
def _mapping(self, ldap_object):
|
||||
return User(ldap_object=ldap_object)
|
||||
|
||||
def by_username(self, username) -> 'User':
|
||||
result = self._query('(uid={username:s})'.format(username=escape_filter_chars(username)))
|
||||
if len(result) > 0:
|
||||
return User(result[0])
|
||||
return result[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
from flask_sqlalchemy import SQLAlchemy, orm
|
||||
import uuid
|
||||
|
||||
db = SQLAlchemy() # type: SQLAlchemy
|
||||
|
||||
|
||||
def generate_uuid():
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
class OAuth(db.Model):
|
||||
token = db.Column(db.Text, primary_key=True)
|
||||
provider = db.Column(db.Text)
|
||||
provider_username = db.Column(db.Text)
|
||||
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.String(length=36), primary_key=True, default=generate_uuid)
|
||||
username = db.Column(db.String, unique=True)
|
||||
|
||||
|
||||
class Client(db.Model):
|
||||
key = db.Column(db.Text, primary_key=True)
|
||||
value = db.Column(db.Text)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# pylint: disable=unused-import
|
||||
|
||||
from .oidc import oidc_provider_views
|
||||
from .auth import auth_views, init_login_manager
|
||||
from .frontend import frontend_views
|
||||
from .auth import auth_views
|
||||
from .frontend import frontend_views, init_login_manager
|
||||
from .api import api_views
|
||||
|
|
65
lenticular_cloud/views/api.py
Normal file
65
lenticular_cloud/views/api.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import flask
|
||||
from flask import Blueprint, redirect, request
|
||||
from flask import current_app, session
|
||||
from flask import jsonify
|
||||
from flask.helpers import make_response
|
||||
from flask.templating import render_template
|
||||
from oic.oic.message import TokenErrorResponse, UserInfoErrorResponse, EndSessionRequest
|
||||
|
||||
from pyop.access_token import AccessToken, BearerTokenError
|
||||
from pyop.exceptions import InvalidAuthenticationRequest, InvalidAccessToken, InvalidClientAuthentication, OAuthError, \
|
||||
InvalidSubjectIdentifier, InvalidClientRegistrationRequest
|
||||
from pyop.util import should_fragment_encode
|
||||
|
||||
from flask import Blueprint, render_template, request, url_for
|
||||
from flask_login import login_required, login_user, logout_user
|
||||
from werkzeug.utils import redirect
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
from base64 import b64decode, b64encode
|
||||
import ory_hydra_client as hydra
|
||||
from requests_oauthlib.oauth2_session import OAuth2Session
|
||||
import requests
|
||||
|
||||
from ..model import User, SecurityUser
|
||||
from ..model_db import User as DbUser
|
||||
from ..form.login import LoginForm
|
||||
from ..auth_providers import LdapAuthProvider
|
||||
|
||||
|
||||
api_views = Blueprint('api', __name__, url_prefix='/api')
|
||||
|
||||
@api_views.route('/userinfo', methods=['GET', 'POST'])
|
||||
def userinfo():
|
||||
token = request.headers['authorization'].replace('Bearer ', '')
|
||||
token_info = current_app.hydra_api.introspect_o_auth2_token(token=token)
|
||||
|
||||
user_db = DbUser.query.get(token_info.sub)
|
||||
user = User.query().by_username(user_db.username)
|
||||
|
||||
r = requests.get(
|
||||
"http://127.0.0.1:4444/userinfo",
|
||||
headers={
|
||||
'authorization': request.headers['authorization']})
|
||||
userinfo = r.json()
|
||||
scopes = token_info.scope.split(' ')
|
||||
if 'email' in scopes:
|
||||
userinfo['email'] = str(user.email)
|
||||
if 'profile' in scopes:
|
||||
userinfo['username'] = str(user.username)
|
||||
print(userinfo)
|
||||
return jsonify(userinfo)
|
||||
|
||||
|
||||
@api_views.route('/users', methods=['GET'])
|
||||
def user_list():
|
||||
if 'authorization' not in request.headers:
|
||||
return '', 403
|
||||
token = request.headers['authorization'].replace('Bearer ', '')
|
||||
token_info = current_app.hydra_api.introspect_o_auth2_token(token=token)
|
||||
|
||||
if 'lc_i_userlist' not in token_info.scope.split(' '):
|
||||
return '', 403
|
||||
|
||||
return jsonify([{'username': str(user.username), 'email': str(user.email)}
|
||||
for user in User.query().all()])
|
|
@ -20,31 +20,44 @@ from werkzeug.utils import redirect
|
|||
import logging
|
||||
from urllib.parse import urlparse
|
||||
from base64 import b64decode, b64encode
|
||||
import http
|
||||
|
||||
from ..model import User, SecurityUser
|
||||
from ..model_db import db, User as DbUser
|
||||
from ..form.login import LoginForm
|
||||
from ..auth_providers import AUTH_PROVIDER_LIST
|
||||
from .oidc import do_logout
|
||||
|
||||
|
||||
auth_views = Blueprint('auth', __name__, url_prefix='')
|
||||
auth_views = Blueprint('auth', __name__, url_prefix='/auth')
|
||||
|
||||
@auth_views.route('/consent', methods=['GET', 'POST'])
|
||||
def consent():
|
||||
"""Always grant consent."""
|
||||
# DUMMPY ONLY
|
||||
|
||||
def init_login_manager(app):
|
||||
@app.login_manager.user_loader
|
||||
def user_loader(username):
|
||||
return User.query().by_username(username)
|
||||
remember_me = True
|
||||
remember_for = 60*60*24*7 # remember for 7 days
|
||||
|
||||
@app.login_manager.request_loader
|
||||
def request_loader(request):
|
||||
pass
|
||||
|
||||
@app.login_manager.unauthorized_handler
|
||||
def unauthorized():
|
||||
return redirect(url_for('auth.login', next=b64encode(request.url.encode())))
|
||||
consent_request = current_app.hydra_api.get_consent_request(request.args['consent_challenge'])
|
||||
requested_scope = consent_request.requested_scope
|
||||
resp = current_app.hydra_api.accept_consent_request(consent_request.challenge, body={
|
||||
'grant_scope': requested_scope,
|
||||
'remember': remember_me,
|
||||
'remember_for': remember_for,
|
||||
})
|
||||
return redirect(resp.redirect_to)
|
||||
|
||||
@auth_views.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
login_challenge = request.args.get('login_challenge')
|
||||
login_request = current_app.hydra_api.get_login_request(login_challenge)
|
||||
|
||||
|
||||
if login_request.skip:
|
||||
resp = current_app.hydra_api.accept_login_request(
|
||||
login_challenge,
|
||||
body={'subject': login_request.subject})
|
||||
return redirect(resp.redirect_to)
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query().by_username(form.data['name'])
|
||||
|
@ -53,13 +66,14 @@ def login():
|
|||
else:
|
||||
session['user'] = None
|
||||
session['auth_providers'] = []
|
||||
return redirect(url_for('auth.login_auth', next=flask.request.args.get('next')))
|
||||
|
||||
return redirect(url_for('auth.login_auth', login_challenge=login_challenge))
|
||||
return render_template('frontend/login.html.j2', form=form)
|
||||
|
||||
|
||||
@auth_views.route('/login/auth', methods=['GET', 'POST'])
|
||||
def login_auth():
|
||||
login_challenge = request.args.get('login_challenge')
|
||||
login_request = current_app.hydra_api.get_login_request(login_challenge)
|
||||
if 'username' not in session:
|
||||
return redirect(url_for('auth.login'))
|
||||
auth_forms = {}
|
||||
|
@ -74,6 +88,21 @@ def login_auth():
|
|||
auth_forms[auth_provider.get_name()]=form
|
||||
|
||||
if len(session['auth_providers']) >= 2:
|
||||
remember_me = True
|
||||
db_user = DbUser.query.filter(DbUser.username == session['username']).one_or_none()
|
||||
if db_user is None:
|
||||
db_user = DbUser(username=session['username'])
|
||||
db.session.add(db_user)
|
||||
db.session.commit()
|
||||
|
||||
subject = db_user.id
|
||||
|
||||
resp = current_app.hydra_api.accept_login_request(
|
||||
login_challenge, body={
|
||||
'subject': subject,
|
||||
'remember': remember_me})
|
||||
return redirect(resp.redirect_to)
|
||||
|
||||
login_user(SecurityUser(session['username']))
|
||||
# TODO use this var
|
||||
_next = None
|
||||
|
@ -89,9 +118,10 @@ def login_auth():
|
|||
|
||||
|
||||
@auth_views.route("/logout")
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
do_logout()
|
||||
return redirect(url_for('.login'))
|
||||
logout_challenge = request.args.get('logout_challenge')
|
||||
logout_request = current_app.hydra_api.get_logout_request(logout_challenge)
|
||||
resp = current_app.hydra_api.accept_logout_request(logout_challenge)
|
||||
return redirect(resp.redirect_to)
|
||||
|
||||
|
||||
|
|
|
@ -14,15 +14,19 @@ from pyop.exceptions import InvalidAuthenticationRequest, InvalidAccessToken, In
|
|||
InvalidSubjectIdentifier, InvalidClientRegistrationRequest
|
||||
from pyop.util import should_fragment_encode
|
||||
|
||||
from flask import Blueprint, render_template, request, url_for
|
||||
from flask import Blueprint, render_template, request, url_for, flash
|
||||
from flask_login import login_required, login_user, logout_user, current_user
|
||||
from werkzeug.utils import redirect
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
import pyotp
|
||||
|
||||
from base64 import b64decode, b64encode
|
||||
from flask_dance.consumer import oauth_authorized
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from flask_dance.consumer import OAuth2ConsumerBlueprint
|
||||
|
||||
from ..model import User, SecurityUser, Totp
|
||||
from ..model_db import OAuth, db, User as DbUser
|
||||
from ..form.login import LoginForm
|
||||
from ..form.frontend import ClientCertForm, TOTPForm, TOTPDeleteForm
|
||||
from ..auth_providers import AUTH_PROVIDER_LIST
|
||||
|
@ -31,6 +35,81 @@ from ..auth_providers import AUTH_PROVIDER_LIST
|
|||
frontend_views = Blueprint('frontend', __name__, url_prefix='')
|
||||
|
||||
|
||||
def init_login_manager(app):
|
||||
@app.login_manager.user_loader
|
||||
def user_loader(username):
|
||||
return User.query().by_username(username)
|
||||
|
||||
@app.login_manager.request_loader
|
||||
def request_loader(request):
|
||||
pass
|
||||
|
||||
@app.login_manager.unauthorized_handler
|
||||
def unauthorized():
|
||||
return redirect(url_for('oauth.login'))
|
||||
|
||||
base_url = app.config['HYDRA_PUBLIC_URL']
|
||||
example_blueprint = OAuth2ConsumerBlueprint(
|
||||
"oauth", __name__,
|
||||
client_id=app.config['OAUTH_ID'],
|
||||
client_secret=app.config['OAUTH_SECRET'],
|
||||
base_url=base_url,
|
||||
token_url=f"{base_url}/oauth2/token",
|
||||
authorization_url=f"{base_url}/oauth2/auth",
|
||||
scope=['openid', 'profile', 'manage']
|
||||
)
|
||||
app.register_blueprint(example_blueprint, url_prefix="/")
|
||||
app.oauth = example_blueprint
|
||||
|
||||
@oauth_authorized.connect_via(app.oauth)
|
||||
def github_logged_in(blueprint, token):
|
||||
if not token:
|
||||
flash("Failed to log in.", category="error")
|
||||
return False
|
||||
print(f'debug ---------------{token}')
|
||||
|
||||
resp = blueprint.session.get("/userinfo")
|
||||
if not resp.ok:
|
||||
msg = "Failed to fetch user info from GitHub."
|
||||
flash(msg, category="error")
|
||||
return False
|
||||
|
||||
oauth_info = resp.json()
|
||||
|
||||
db_user = DbUser.query.get(str(oauth_info["sub"]))
|
||||
oauth_username = db_user.username
|
||||
|
||||
# Find this OAuth token in the database, or create it
|
||||
query = OAuth.query.filter_by(
|
||||
provider=blueprint.name,
|
||||
provider_username=oauth_username,
|
||||
)
|
||||
try:
|
||||
oauth = query.one()
|
||||
except NoResultFound:
|
||||
oauth = OAuth(
|
||||
provider=blueprint.name,
|
||||
provider_username=oauth_username,
|
||||
token=token,
|
||||
)
|
||||
|
||||
|
||||
login_user(SecurityUser(oauth.provider_username))
|
||||
#flash("Successfully signed in with GitHub.")
|
||||
|
||||
# Since we're manually creating the OAuth model in the database,
|
||||
# we should return False so that Flask-Dance knows that
|
||||
# it doesn't have to do it. If we don't return False, the OAuth token
|
||||
# could be saved twice, or Flask-Dance could throw an error when
|
||||
# trying to incorrectly save it for us.
|
||||
return True
|
||||
|
||||
@frontend_views.route('/logout')
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(f'{current_app.config["HYDRA_PUBLIC_URL"]}/oauth2/sessions/logout')
|
||||
|
||||
|
||||
@frontend_views.route('/', methods=['GET'])
|
||||
@login_required
|
||||
def index():
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pyop
|
||||
Flask
|
||||
gunicorn
|
||||
flask_babel
|
||||
|
@ -12,6 +11,10 @@ python-u2flib-server
|
|||
pyotp
|
||||
cryptography
|
||||
|
||||
requests
|
||||
requests_oauthlib
|
||||
blinker
|
||||
ory-hydra-client
|
||||
|
||||
flask-debug
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<div class="sidebar-sticky active">
|
||||
{#<a href="/"><img alt="logo" class="container-fluid" src="/static/images/dog_main_small.png"></a>#}
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('frontend.index') }}">{{ gettext('Account') }}</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('auth.logout') }}">{{ gettext('Logout') }}</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('frontend.logout') }}">{{ gettext('Logout') }}</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('frontend.client_cert') }}">{{ gettext('Client Cert') }}</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('frontend.totp') }}">{{ gettext('2FA - TOTP') }}</a></li>
|
||||
</div>
|
||||
|
|
9
wsgi.py
9
wsgi.py
|
@ -1,13 +1,12 @@
|
|||
import logging
|
||||
|
||||
from lenticular_cloud.app import oidc_provider_init_app
|
||||
from lenticular_cloud.app import init_app
|
||||
|
||||
name = 'oidc_provider'
|
||||
app = oidc_provider_init_app(name)
|
||||
app = init_app(name)
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
from flask_debug import Debug
|
||||
Debug(app)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(ssl_context=('https.crt', 'https.key'), debug=True, host='::')
|
||||
#app.run(ssl_context=('https.crt', 'https.key'), debug=True, host='127.0.0.1')
|
||||
app.run(debug=True, host='127.0.0.1')
|
||||
|
|
Loading…
Reference in a new issue