tests workflow

This commit is contained in:
Éloi Rivard 2020-08-18 17:39:34 +02:00
parent 9e75432145
commit 531c34a689
15 changed files with 583 additions and 38 deletions

2
.gitignore vendored
View file

@ -1,7 +1,9 @@
*.sqlite
*.pyc
venv/*
env
.*@neomake*
.ash_history
.python_history
config.toml
.tox

29
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,29 @@
---
image: python
stages:
- test
- build
- release
before_script:
- apt update
- env DEBIAN_FRONTEND=noninteractive apt install --yes slapd python3-dev libldap2-dev libsasl2-dev libssl-dev ldap-utils
- curl -O https://bootstrap.pypa.io/get-pip.py
- python get-pip.py
- pip install tox poetry coveralls pyyaml
python36:
image: python:3.6
stage: test
script: tox -e py36
python37:
image: python:3.7
stage: test
script: tox -e py37
python38:
image: python:3.8
stage: test
script: tox -e py38

View file

@ -3,3 +3,15 @@
oidc-ldap-bridge is a simple OpenID Connect provider based upon OpenLDAP.
It authenticates your LDAP users, and do not need any additional database to work. Everything is stored in your OpenLDAP server.
## Contribute
Contributions are welcome!
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 config.sample.toml config.toml
docker-compose up
```

View file

@ -6,6 +6,7 @@ NAME = "MyDomain"
LANGUAGE = "en"
[LDAP]
URI = "ldaps://ldap.mydomain.tld"
BIND_USER = "cn=admin,dc=mydomain,dc=tld"
URI = "ldap://ldap"
ROOT_DN = "dc=mydomain,dc=tld"
BIND_DN = "cn=admin,dc=mydomain,dc=tld"
BIND_PW = "admin"

View file

@ -8,8 +8,11 @@ services:
- LDAP_DOMAIN=mydomain.tld
volumes:
- ./docker/bootstrap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-boostrap.ldif:ro
- ./oauth2-openldap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/40-oauth2.ldif:ro
- ./schemas/oauth2-openldap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/40-oauth2.ldif:ro
command: --copy-service
ports:
- 5389:389
- 5636:636
oauth:
build:

View file

@ -3,4 +3,7 @@ flask
flask-babel
flask-wtf
python-ldap
pytest
pytest-flask
toml
pdbpp

View file

@ -0,0 +1,334 @@
attributetype ( 1.3.6.1.4.1.56207.1.1.1 NAME 'oauthCode'
DESC 'OAuth 2.0 Authorization Code'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.2 NAME 'oauthClientID'
DESC 'Authorized client'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.3 NAME 'oauthRedirectURI'
DESC 'Authorization Code Redirection URI'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.4 NAME 'oauthResponseType'
DESC 'OAuth 2.0 response type'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.5 NAME 'oauthScope'
DESC 'OAuth 2.0 scope value'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.6 NAME 'oauthNonce'
DESC 'OAuth 2.0 nonce'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.7 NAME 'oauthAuthorizationDate'
DESC 'Access token issue date'
EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.8 NAME 'oauthCodeChallenge'
DESC 'OAuth 2.0 nonce'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.9 NAME 'oauthCodeChallengeMethod'
DESC 'OAuth 2.0 nonce'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.10 NAME 'oauthClientSecret'
DESC 'Client secret'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.11 NAME 'oauthClientSecretExpDate'
DESC 'Client secret expiration date/time'
EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.12 NAME 'oauthIssueDate'
DESC 'Client identifier issue date/time'
EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.13 NAME 'oauthGrantType'
DESC 'OAuth 2.0 grant type'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.14 NAME 'oauthTokenLifetime'
DESC 'OAuth 2.0 refresh token lifetime, in seconds'
EQUALITY integerMatch
ORDERING integerOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.15 NAME 'oauthClientName'
DESC 'Client name'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.16 NAME 'oauthClientContact'
DESC 'Client name'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.17 NAME 'oauthClientURI'
DESC 'Client URI'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.18 NAME 'oauthLogoURI'
DESC 'Logo URI'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.19 NAME 'oauthTermsOfServiceURI'
DESC 'Terms of service URI'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.20 NAME 'oauthPolicyURI'
DESC 'Policy URI'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.21 NAME 'oauthJWKURI'
DESC 'JWK set URI'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.22 NAME 'oauthJWK'
DESC 'JWK set JSON'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.23 NAME 'oauthSoftwareID'
DESC 'Software identifier'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.24 NAME 'oauthSoftwareVersion'
DESC 'Software version'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.25 NAME 'oauthToken'
DESC 'OAuth 2.0 Token'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.26 NAME 'oauthTokenType'
DESC 'OAuth 2.0 Token'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.27 NAME 'oauthAccessToken'
DESC 'OAuth 2.0 access token'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.28 NAME 'oauthRefreshToken'
DESC 'OAuth 2.0 refresh token'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.29 NAME 'oauthTokenEndpointAuthMethod'
DESC 'OAuth 2.0 Token endpoint authentication method'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0 Dynamic Client Registration Protocol' )
attributetype ( 1.3.6.1.4.1.56207.1.1.30 NAME 'oauthSubject'
DESC 'OAuth 2.0 Token subject'
EQUALITY caseExactMatch
ORDERING caseExactOrderingMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0 Dynamic Client Registration Protocol' )
attributetype ( 1.3.6.1.4.1.56207.1.1.31 NAME 'oauthRedirectURIs'
DESC 'Authorization Code Redirection URI'
EQUALITY caseIgnoreMatch
ORDERING caseIgnoreOrderingMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
attributetype ( 1.3.6.1.4.1.56207.1.1.32 NAME 'oauthAuthorizationLifetime'
DESC 'OAuth 2.0 authorization code lifetime, in seconds'
EQUALITY integerMatch
ORDERING integerOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE
USAGE userApplications
X-ORIGIN 'OAuth 2.0' )
objectclass ( 1.3.6.1.4.1.56207.1.2.1 NAME 'oauthClient'
DESC 'OAuth 2.0 Authorization Code'
SUP top
STRUCTURAL
MUST oauthClientID
MAY ( description $
oauthClientName $
oauthClientContact $
oauthClientURI $
oauthRedirectURIs $
oauthLogoURI $
oauthIssueDate $
oauthClientSecret $
oauthClientSecretExpDate $
oauthGrantType $
oauthResponseType $
oauthScope $
oauthTermsOfServiceURI $
oauthPolicyURI $
oauthJWKURI $
oauthJWK $
oauthTokenEndpointAuthMethod $
oauthSoftwareID $
oauthSoftwareVersion )
)
X-ORIGIN 'OAuth 2.0' )
objectclass ( 1.3.6.1.4.1.56207.1.2.2 NAME 'oauthAuthorizationCode'
DESC 'OAuth 2.0 Authorization Code'
SUP top
STRUCTURAL
MUST oauthCode
MAY ( description $
oauthClientID $
oauthSubject $
oauthRedirectURI $
oauthResponseType $
oauthScope $
oauthNonce $
oauthAuthorizationDate $
oauthAuthorizationLifetime $
oauthCodeChallenge $
oauthCodeChallengeMethod )
X-ORIGIN 'OAuth 2.0' )
objectclass ( 1.3.6.1.4.1.56207.1.2.3 NAME 'oauthToken'
DESC 'OAuth 2.0 Token'
SUP top
STRUCTURAL
MUST oauthAccessToken
MAY ( description $
oauthClientID $
oauthSubject $
oauthTokenType $
oauthRefreshToken $
oauthScope $
oauthIssueDate $
oauthTokenLifetime )
X-ORIGIN 'OAuth 2.0' )

11
setup.cfg Normal file
View file

@ -0,0 +1,11 @@
[tox:tox]
envlist =
py36
py37
py38
skipsdist=True
[testenv]
install_command = pip install {packages}
commands = {envbindir}/pytest --showlocals {posargs}
deps = --requirement requirements.txt

0
tests/__init__.py Normal file
View file

117
tests/conftest.py Normal file
View file

@ -0,0 +1,117 @@
import datetime
import ldap.ldapobject
import os
import pytest
import slapdtest
from werkzeug.security import gen_salt
from web import create_app
from web.models import User, Client, Token, AuthorizationCode
from web.ldaputils import LDAPObjectHelper
class CustomSlapdObject(slapdtest.SlapdObject):
custom_schema_files = ("oauth2-openldap.schema",)
def _ln_schema_files(self, *args, **kwargs):
dir_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "schemas"
)
super()._ln_schema_files(*args, **kwargs)
super()._ln_schema_files(self.custom_schema_files, dir_path)
def gen_config(self):
previous = self.openldap_schema_files
self.openldap_schema_files += self.custom_schema_files
config = super().gen_config()
self.openldap_schema_files = previous
return config
@pytest.fixture(scope="session")
def slapd_server():
slapd = CustomSlapdObject()
try:
slapd.start()
suffix_dc = slapd.suffix.split(",")[0][3:]
slapd.ldapadd(
"\n".join(
[
"dn: " + slapd.suffix,
"objectClass: dcObject",
"objectClass: organization",
"dc: " + suffix_dc,
"o: " + suffix_dc,
"",
"dn: " + slapd.root_dn,
"objectClass: applicationProcess",
"cn: " + slapd.root_cn,
]
)
+ "\n"
)
yield slapd
finally:
slapd.stop()
@pytest.fixture
def slapd_connection(slapd_server):
conn = ldap.ldapobject.SimpleLDAPObject(slapd_server.ldap_uri)
conn.protocol_version = 3
conn.simple_bind_s(slapd_server.root_dn, slapd_server.root_pw)
yield conn
conn.unbind_s()
@pytest.fixture
def app(slapd_server, slapd_connection):
LDAPObjectHelper.root_dn = slapd_server.suffix
Client.initialize(slapd_connection)
User.initialize(slapd_connection)
Token.initialize(slapd_connection)
AuthorizationCode.initialize(slapd_connection)
app = create_app(
{
"LDAP": {
"URI": slapd_server.ldap_uri,
"BIND_DN": slapd_server.root_dn,
"BIND_PW": slapd_server.root_pw,
}
}
)
return app
@pytest.fixture
def client(app, slapd_connection):
c = Client(
oauthClientID=gen_salt(24),
oauthClientName="Some client",
oauthClientContact="contact@mydomain.tld",
oauthClientURI="https://mydomain.tld",
oauthRedirectURIs=[
"https://mydomain.tld/redirect1",
"https://mydomain.tld/redirect2",
],
oauthLogoURI="https://mydomain.tld/logo.png",
oauthIssueDate=datetime.datetime.now().strftime("%Y%m%d%H%S%MZ"),
oauthClientSecret=gen_salt(48),
oauthGrantType=["password", "authorization_code"],
oauthResponseType=["code"],
oauthScope=["openid", "profile"],
oauthTermsOfServiceURI="https://mydomain.tld/tos",
oauthPolicyURI="https://mydomain.tld/policy",
oauthJWKURI="https://mydomain.tld/jwk",
oauthTokenEndpointAuthMethod="client_secret_basic",
)
c.save(slapd_connection)
return c
@pytest.fixture
def user(app, slapd_connection):
u = User(cn="John Doe", sn="Doe",)
u.save(slapd_connection)
return u

View file

@ -0,0 +1,2 @@
def test_foobar(slapd_connection, user, client):
assert True

View file

@ -7,35 +7,26 @@ from flask import Flask, g, request
from flask_babel import Babel
from .oauth2utils import config_oauth
from .ldaputils import LDAPObjectHelper
def create_app(config=None):
app = Flask(__name__)
app.config.from_mapping(
{"OAUTH2_REFRESH_TOKEN_GENERATOR": True,}
)
app.config.from_mapping(toml.load(os.environ.get("CONFIG", "config.toml")))
app.url_map.strict_slashes = False
app.config.from_mapping({"OAUTH2_REFRESH_TOKEN_GENERATOR": True})
if config:
app.config.from_mapping(config)
elif "CONFIG" in os.environ:
app.config.from_mapping(toml.load(os.environ.get("CONFIG")))
elif os.path.exists("config.toml"):
app.config.from_mapping(toml.load("config.toml"))
setup_app(app)
return app
def setup_app(app):
@app.before_request
def before_request():
g.ldap = ldap.initialize(app.config["LDAP"]["URI"])
g.ldap.simple_bind_s(
app.config["LDAP"]["BIND_USER"], app.config["LDAP"]["BIND_PW"]
)
@app.after_request
def after_request(response):
if "ldap" in g:
g.ldap.unbind_s()
return response
app.url_map.strict_slashes = False
config_oauth(app)
app.register_blueprint(routes.bp)
@ -44,6 +35,20 @@ def setup_app(app):
babel = Babel(app)
@app.before_request
def before_request():
LDAPObjectHelper.root_dn = app.config["LDAP"]["ROOT_DN"]
g.ldap = ldap.initialize(app.config["LDAP"]["URI"])
g.ldap.simple_bind_s(
app.config["LDAP"]["BIND_DN"], app.config["LDAP"]["BIND_PW"]
)
@app.after_request
def after_request(response):
if "ldap" in g:
g.ldap.unbind_s()
return response
@app.context_processor
def global_processor():
return {

View file

@ -8,6 +8,7 @@ class LDAPObjectHelper:
may = None
must = None
base = None
root_dn = None
id = None
def __init__(self, dn=None, **kwargs):
@ -29,6 +30,10 @@ class LDAPObjectHelper:
self.__class__.__name__, self.id, getattr(self, self.id)
)
@classmethod
def ldap(cls):
return g.ldap
def keys(self):
return self.must + self.may
@ -40,20 +45,37 @@ class LDAPObjectHelper:
self.__setattr__(k, v)
def delete(self):
g.ldap.delete_s(self.dn)
self.ldap().delete_s(self.dn)
@property
def dn(self):
if not self.id in self.attrs:
return None
return f"{self.id}={self.attrs[self.id][0]},{self.base}"
return f"{self.id}={self.attrs[self.id][0]},{self.base},{self.root_dn}"
@classmethod
def ocs_by_name(cls):
def initialize(cls, conn=None):
conn = conn or cls.ldap()
cls.ocs_by_name(conn)
cls.attr_type_by_name(conn)
dn = f"{cls.base},{cls.root_dn}"
conn.add_s(
dn,
[
("objectClass", [b"organizationalUnit"]),
("ou", [cls.base.encode("utf-8")]),
],
)
@classmethod
def ocs_by_name(cls, conn=None):
if cls._object_class_by_name:
return cls._object_class_by_name
res = g.ldap.search_s(
conn = conn or cls.ldap()
res = conn.search_s(
"cn=subschema", ldap.SCOPE_BASE, "(objectclass=*)", ["*", "+"]
)
subschema_entry = res[0]
@ -69,11 +91,13 @@ class LDAPObjectHelper:
return cls._object_class_by_name
@classmethod
def attr_type_by_name(cls):
def attr_type_by_name(cls, conn=None):
if cls._attribute_type_by_name:
return cls._attribute_type_by_name
res = g.ldap.search_s(
conn = conn or cls.ldap()
res = conn.search_s(
"cn=subschema", ldap.SCOPE_BASE, "(objectclass=*)", ["*", "+"]
)
subschema_entry = res[0]
@ -88,9 +112,10 @@ class LDAPObjectHelper:
return cls._attribute_type_by_name
def save(self):
def save(self, conn=None):
conn = conn or self.ldap()
try:
match = bool(g.ldap.search_s(self.dn, ldap.SCOPE_SUBTREE))
match = bool(conn.search_s(self.dn, ldap.SCOPE_SUBTREE))
except ldap.NO_SUCH_OBJECT:
match = False
@ -99,19 +124,19 @@ class LDAPObjectHelper:
(ldap.MOD_REPLACE, k, [elt.encode("utf-8") for elt in v])
for k, v in self.attrs.items()
]
g.ldap.modify_s(self.dn, attributes)
conn.modify_s(self.dn, attributes)
else:
attributes = [
(k, [elt.encode("utf-8") for elt in v]) for k, v in self.attrs.items()
]
g.ldap.add_s(self.dn, attributes)
conn.add_s(self.dn, attributes)
@classmethod
def get(cls, dn):
if "=" not in dn:
dn = f"{cls.id}={dn},{cls.base}"
result = g.ldap.search_s(dn, ldap.SCOPE_SUBTREE)
dn = f"{cls.id}={dn},{cls.base},{cls.root_dn}"
result = cls.ldap().search_s(dn, ldap.SCOPE_SUBTREE)
if not result:
return None
@ -127,7 +152,8 @@ class LDAPObjectHelper:
class_filter = "".join([f"(objectClass={oc})" for oc in cls.objectClass])
arg_filter = "".join(f"({k}={v})" for k, v in kwargs.items())
ldapfilter = f"(&{class_filter}{arg_filter})"
result = g.ldap.search_s(base or cls.base, ldap.SCOPE_SUBTREE, ldapfilter)
base = base or f"{cls.base},{cls.root_dn}"
result = cls.ldap().search_s(base, ldap.SCOPE_SUBTREE, ldapfilter)
return [
cls(**{k: [elt.decode("utf-8") for elt in v] for k, v in args.items()},)

View file

@ -11,7 +11,7 @@ from .ldaputils import LDAPObjectHelper
class User(LDAPObjectHelper):
objectClass = ["person"]
base = "ou=users,dc=mydomain,dc=tld"
base = "ou=users"
id = "cn"
def check_password(self, password):
@ -24,7 +24,7 @@ class User(LDAPObjectHelper):
class Client(LDAPObjectHelper, ClientMixin):
objectClass = ["oauthClient"]
base = "ou=clients,dc=mydomain,dc=tld"
base = "ou=clients"
id = "oauthClientID"
@property
@ -93,7 +93,7 @@ class Client(LDAPObjectHelper, ClientMixin):
class AuthorizationCode(LDAPObjectHelper, AuthorizationCodeMixin):
objectClass = ["oauthAuthorizationCode"]
base = "ou=authorizations,dc=mydomain,dc=tld"
base = "ou=authorizations"
id = "oauthCode"
def get_redirect_uri(self):
@ -121,7 +121,7 @@ class AuthorizationCode(LDAPObjectHelper, AuthorizationCodeMixin):
class Token(LDAPObjectHelper, TokenMixin):
objectClass = ["oauthToken"]
base = "ou=tokens,dc=mydomain,dc=tld"
base = "ou=tokens"
id = "oauthAccessToken"
def get_client_id(self):