Skip to content

Commit 6535f9e

Browse files
authored
Merge pull request #70 from dlt-hub/feat/auth_improvements
auth improvements: select correct global auth, add secrets to secrets.toml, add note in readme if secret present
2 parents 52995d1 + f6fa15b commit 6535f9e

File tree

9 files changed

+65
-7
lines changed

9 files changed

+65
-7
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,7 @@ And use it with the config argument:
125125
```console
126126
$ dlt-openapi init pokemon --url ... --config config.yml
127127
```
128+
129+
## Implementation notes
130+
* OAuth Authentication currently is not natively supported, you can supply your own
131+
* Per endpoint authentication currently is not supported by the generator, only the first globally set securityScheme will be applied. You can add your own per endpoint if you need to.

dlt_openapi/detector/default/__init__.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,10 @@ def run(self, open_api: OpenapiParser) -> None:
8686
self._add_warning(UnresolvedPathParametersWarning(params), e)
8787

8888
def detect_security_schemes(self, open_api: OpenapiParser) -> None:
89-
schemes = list(open_api.security_schemes.values())
9089

9190
# detect scheme settings
9291
# TODO: make this a bit nicer
93-
for scheme in schemes:
92+
for name, scheme in open_api.security_schemes.items():
9493

9594
if scheme.type == "apiKey":
9695
scheme.detected_secret_name = "api_key"
@@ -115,10 +114,17 @@ def detect_security_schemes(self, open_api: OpenapiParser) -> None:
115114
"""
116115

117116
# find default scheme
118-
if len(schemes) and schemes[0].supported:
119-
open_api.detected_global_security_scheme = schemes[0]
120-
elif len(schemes) and not schemes[0].supported:
121-
self._add_warning(UnsupportedSecuritySchemeWarning(schemes[0].type))
117+
if open_api.global_security_name:
118+
global_scheme = None
119+
for name, scheme in open_api.security_schemes.items():
120+
if name == open_api.global_security_name:
121+
global_scheme = scheme
122+
break
123+
124+
if global_scheme and global_scheme.supported:
125+
open_api.detected_global_security_scheme = global_scheme
126+
elif global_scheme and not global_scheme.supported:
127+
self._add_warning(UnsupportedSecuritySchemeWarning(global_scheme.type))
122128

123129
def detect_resource_names(self, endpoints: EndpointCollection) -> None:
124130
"""iterate all endpoints and find a strategy to select the right resource name"""

dlt_openapi/parser/openapi_parser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@ class OpenapiParser:
2222
endpoints: EndpointCollection = None
2323
security_schemes: Dict[str, SecurityScheme] = {}
2424

25+
global_security_name: str = None
26+
2527
detected_global_security_scheme: SecurityScheme = None
2628
detected_global_pagination: Pagination = None
2729

2830
def __init__(self, config: Config) -> None:
2931
self.config = config
3032

3133
def parse(self, data: bytes) -> None:
34+
3235
self.spec_raw = self._load_yaml_or_json(data)
3336
self.security_schemes = {}
3437

@@ -45,6 +48,8 @@ def parse(self, data: bytes) -> None:
4548
logger.success("Completed extracting openapi metadata and credentials.")
4649

4750
logger.info("Extracting security schemes")
51+
if spec.security:
52+
self.global_security_name = list(spec.security[0].keys())[0]
4853
if self.context.spec.components and self.context.spec.components.securitySchemes:
4954
for name, scheme in self.context.spec.components.securitySchemes.items():
5055
self.security_schemes[name] = SecurityScheme(

dlt_openapi/renderer/default/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def run(self, openapi: OpenapiParser, dry: bool = False) -> None:
5454
class_name=lambda x: misc.ClassName(x, ""),
5555
package_name=self.package_name,
5656
project_name=self.config.project_name,
57+
credentials=self.openapi.detected_global_security_scheme,
5758
)
5859

5960
if dry:
@@ -110,6 +111,13 @@ def _build_dlt_config(self) -> None:
110111
encoding=FILE_ENCODING,
111112
)
112113

114+
secrets_template = self.env.get_template("dlt_secrets.toml.j2")
115+
secrets_path = config_dir / "secrets.toml"
116+
secrets_path.write_text(
117+
secrets_template.render(),
118+
encoding=FILE_ENCODING,
119+
)
120+
113121
def _build_source(self) -> None:
114122
module_path = self.package_dir / "__init__.py"
115123
module_path.write_text(
@@ -123,7 +131,6 @@ def _render_source(self) -> str:
123131
source_name=self.source_name,
124132
endpoint_collection=self.openapi.endpoints,
125133
imports=[],
126-
credentials=self.openapi.detected_global_security_scheme,
127134
global_paginator_config=(
128135
self.openapi.detected_global_pagination.paginator_config
129136
if self.openapi.detected_global_pagination

dlt_openapi/renderer/default/templates/README.md.j2

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ Created with [dlt-openapi](https://github.com/dlt-hub/dlt-openapi) v. {{version}
66
* https://github.com/dlt-hub/dlt
77
* https://github.com/dlt-hub/dlt-openapi
88

9+
{% if credentials %}
10+
## Credentials
11+
This API uses {{ credentials.type }} authentication. Please fill in the required variable {{ credentials.detected_secret_name }} in your
12+
secrets.toml.
13+
{% endif %}
14+
915
## Available resources
1016
{% for endpoint in endpoint_collection.all_endpoints_to_render %}
1117
* {{ endpoint.detected_resource_name }}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{% if credentials %}
2+
{{ credentials.detected_secret_name }} = "FILL ME OUT" # TODO: fill in your credentials
3+
{% endif %}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
openapi: 3.0.0
2+
info:
3+
title: 'basic, but not set in security section'
4+
version: 1.0.0
5+
servers:
6+
- url: 'https://pokeapi.co/'
7+
components:
8+
securitySchemes:
9+
basicAuth: # <-- arbitrary name for the security scheme
10+
type: http
11+
scheme: basic
12+
# security:
13+
# - basicAuth: [] # <-- use the same name here
14+
paths:
15+
/api/v2/pokemon/:
16+
get:
17+
operationId: pokemon_list
18+
responses:
19+
'200':
20+
description: OK

tests/cases/artificial_specs/auth/oauth.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ components:
1414
scopes:
1515
read: Grants read access
1616
write: Grants write access
17+
security:
18+
- oauth2: []
1719
paths:
1820
/api/v2/pokemon/:
1921
get:

tests/integration/basics/test_auth.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ def test_basic_auth() -> None:
3030
}
3131

3232

33+
def test_unused_auth() -> None:
34+
source_dict = get_dict_by_case("artificial", "auth/basic_auth_not_used.yml")
35+
assert not source_dict["client"].get("auth")
36+
37+
3338
def test_oauth_warning() -> None:
3439
source_dict = get_dict_by_case("artificial", "auth/oauth.yml")
3540
assert not source_dict["client"].get("auth")

0 commit comments

Comments
 (0)