Packaging

This commit is contained in:
Éloi Rivard 2020-08-31 11:23:50 +02:00
parent 2c71e44eb3
commit 2eba625c39
74 changed files with 114 additions and 72 deletions

10
.gitignore vendored
View file

@ -14,8 +14,8 @@ htmlcov
build
dist
python-ldap-test*
conf/oauth-authorization-server.json
conf/openid-configuration.json
conf/*.pem
conf/*.pub
conf/*.key
oidc_ldap_bridge/conf/oauth-authorization-server.json
oidc_ldap_bridge/conf/openid-configuration.json
oidc_ldap_bridge/conf/*.pem
oidc_ldap_bridge/conf/*.pub
oidc_ldap_bridge/conf/*.key

6
MANIFEST.in Normal file
View file

@ -0,0 +1,6 @@
include schemas/*
include oidc_ldap_bridge/conf/*.sample.*
graft oidc_ldap_bridge/templates
graft oidc_ldap_bridge/translations
graft oidc_ldap_bridge/static
exclude tests/*

View file

@ -26,7 +26,18 @@ sudo service slapd restart
sudo slapadd -n0 -l schema/*.ldif
```
TBD
Then you can deploy the code either by copying the git repository or installing the pip package:
```bash
pip install oidc_ldap_bridge
```
Finally you have to run the website in a WSGI server:
```bash
pip install gunicorn
gunicorn "oidc_ldap_bridge:create_app()"
```
## Contribute
@ -36,8 +47,8 @@ To run the tests, you just need to run `tox`.
To try a development environment, you can run the docker image and then open https://127.0.0.1:5000
```bash
cp conf/config.sample.toml conf/config.toml
cp conf/oauth-authorization-server.sample.json conf/oauth-authorization-server
cp conf/openid-configuration.sample.json conf/openid-configuration
cp oidc_ldap_bridge/conf/config.sample.toml oidc_ldap_bridge/conf/config.toml
cp oidc_ldap_bridge/conf/oauth-authorization-server.sample.json oidc_ldap_bridge/conf/oauth-authorization-server.json
cp oidc_ldap_bridge/conf/openid-configuration.sample.json oidc_ldap_bridge/conf/openid-configuration.json
docker-compose up
```

View file

@ -1,3 +0,0 @@
[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_

View file

@ -12,8 +12,8 @@ RUN pip install /app/
WORKDIR /app
USER oauthserver
ENV FLASK_APP=web
ENV FLASK_APP=oidc_ldap_bridge
ENV FLASK_ENV=development
ENV AUTHLIB_INSECURE_TRANSPORT=1
ENTRYPOINT [ "flask", "run", "--host", "0.0.0.0" ]
ENTRYPOINT [ "flask", "run", "--host", "0.0.0.0", "--extra-files", "oidc_ldap_bridge/conf/config.toml" ]

View file

@ -4,14 +4,14 @@ import toml
from flask import Flask, g, request, render_template
from flask_babel import Babel
import web.admin
import web.admin.tokens
import web.admin.authorizations
import web.admin.clients
import web.oauth
import web.routes
import web.tokens
import web.well_known
import oidc_ldap_bridge.admin
import oidc_ldap_bridge.admin.tokens
import oidc_ldap_bridge.admin.authorizations
import oidc_ldap_bridge.admin.clients
import oidc_ldap_bridge.oauth
import oidc_ldap_bridge.routes
import oidc_ldap_bridge.tokens
import oidc_ldap_bridge.well_known
from cryptography.hazmat.primitives import serialization as crypto_serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend as crypto_default_backend
@ -22,6 +22,7 @@ from .oauth2utils import config_oauth
def create_app(config=None):
app = Flask(__name__)
dir_path = os.path.dirname(os.path.realpath(__file__))
app.config.from_mapping(
{
@ -33,8 +34,8 @@ def create_app(config=None):
app.config.from_mapping(config)
elif "CONFIG" in os.environ:
app.config.from_mapping(toml.load(os.environ.get("CONFIG")))
elif os.path.exists("conf/config.toml"):
app.config.from_mapping(toml.load("conf/config.toml"))
elif os.path.exists(os.path.join(dir_path, "conf", "config.toml")):
app.config.from_mapping(toml.load(os.path.join(dir_path, "conf", "config.toml")))
else:
raise Exception(
"No configuration file found. "
@ -84,15 +85,15 @@ def setup_app(app):
app.url_map.strict_slashes = False
config_oauth(app)
app.register_blueprint(web.routes.bp)
app.register_blueprint(web.oauth.bp, url_prefix="/oauth")
app.register_blueprint(web.tokens.bp, url_prefix="/token")
app.register_blueprint(web.well_known.bp, url_prefix="/.well-known")
app.register_blueprint(web.admin.tokens.bp, url_prefix="/admin/token")
app.register_blueprint(oidc_ldap_bridge.routes.bp)
app.register_blueprint(oidc_ldap_bridge.oauth.bp, url_prefix="/oauth")
app.register_blueprint(oidc_ldap_bridge.tokens.bp, url_prefix="/token")
app.register_blueprint(oidc_ldap_bridge.well_known.bp, url_prefix="/.well-known")
app.register_blueprint(oidc_ldap_bridge.admin.tokens.bp, url_prefix="/admin/token")
app.register_blueprint(
web.admin.authorizations.bp, url_prefix="/admin/authorization"
oidc_ldap_bridge.admin.authorizations.bp, url_prefix="/admin/authorization"
)
app.register_blueprint(web.admin.clients.bp, url_prefix="/admin/client")
app.register_blueprint(oidc_ldap_bridge.admin.clients.bp, url_prefix="/admin/client")
babel = Babel(app)

View file

@ -1,6 +1,6 @@
from flask import Blueprint, render_template
from web.models import AuthorizationCode
from web.flaskutils import admin_needed
from oidc_ldap_bridge.models import AuthorizationCode
from oidc_ldap_bridge.flaskutils import admin_needed
bp = Blueprint(__name__, "authorizations")

View file

@ -5,8 +5,8 @@ from flask import Blueprint, render_template, request, flash, redirect, url_for
from flask_wtf import FlaskForm
from flask_babel import gettext
from werkzeug.security import gen_salt
from web.models import Client
from web.flaskutils import admin_needed
from oidc_ldap_bridge.models import Client
from oidc_ldap_bridge.flaskutils import admin_needed
bp = Blueprint(__name__, "clients")
@ -156,7 +156,7 @@ def add():
"success",
)
return redirect(url_for("web.admin.clients.edit", client_id=client_id))
return redirect(url_for("oidc_ldap_bridge.admin.clients.edit", client_id=client_id))
@bp.route("/edit/<client_id>", methods=["GET", "POST"])

View file

@ -1,6 +1,6 @@
from flask import Blueprint, render_template
from web.models import Token
from web.flaskutils import admin_needed
from oidc_ldap_bridge.models import Token
from oidc_ldap_bridge.flaskutils import admin_needed
bp = Blueprint(__name__, "tokens")

View file

@ -6,8 +6,8 @@ NAME = "MyDomain"
LANGUAGE = "en"
# Path to the RFC8414 metadata file
OAUTH2_METADATA_FILE = "conf/oauth-authorization-server.json"
OIDC_METADATA_FILE = "conf/openid-configuration.json"
OAUTH2_METADATA_FILE = "oidc_ldap_bridge/conf/oauth-authorization-server.json"
OIDC_METADATA_FILE = "oidc_ldap_bridge/conf/openid-configuration.json"
[LDAP]
URI = "ldap://ldap"
@ -25,8 +25,8 @@ USER_FILTER = "(|(uid={login})(cn={login}))"
ADMIN_FILTER = "cn=Jane Doe"
[JWT]
PUBLIC_KEY = "conf/public.pem"
PRIVATE_KEY = "conf/private.pem"
PUBLIC_KEY = "oidc_ldap_bridge/conf/public.pem"
PRIVATE_KEY = "oidc_ldap_bridge/conf/private.pem"
KTY = "RSA"
ALG = "RS256"
EXP = 3600

View file

@ -1,6 +1,6 @@
from functools import wraps
from flask import session, abort
from web.models import User
from oidc_ldap_bridge.models import User
def current_user():

View file

@ -14,8 +14,8 @@ bp = Blueprint(__name__, "home")
@bp.route("/")
def index():
if not current_user():
return redirect(url_for("web.routes.login"))
return redirect(url_for("web.tokens.tokens"))
return redirect(url_for("oidc_ldap_bridge.routes.login"))
return redirect(url_for("oidc_ldap_bridge.tokens.tokens"))
@bp.route("/login", methods=("GET", "POST"))
@ -29,7 +29,7 @@ def login():
flash(gettext("Login failed, please check your information"), "error")
return render_template("login.html", form=form)
return redirect(url_for("web.routes.index"))
return redirect(url_for("oidc_ldap_bridge.routes.index"))
return render_template("login.html", form=form)

View file

Before

Width:  |  Height:  |  Size: 382 KiB

After

Width:  |  Height:  |  Size: 382 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -22,8 +22,8 @@
</thead>
{% for authorization in authorizations %}
<tr>
<td><a href="{{ url_for('web.admin.authorizations.view', authorization_id=authorization.oauthCode) }}">{{ authorization.oauthCode }}</a></td>
<td><a href="{{ url_for('web.admin.clients.edit', client_id=authorization.oauthClientID) }}">{{ authorization.oauthClientID }}</a></td>
<td><a href="{{ url_for('oidc_ldap_bridge.admin.authorizations.view', authorization_id=authorization.oauthCode) }}">{{ authorization.oauthCode }}</a></td>
<td><a href="{{ url_for('oidc_ldap_bridge.admin.clients.edit', client_id=authorization.oauthClientID) }}">{{ authorization.oauthClientID }}</a></td>
<td>{{ authorization.oauthSubject }}</td>
<td>{{ authorization.issue_date }}</td>
</tr>

View file

@ -14,7 +14,7 @@
{% block content %}
<div class="ui segment">
<a class="ui primary button" href="{{ url_for('web.admin.clients.add') }}">{% trans %}Add client{% endtrans %}</a>
<a class="ui primary button" href="{{ url_for('oidc_ldap_bridge.admin.clients.add') }}">{% trans %}Add client{% endtrans %}</a>
</div>
<table class="ui table">
@ -25,7 +25,7 @@
</thead>
{% for client in clients %}
<tr>
<td><a href="{{ url_for('web.admin.clients.edit', client_id=client.oauthClientID) }}">{{ client.oauthClientName }}</a></td>
<td><a href="{{ url_for('oidc_ldap_bridge.admin.clients.edit', client_id=client.oauthClientID) }}">{{ client.oauthClientName }}</a></td>
<td><a href="{{ client.oauthClientURI }}">{{ client.oauthClientURI }}</a></td>
<td>{{ client.issue_date }}</td>
</tr>

View file

@ -22,8 +22,8 @@
</thead>
{% for token in tokens %}
<tr>
<td><a href="{{ url_for('web.admin.tokens.view', token_id=token.oauthAccessToken) }}">{{ token.oauthAccessToken }}</a></td>
<td><a href="{{ url_for('web.admin.clients.edit', client_id=token.oauthClientID) }}">{{ token.oauthClientID }}</a></td>
<td><a href="{{ url_for('oidc_ldap_bridge.admin.tokens.view', token_id=token.oauthAccessToken) }}">{{ token.oauthAccessToken }}</a></td>
<td><a href="{{ url_for('oidc_ldap_bridge.admin.clients.edit', client_id=token.oauthClientID) }}">{{ token.oauthClientID }}</a></td>
<td>{{ token.oauthSubject }}</td>
<td>{{ token.issue_date }}</td>
</tr>

View file

@ -30,7 +30,7 @@
</a>
</div>
{% endif %}
<a class="item" href="{{ url_for('web.tokens.tokens') }}">
<a class="item" href="{{ url_for('oidc_ldap_bridge.tokens.tokens') }}">
<i class="key icon"></i>
{% trans %}My accesses{% endtrans %}
</a>
@ -39,22 +39,22 @@
<i class="settings icon"></i>
Admin
<div class="menu">
<a class="item" href="{{ url_for('web.admin.clients.index') }}">
<a class="item" href="{{ url_for('oidc_ldap_bridge.admin.clients.index') }}">
<i class="plug icon"></i>
{% trans %}Clients{% endtrans %}
</a>
<a class="item" href="{{ url_for('web.admin.tokens.index') }}">
<a class="item" href="{{ url_for('oidc_ldap_bridge.admin.tokens.index') }}">
<i class="key icon"></i>
{% trans %}Tokens{% endtrans %}
</a>
<a class="item" href="{{ url_for('web.admin.authorizations.index') }}">
<a class="item" href="{{ url_for('oidc_ldap_bridge.admin.authorizations.index') }}">
<i class="user secret icon"></i>
{% trans %}Codes{% endtrans %}
</a>
</div>
</div>
{% endif %}
<a class="item" href="{{ url_for('web.routes.logout') }}">
<a class="item" href="{{ url_for('oidc_ldap_bridge.routes.logout') }}">
<i class="sign out alternate icon"></i>
{% trans %}Log out{% endtrans %}
</a>

View file

@ -48,7 +48,7 @@
</ul>
</div>
</div>
<a class="ui bottom attached button" href="{{ url_for('web.tokens.delete', token_id=token.oauthAccessToken ) }}">
<a class="ui bottom attached button" href="{{ url_for('oidc_ldap_bridge.tokens.delete', token_id=token.oauthAccessToken ) }}">
<i class="remove icon"></i>
{% trans %}Remove access{% endtrans %}
</a>

View file

@ -1,7 +1,7 @@
from flask import Blueprint, render_template, flash, redirect, url_for
from flask_babel import gettext
from web.models import Token, Client
from web.flaskutils import user_needed
from oidc_ldap_bridge.models import Token, Client
from oidc_ldap_bridge.flaskutils import user_needed
bp = Blueprint(__name__, "tokens")
@ -34,4 +34,4 @@ def delete(user, token_id):
token.save()
flash(gettext("The access has been revoked"), "success")
return redirect(url_for("web.tokens.tokens"))
return redirect(url_for("oidc_ldap_bridge.tokens.tokens"))

View file

@ -26,6 +26,10 @@ install_requires =
python-ldap
toml
[options.packages.find]
exclude =
tests
[tox:tox]
envlist =
py36
@ -58,5 +62,28 @@ commands =
[coverage:run]
source =
web
oidc_ldap_bridge
tests
[extract_messages]
copyright_holder = Yaal Coop Team
input_paths = oidc_ldap_bridge
output_file = oidc_ldap_bridge/translations/messages.pot
[init_catalog]
input_file = oidc_ldap_bridge/translations/messages.pot
output_dir = oidc_ldap_bridge/translations/
domain = oidc_ldap_bridge
[update_catalog]
input_file = oidc_ldap_bridge/translations/messages.pot
output_dir = oidc_ldap_bridge/translations/
domain = oidc_ldap_bridge
update-header-comment = true
no-fuzzy-matching = true
ignore-obsolete = true
[compile_catalog]
directory = oidc_ldap_bridge/translations/
domain = oidc_ldap_bridge
statistics = true

View file

@ -8,9 +8,9 @@ from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend as crypto_default_backend
from flask_webtest import TestApp
from werkzeug.security import gen_salt
from web import create_app
from web.models import User, Client, Token, AuthorizationCode
from web.ldaputils import LDAPObjectHelper
from oidc_ldap_bridge import create_app
from oidc_ldap_bridge.models import User, Client, Token, AuthorizationCode
from oidc_ldap_bridge.ldaputils import LDAPObjectHelper
class CustomSlapdObject(slapdtest.SlapdObject):
@ -116,8 +116,8 @@ def app(slapd_server, keypair_path):
app = create_app(
{
"SECRET_KEY": gen_salt(24),
"OAUTH2_METADATA_FILE": "conf/oauth-authorization-server.sample.json",
"OIDC_METADATA_FILE": "conf/openid-configuration.sample.json",
"OAUTH2_METADATA_FILE": "oidc_ldap_bridge/conf/oauth-authorization-server.sample.json",
"OIDC_METADATA_FILE": "oidc_ldap_bridge/conf/openid-configuration.sample.json",
"LDAP": {
"ROOT_DN": slapd_server.suffix,
"URI": slapd_server.ldap_uri,

View file

@ -1,7 +1,7 @@
from . import client_credentials
from authlib.oauth2.rfc7636 import create_s256_code_challenge
from urllib.parse import urlsplit, parse_qs
from web.models import AuthorizationCode, Token
from oidc_ldap_bridge.models import AuthorizationCode, Token
from werkzeug.security import gen_salt

View file

@ -1,4 +1,4 @@
from web.models import Client
from oidc_ldap_bridge.models import Client
def test_no_logged_no_access(testclient):

View file

@ -1,6 +1,6 @@
from authlib.jose import jwt
from urllib.parse import urlsplit, parse_qs
from web.models import AuthorizationCode, Token
from oidc_ldap_bridge.models import AuthorizationCode, Token
def test_oauth_hybrid(testclient, slapd_connection, user, client):

View file

@ -1,6 +1,6 @@
from authlib.jose import jwt
from urllib.parse import urlsplit, parse_qs
from web.models import Token
from oidc_ldap_bridge.models import Token
def test_oauth_implicit(testclient, slapd_connection, user, client):

View file

@ -1,5 +1,5 @@
from . import client_credentials
from web.models import Token
from oidc_ldap_bridge.models import Token
def test_password_flow(testclient, slapd_connection, user, client):