Packaging
10
.gitignore
vendored
|
@ -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
|
@ -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/*
|
19
README.md
|
@ -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
|
||||
```
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[python: **.py]
|
||||
[jinja2: **/templates/**.html]
|
||||
extensions=jinja2.ext.autoescape,jinja2.ext.with_
|
|
@ -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" ]
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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")
|
|
@ -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"])
|
|
@ -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")
|
|
@ -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
|
|
@ -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():
|
|
@ -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)
|
||||
|
Before Width: | Height: | Size: 496 KiB After Width: | Height: | Size: 496 KiB |
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 382 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"))
|
29
setup.cfg
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from web.models import Client
|
||||
from oidc_ldap_bridge.models import Client
|
||||
|
||||
|
||||
def test_no_logged_no_access(testclient):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|