Skip to content
This repository was archived by the owner on Jun 1, 2023. It is now read-only.

Commit 0bf76ef

Browse files
committed
Generalized the configuration handling.
1 parent 2a50753 commit 0bf76ef

4 files changed

Lines changed: 165 additions & 38 deletions

File tree

example/flask_rp/wsgi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
template_dir = os.path.join(dir_path, 'templates')
2222

2323
_config = create_from_config_file(Configuration,
24-
entity_conf_class=RPConfiguration,
24+
entity_conf=[{"class": RPConfiguration, "attr": "rp"}],
2525
filename=conf)
2626

2727
app = application.oidc_provider_init_app(_config.rp, name, template_folder=template_dir)

src/oidcrp/configure.py

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import json
44
import logging
55
import os
6+
from typing import Any
67
from typing import Dict
78
from typing import List
89
from typing import Optional
910

1011
from oidcrp.logging import configure_logging
1112
from oidcrp.util import load_yaml_config
1213
from oidcrp.util import lower_or_upper
13-
from oidcrp.util import replace
1414

1515
try:
1616
from secrets import token_urlsafe as rnd_token
@@ -87,6 +87,7 @@ class RPConfiguration(Base):
8787
def __init__(self,
8888
conf: Dict,
8989
base_path: Optional[str] = '',
90+
entity_conf: Optional[List[dict]] = None,
9091
domain: Optional[str] = "127.0.0.1",
9192
port: Optional[int] = 80,
9293
file_attributes: Optional[List[str]] = None,
@@ -100,19 +101,14 @@ def __init__(self,
100101

101102
self.keys = _keys_conf
102103

104+
if not domain:
105+
domain = conf.get("domain", "127.0.0.1")
106+
107+
if not port:
108+
port = conf.get("port", 80)
109+
103110
conf = set_domain_and_port(conf, URIS, domain, port)
104111
self.clients = lower_or_upper(conf, "clients")
105-
# if _clients:
106-
# format_args = {"domain": domain, "port": port}
107-
# for key, spec in _clients.items():
108-
# if key == "":
109-
# continue
110-
#
111-
# for uri in ['redirect_uris', 'post_logout_redirect_uris', 'frontchannel_logout_uri',
112-
# 'backchannel_logout_uri', 'issuer']:
113-
# replace(spec, uri, **format_args)
114-
#
115-
# self.clients = _clients
116112

117113
hash_seed = lower_or_upper(conf, 'hash_seed')
118114
if not hash_seed:
@@ -129,13 +125,13 @@ class Configuration(Base):
129125

130126
def __init__(self,
131127
conf: Dict,
132-
entity_conf_class,
133128
base_path: str = '',
129+
entity_conf: Optional[List[dict]] = None,
134130
file_attributes: Optional[List[str]] = None,
135131
domain: Optional[str] = "",
136132
port: Optional[int] = 0,
137133
):
138-
Base.__init__(self, conf, base_path, file_attributes)
134+
Base.__init__(self, conf, base_path=base_path, file_attributes=file_attributes)
139135

140136
log_conf = conf.get('logging')
141137
if log_conf:
@@ -152,37 +148,42 @@ def __init__(self,
152148
if not port:
153149
port = conf.get("port", 80)
154150

155-
self.rp = entity_conf_class(conf, base_path=base_path, file_attributes=file_attributes,
156-
domain=domain, port=port)
151+
if entity_conf:
152+
for econf in entity_conf:
153+
_path = econf.get("path")
154+
_cnf = conf
155+
if _path:
156+
for step in _path:
157+
_cnf = _cnf[step]
158+
_attr = econf["attr"]
159+
_cls = econf["class"]
160+
setattr(self, _attr,
161+
_cls(_cnf, base_path=base_path, file_attributes=file_attributes,
162+
domain=domain, port=port))
157163

158164

159165
def create_from_config_file(cls,
160-
entity_conf_class,
161166
filename: str,
162-
base_path: str = '',
167+
base_path: Optional[str] = '',
168+
entity_conf: Optional[List[dict]] = None,
163169
file_attributes: Optional[List[str]] = None,
164170
domain: Optional[str] = "",
165171
port: Optional[int] = 0):
166172
if filename.endswith(".yaml"):
167173
"""Load configuration as YAML"""
168-
return cls(load_yaml_config(filename),
169-
entity_conf_class=entity_conf_class,
170-
base_path=base_path, file_attributes=file_attributes,
171-
domain=domain, port=port)
174+
_cnf = load_yaml_config(filename)
172175
elif filename.endswith(".json"):
173176
_str = open(filename).read()
174-
return cls(json.loads(_str),
175-
entity_conf_class=entity_conf_class,
176-
base_path=base_path, file_attributes=file_attributes, domain=domain, port=port)
177+
_cnf = json.loads(_str)
177178
elif filename.endswith(".py"):
178179
head, tail = os.path.split(filename)
179180
tail = tail[:-3]
180181
module = importlib.import_module(tail)
181182
_cnf = getattr(module, "CONFIG")
183+
else:
184+
raise ValueError("Unknown file type")
182185

183-
# _str = open(filename).read()
184-
# _cnf = ast.literal_eval(_str)
185-
return cls(_cnf,
186-
entity_conf_class=entity_conf_class,
187-
base_path=base_path, file_attributes=file_attributes,
188-
domain=domain, port=port)
186+
return cls(_cnf,
187+
entity_conf=entity_conf,
188+
base_path=base_path, file_attributes=file_attributes,
189+
domain=domain, port=port)

tests/rp_conf.yaml

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
port: &port 8090
2+
domain: &domain 127.0.0.1
3+
base_url: "https://{domain}:{port}"
4+
5+
httpc_params:
6+
# This is just for testing a local usage. In all other cases it MUST be True
7+
verify: false
8+
# Client side
9+
#client_cert: "certs/client.crt"
10+
#client_key: "certs/client.key"
11+
12+
keydefs: &keydef
13+
- "type": "RSA"
14+
"key": ''
15+
"use": ["sig"]
16+
- "type": "EC"
17+
"crv": "P-256"
18+
"use": ["sig"]
19+
20+
rp_keys:
21+
'private_path': 'private/jwks.json'
22+
'key_defs': *keydef
23+
'public_path': 'static/jwks.json'
24+
# this will create the jwks files if they are absent
25+
'read_only': False
26+
27+
client_preferences: &id001
28+
application_name: rphandler
29+
application_type: web
30+
contacts:
31+
- ops@example.com
32+
response_types:
33+
- code
34+
scope:
35+
- openid
36+
- profile
37+
- email
38+
- address
39+
- phone
40+
token_endpoint_auth_method:
41+
- client_secret_basic
42+
- client_secret_post
43+
44+
services: &id002
45+
discovery: &disc
46+
class: oidcservice.oidc.provider_info_discovery.ProviderInfoDiscovery
47+
kwargs: {}
48+
registration: &regist
49+
class: oidcservice.oidc.registration.Registration
50+
kwargs: {}
51+
authorization: &authz
52+
class: oidcservice.oidc.authorization.Authorization
53+
kwargs: {}
54+
accesstoken: &acctok
55+
class: oidcservice.oidc.access_token.AccessToken
56+
kwargs: {}
57+
userinfo: &userinfo
58+
class: oidcservice.oidc.userinfo.UserInfo
59+
kwargs: {}
60+
end_session: &sess
61+
class: oidcservice.oidc.end_session.EndSession
62+
kwargs: {}
63+
64+
clients:
65+
"":
66+
client_preferences: *id001
67+
redirect_uris: None
68+
services: *id002
69+
flop:
70+
client_preferences: *id001
71+
issuer: https://127.0.0.1:5000/
72+
redirect_uris:
73+
- 'https://{domain}:{port}/authz_cb/flop'
74+
post_logout_redirect_uris:
75+
- "https://{domain}:{port}/session_logout/flop"
76+
frontchannel_logout_uri: "https://{domain}:{port}/fc_logout/flop"
77+
frontchannel_logout_session_required: True
78+
backchannel_logout_uri: "https://{domain}:{port}/bc_logout/flop"
79+
backchannel_logout_session_required: True
80+
services:
81+
discovery: *disc
82+
registration: *regist
83+
authorization: *authz
84+
accesstoken: *acctok
85+
userinfo: *userinfo
86+
end_session: *sess
87+
add_ons:
88+
pkce:
89+
function: oidcservice.oidc.add_on.pkce.add_pkce_support
90+
kwargs:
91+
code_challenge_length: 64
92+
code_challenge_method: S256
93+
# status_check:
94+
# function: oidcservice.oidc.add_on.status_check.add_status_check_support
95+
# kwargs:
96+
# rp_iframe_path: "templates/rp_iframe.html"
97+
bobcat:
98+
client_id: client3
99+
client_secret: 'abcdefghijklmnop'
100+
client_preferences: *id001
101+
issuer: http://127.0.0.1:8080/
102+
jwks_uri: 'static/jwks.json'
103+
redirect_uris: ['https://{domain}:{port}/authz_cb/bobcat']
104+
post_logout_redirect_uris:
105+
- "https://{domain}:{port}/session_logout/bobcat"
106+
services: *id002
107+
request_args:
108+
claims:
109+
id_token:
110+
acr:
111+
essential:
112+
true

tests/test_22_config.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,29 @@
88

99

1010
def test_yaml_config():
11-
c = create_from_config_file(cls=Configuration, entity_conf_class=RPConfiguration,
11+
c = create_from_config_file(Configuration,
12+
entity_conf=[{"class": RPConfiguration, "attr": "rp"}],
1213
filename=os.path.join(_dirname, 'conf.yaml'),
1314
base_path=_dirname)
1415
assert c
1516
assert set(c.web_conf.keys()) == {'port', 'domain', 'server_cert', 'server_key', 'debug'}
1617

17-
entity_config = c.entity
18-
assert entity_config.base_url == "https://127.0.0.1:8090"
19-
assert entity_config.httpc_params == {"verify": False}
20-
assert set(entity_config.services.keys()) == {'discovery', 'registration', 'authorization',
18+
rp_config = c.rp
19+
assert rp_config.base_url == "https://127.0.0.1:8090"
20+
assert rp_config.httpc_params == {"verify": False}
21+
assert set(rp_config.services.keys()) == {'discovery', 'registration', 'authorization',
22+
'accesstoken', 'userinfo', 'end_session'}
23+
assert set(rp_config.clients.keys()) == {'', 'bobcat', 'flop'}
24+
25+
26+
def test_dict():
27+
configuration = create_from_config_file(RPConfiguration,
28+
filename=os.path.join(_dirname, 'rp_conf.yaml'),
29+
base_path=_dirname)
30+
assert configuration
31+
32+
assert configuration.base_url == "https://127.0.0.1:8090"
33+
assert configuration.httpc_params == {"verify": False}
34+
assert set(configuration.services.keys()) == {'discovery', 'registration', 'authorization',
2135
'accesstoken', 'userinfo', 'end_session'}
22-
assert set(entity_config.clients.keys()) == {'', 'bobcat', 'flop'}
36+
assert set(configuration.clients.keys()) == {'', 'bobcat', 'flop'}

0 commit comments

Comments
 (0)