cdk-factory 0.13.0__py3-none-any.whl → 0.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cdk-factory might be problematic. Click here for more details.
- cdk_factory/lambdas/edge/ip_gate/handler.py +163 -80
- cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +48 -6
- cdk_factory/version.py +1 -1
- {cdk_factory-0.13.0.dist-info → cdk_factory-0.13.2.dist-info}/METADATA +1 -1
- {cdk_factory-0.13.0.dist-info → cdk_factory-0.13.2.dist-info}/RECORD +8 -8
- {cdk_factory-0.13.0.dist-info → cdk_factory-0.13.2.dist-info}/WHEEL +0 -0
- {cdk_factory-0.13.0.dist-info → cdk_factory-0.13.2.dist-info}/entry_points.txt +0 -0
- {cdk_factory-0.13.0.dist-info → cdk_factory-0.13.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,104 +1,187 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Lambda@Edge
|
|
2
|
+
Lambda@Edge function for IP-based access gating.
|
|
3
3
|
Geek Cafe, LLC
|
|
4
4
|
Maintainers: Eric Wilson
|
|
5
|
-
"""
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
Since Lambda@Edge does not support environment variables, configuration
|
|
7
|
+
is fetched from SSM Parameter Store at runtime (with caching).
|
|
8
|
+
"""
|
|
8
9
|
import json
|
|
9
|
-
import
|
|
10
|
+
import ipaddress
|
|
11
|
+
import boto3
|
|
12
|
+
from functools import lru_cache
|
|
10
13
|
|
|
14
|
+
# SSM client - will be created in the region where the function executes
|
|
15
|
+
ssm = None
|
|
11
16
|
|
|
12
|
-
|
|
17
|
+
@lru_cache(maxsize=128)
|
|
18
|
+
def get_ssm_parameter(parameter_name: str, region: str = 'us-east-1') -> str:
|
|
13
19
|
"""
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
Fetch SSM parameter with caching.
|
|
21
|
+
Lambda@Edge cannot use environment variables, so we fetch from SSM.
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- Rewrite blocked IPs to maintenance CloudFront distribution, and serve up maintenance site
|
|
21
|
-
- Toggle via GATE_ENABLED environment variable
|
|
22
|
-
"""
|
|
23
|
+
Args:
|
|
24
|
+
parameter_name: Name of the SSM parameter
|
|
25
|
+
region: AWS region (default us-east-1)
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
Returns:
|
|
28
|
+
Parameter value
|
|
29
|
+
"""
|
|
30
|
+
global ssm
|
|
31
|
+
if ssm is None:
|
|
32
|
+
ssm = boto3.client('ssm', region_name=region)
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
try:
|
|
35
|
+
response = ssm.get_parameter(Name=parameter_name, WithDecryption=False)
|
|
36
|
+
return response['Parameter']['Value']
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print(f"Error fetching SSM parameter {parameter_name}: {str(e)}")
|
|
39
|
+
raise
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_client_ip(request):
|
|
43
|
+
"""Extract client IP from CloudFront request."""
|
|
44
|
+
if 'clientIp' in request:
|
|
45
|
+
return request['clientIp']
|
|
32
46
|
|
|
33
|
-
#
|
|
34
|
-
|
|
47
|
+
# Fallback to headers
|
|
48
|
+
headers = request.get('headers', {})
|
|
49
|
+
if 'x-forwarded-for' in headers:
|
|
50
|
+
xff = headers['x-forwarded-for'][0]['value']
|
|
51
|
+
return xff.split(',')[0].strip()
|
|
35
52
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def is_ip_allowed(client_ip: str, allowed_cidrs: list) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Check if client IP is in any of the allowed CIDR ranges.
|
|
39
59
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
'
|
|
43
|
-
}]
|
|
60
|
+
Args:
|
|
61
|
+
client_ip: Client IP address
|
|
62
|
+
allowed_cidrs: List of CIDR ranges (e.g., ['10.0.0.0/8', '192.168.1.0/24'])
|
|
44
63
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
Returns:
|
|
65
|
+
True if IP is allowed, False otherwise
|
|
66
|
+
"""
|
|
67
|
+
if not client_ip:
|
|
68
|
+
return False
|
|
48
69
|
|
|
49
|
-
# Check if IP is in allowlist
|
|
50
|
-
ip_allowed = False
|
|
51
70
|
try:
|
|
52
71
|
client_ip_obj = ipaddress.ip_address(client_ip)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if client_ip_obj in network:
|
|
57
|
-
ip_allowed = True
|
|
58
|
-
break
|
|
59
|
-
except (ValueError, ipaddress.AddressValueError):
|
|
60
|
-
# Invalid CIDR, skip
|
|
61
|
-
continue
|
|
62
|
-
except (ValueError, ipaddress.AddressValueError):
|
|
63
|
-
# Invalid client IP, block by default
|
|
64
|
-
ip_allowed = False
|
|
72
|
+
except ValueError as e:
|
|
73
|
+
print(f"Invalid client IP address: {e}")
|
|
74
|
+
return False
|
|
65
75
|
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
# Check each CIDR individually, skipping invalid ones
|
|
77
|
+
for cidr in allowed_cidrs:
|
|
78
|
+
try:
|
|
79
|
+
network = ipaddress.ip_network(cidr.strip(), strict=False)
|
|
80
|
+
if client_ip_obj in network:
|
|
81
|
+
return True
|
|
82
|
+
except ValueError as e:
|
|
83
|
+
# Invalid CIDR, log and continue checking others
|
|
84
|
+
print(f"Invalid CIDR '{cidr}': {e}")
|
|
85
|
+
continue
|
|
69
86
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def lambda_handler(event, context):
|
|
91
|
+
"""
|
|
92
|
+
Lambda@Edge function for IP-based gating.
|
|
75
93
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
'port': 443,
|
|
81
|
-
'protocol': 'https',
|
|
82
|
-
'path': '',
|
|
83
|
-
'sslProtocols': ['TLSv1.2'],
|
|
84
|
-
'readTimeout': 30,
|
|
85
|
-
'keepaliveTimeout': 5,
|
|
86
|
-
'customHeaders': {}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
94
|
+
Configuration (fetched from SSM Parameter Store):
|
|
95
|
+
- GATE_ENABLED: Whether IP gating is enabled (true/false)
|
|
96
|
+
- ALLOW_CIDRS: Comma-separated list of allowed CIDR ranges
|
|
97
|
+
- MAINT_CF_HOST: CloudFront domain for maintenance/lockout page
|
|
89
98
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
Runtime configuration is bundled in runtime_config.json by CDK.
|
|
100
|
+
SSM parameter paths are auto-generated by CDK as:
|
|
101
|
+
/{environment}/{full-function-name}/{env-var-name-kebab-case}
|
|
102
|
+
"""
|
|
103
|
+
request = event['Records'][0]['cf']['request']
|
|
104
|
+
|
|
105
|
+
# Load runtime configuration bundled by CDK
|
|
106
|
+
# This file is created during deployment and contains environment, function name, etc.
|
|
107
|
+
try:
|
|
108
|
+
with open('runtime_config.json', 'r') as f:
|
|
109
|
+
runtime_config = json.load(f)
|
|
110
|
+
|
|
111
|
+
env = runtime_config.get('environment', 'dev')
|
|
112
|
+
function_base_name = runtime_config.get('function_name', 'ip-gate')
|
|
113
|
+
|
|
114
|
+
print(f"Runtime config loaded: environment={env}, function_name={function_base_name}")
|
|
115
|
+
except FileNotFoundError:
|
|
116
|
+
# Fallback: extract from Lambda context (less reliable)
|
|
117
|
+
print("Warning: runtime_config.json not found, falling back to function name parsing")
|
|
118
|
+
function_full_name = context.function_name
|
|
119
|
+
|
|
120
|
+
# Parse environment from function name as fallback
|
|
121
|
+
parts = function_full_name.split('-')
|
|
122
|
+
common_envs = ['dev', 'prod', 'staging', 'test', 'qa', 'uat']
|
|
123
|
+
env = 'dev'
|
|
124
|
+
|
|
125
|
+
for part in parts:
|
|
126
|
+
if part in common_envs:
|
|
127
|
+
env = part
|
|
128
|
+
break
|
|
129
|
+
|
|
130
|
+
function_base_name = 'ip-gate'
|
|
131
|
+
print(f"Fallback: environment={env}, function_name={function_base_name}")
|
|
95
132
|
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
request['uri'] = uri + 'index.html'
|
|
100
|
-
elif '.' not in uri.split('/')[-1]:
|
|
101
|
-
# No file extension, likely a directory
|
|
102
|
-
request['uri'] = uri + '/index.html'
|
|
133
|
+
# Full function name for SSM paths
|
|
134
|
+
function_name = context.function_name
|
|
135
|
+
print(f"Lambda function ARN: {context.invoked_function_arn}")
|
|
103
136
|
|
|
104
|
-
|
|
137
|
+
try:
|
|
138
|
+
# Fetch configuration from SSM Parameter Store
|
|
139
|
+
# Auto-generated paths: /{env}/{function-name}/{key}
|
|
140
|
+
gate_enabled = get_ssm_parameter(f'/{env}/{function_name}/gate-enabled', 'us-east-1')
|
|
141
|
+
|
|
142
|
+
# If gating is disabled, allow all traffic
|
|
143
|
+
if gate_enabled.lower() not in ('true', '1', 'yes'):
|
|
144
|
+
print(f"IP gating is disabled (GATE_ENABLED={gate_enabled})")
|
|
145
|
+
return request
|
|
146
|
+
|
|
147
|
+
# Get allowed CIDRs and maintenance host
|
|
148
|
+
allow_cidrs_str = get_ssm_parameter(f'/{env}/{function_name}/allow-cidrs', 'us-east-1')
|
|
149
|
+
maint_cf_host = get_ssm_parameter(f'/{env}/{function_name}/maint-cf-host', 'us-east-1')
|
|
150
|
+
|
|
151
|
+
# Parse allowed CIDRs
|
|
152
|
+
allowed_cidrs = [cidr.strip() for cidr in allow_cidrs_str.split(',') if cidr.strip()]
|
|
153
|
+
|
|
154
|
+
# Get client IP
|
|
155
|
+
client_ip = get_client_ip(request)
|
|
156
|
+
print(f"Client IP: {client_ip}")
|
|
157
|
+
|
|
158
|
+
# Check if IP is allowed
|
|
159
|
+
if is_ip_allowed(client_ip, allowed_cidrs):
|
|
160
|
+
print(f"IP {client_ip} is allowed")
|
|
161
|
+
return request
|
|
162
|
+
|
|
163
|
+
# IP not allowed - redirect to maintenance page
|
|
164
|
+
print(f"IP {client_ip} is NOT allowed, redirecting to {maint_cf_host}")
|
|
165
|
+
|
|
166
|
+
response = {
|
|
167
|
+
'status': '302',
|
|
168
|
+
'statusDescription': 'Found',
|
|
169
|
+
'headers': {
|
|
170
|
+
'location': [{
|
|
171
|
+
'key': 'Location',
|
|
172
|
+
'value': f'https://{maint_cf_host}'
|
|
173
|
+
}],
|
|
174
|
+
'cache-control': [{
|
|
175
|
+
'key': 'Cache-Control',
|
|
176
|
+
'value': 'no-cache, no-store, must-revalidate'
|
|
177
|
+
}]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return response
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print(f"Error in IP gating function: {str(e)}")
|
|
185
|
+
# On error, allow the request to proceed (fail open)
|
|
186
|
+
# Change this to fail closed if preferred
|
|
187
|
+
return request
|
|
@@ -9,6 +9,9 @@ MIT License. See Project Root for the license information.
|
|
|
9
9
|
from typing import Optional, Dict
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
import json
|
|
12
|
+
import tempfile
|
|
13
|
+
import shutil
|
|
14
|
+
import importlib.resources
|
|
12
15
|
|
|
13
16
|
import aws_cdk as cdk
|
|
14
17
|
from aws_cdk import aws_lambda as _lambda
|
|
@@ -126,11 +129,38 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
126
129
|
def _create_lambda_function(self, function_name: str) -> None:
|
|
127
130
|
"""Create the Lambda function"""
|
|
128
131
|
|
|
129
|
-
# Resolve code path
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
# Resolve code path - support package references (e.g., "cdk_factory:lambdas/edge/ip_gate")
|
|
133
|
+
code_path_str = self.edge_config.code_path
|
|
134
|
+
|
|
135
|
+
if ':' in code_path_str:
|
|
136
|
+
# Package reference format: "package_name:path/within/package"
|
|
137
|
+
package_name, package_path = code_path_str.split(':', 1)
|
|
138
|
+
logger.info(f"Resolving package reference: {package_name}:{package_path}")
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
# Get the package's installed location
|
|
142
|
+
if hasattr(importlib.resources, 'files'):
|
|
143
|
+
# Python 3.9+
|
|
144
|
+
package_root = importlib.resources.files(package_name)
|
|
145
|
+
code_path = Path(str(package_root / package_path))
|
|
146
|
+
else:
|
|
147
|
+
# Fallback for older Python
|
|
148
|
+
import pkg_resources
|
|
149
|
+
package_root = pkg_resources.resource_filename(package_name, '')
|
|
150
|
+
code_path = Path(package_root) / package_path
|
|
151
|
+
|
|
152
|
+
logger.info(f"Resolved package path to: {code_path}")
|
|
153
|
+
except Exception as e:
|
|
154
|
+
raise FileNotFoundError(
|
|
155
|
+
f"Could not resolve package reference '{code_path_str}': {e}\n"
|
|
156
|
+
f"Make sure package '{package_name}' is installed."
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
# Regular file path
|
|
160
|
+
code_path = Path(code_path_str)
|
|
161
|
+
if not code_path.is_absolute():
|
|
162
|
+
# Assume relative to the project root
|
|
163
|
+
code_path = Path.cwd() / code_path
|
|
134
164
|
|
|
135
165
|
if not code_path.exists():
|
|
136
166
|
raise FileNotFoundError(
|
|
@@ -140,6 +170,15 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
140
170
|
|
|
141
171
|
logger.info(f"Loading Lambda code from: {code_path}")
|
|
142
172
|
|
|
173
|
+
# Create isolated temp directory for this function instance
|
|
174
|
+
# This prevents conflicts when multiple functions use the same handler code
|
|
175
|
+
temp_code_dir = Path(tempfile.mkdtemp(prefix=f"{function_name.replace('/', '-')}-"))
|
|
176
|
+
logger.info(f"Creating isolated code directory at: {temp_code_dir}")
|
|
177
|
+
|
|
178
|
+
# Copy source code to temp directory
|
|
179
|
+
shutil.copytree(code_path, temp_code_dir, dirs_exist_ok=True)
|
|
180
|
+
logger.info(f"Copied code from {code_path} to {temp_code_dir}")
|
|
181
|
+
|
|
143
182
|
# Create runtime configuration file for Lambda@Edge
|
|
144
183
|
# Since Lambda@Edge doesn't support environment variables, we bundle a config file
|
|
145
184
|
runtime_config = {
|
|
@@ -148,7 +187,7 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
148
187
|
'region': self.deployment.region
|
|
149
188
|
}
|
|
150
189
|
|
|
151
|
-
runtime_config_path =
|
|
190
|
+
runtime_config_path = temp_code_dir / 'runtime_config.json'
|
|
152
191
|
logger.info(f"Creating runtime config at: {runtime_config_path}")
|
|
153
192
|
|
|
154
193
|
with open(runtime_config_path, 'w') as f:
|
|
@@ -156,6 +195,9 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
156
195
|
|
|
157
196
|
logger.info(f"Runtime config: {runtime_config}")
|
|
158
197
|
|
|
198
|
+
# Use the temp directory for the Lambda code asset
|
|
199
|
+
code_path = temp_code_dir
|
|
200
|
+
|
|
159
201
|
# Map runtime string to CDK Runtime
|
|
160
202
|
runtime_map = {
|
|
161
203
|
"python3.11": _lambda.Runtime.PYTHON_3_11,
|
cdk_factory/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.13.
|
|
1
|
+
__version__ = "0.13.2"
|
|
@@ -2,7 +2,7 @@ cdk_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
2
2
|
cdk_factory/app.py,sha256=RnX0-pwdTAPAdKJK_j13Zl8anf9zYKBwboR0KA8K8xM,10346
|
|
3
3
|
cdk_factory/cdk.json,sha256=SKZKhJ2PBpFH78j-F8S3VDYW-lf76--Q2I3ON-ZIQfw,3106
|
|
4
4
|
cdk_factory/cli.py,sha256=FGbCTS5dYCNsfp-etshzvFlGDCjC28r6rtzYbe7KoHI,6407
|
|
5
|
-
cdk_factory/version.py,sha256=
|
|
5
|
+
cdk_factory/version.py,sha256=blu6md2c3Nnj5gDBi8U36sYO3k8HcND8s7UoQBjfn3g,23
|
|
6
6
|
cdk_factory/builds/README.md,sha256=9BBWd7bXpyKdMU_g2UljhQwrC9i5O_Tvkb6oPvndoZk,90
|
|
7
7
|
cdk_factory/commands/command_loader.py,sha256=QbLquuP_AdxtlxlDy-2IWCQ6D-7qa58aphnDPtp_uTs,3744
|
|
8
8
|
cdk_factory/configurations/base_config.py,sha256=JKjhNsy0RCUZy1s8n5D_aXXI-upR9izaLtCTfKYiV9k,9624
|
|
@@ -66,7 +66,7 @@ cdk_factory/interfaces/istack.py,sha256=bhTBs-o9FgKwvJMSuwxjUV6D3nUlvZHVzfm27jP9
|
|
|
66
66
|
cdk_factory/interfaces/live_ssm_resolver.py,sha256=3FIr9a02SXqZmbFs3RT0WxczWEQR_CF7QSt7kWbDrVE,8163
|
|
67
67
|
cdk_factory/interfaces/ssm_parameter_mixin.py,sha256=uA2j8HmAOpuEA9ynRj51s0WjUHMVLsbLQN-QS9NKyHA,12089
|
|
68
68
|
cdk_factory/lambdas/health_handler.py,sha256=dd40ykKMxWCFEIyp2ZdQvAGNjw_ylI9CSm1N24Hp2ME,196
|
|
69
|
-
cdk_factory/lambdas/edge/ip_gate/handler.py,sha256=
|
|
69
|
+
cdk_factory/lambdas/edge/ip_gate/handler.py,sha256=NCa16_B66zIczPb9whf_CaspnetuTYKTFpIX6Z7Wqjw,6393
|
|
70
70
|
cdk_factory/pipeline/path_utils.py,sha256=fvWdrcb4onmpIu1APkHLhXg8zWfK74HcW3Ra2ynxfXM,2586
|
|
71
71
|
cdk_factory/pipeline/pipeline_factory.py,sha256=rvtkdlTPJG477nTVRN8S2ksWt4bwpd9eVLFd9WO02pM,17248
|
|
72
72
|
cdk_factory/pipeline/stage.py,sha256=Be7ExMB9A-linRM18IQDOzQ-cP_I2_ThRNzlT4FIrUg,437
|
|
@@ -95,7 +95,7 @@ cdk_factory/stack_library/ecr/ecr_stack.py,sha256=1xA68sxFVyqreYjXrP_7U9I8RF9RtF
|
|
|
95
95
|
cdk_factory/stack_library/ecs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
96
96
|
cdk_factory/stack_library/ecs/ecs_service_stack.py,sha256=zuGdZEP5KmeVDTJb-H47LYhvs-85-Fi4Xb78nsA-lF4,24685
|
|
97
97
|
cdk_factory/stack_library/lambda_edge/__init__.py,sha256=ByBJ_CWdc4UtTmFBZH-6pzBMNkjkdtE65AmnB0Fs6lM,156
|
|
98
|
-
cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py,sha256=
|
|
98
|
+
cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py,sha256=fCnS_WFUwe9gyvsqrevevrDcUqZpZgnwM8v9tQVxFzk,15491
|
|
99
99
|
cdk_factory/stack_library/load_balancer/__init__.py,sha256=wZpKw2OecLJGdF5mPayCYAEhu2H3c2gJFFIxwXftGDU,52
|
|
100
100
|
cdk_factory/stack_library/load_balancer/load_balancer_stack.py,sha256=t5JUe5lMUbQCRFZR08k8nO-g-53yWY8gKB9v8ZnedBs,24391
|
|
101
101
|
cdk_factory/stack_library/monitoring/__init__.py,sha256=k1G_KDx47Aw0UugaL99PN_TKlyLK4nkJVApCaAK7GJg,153
|
|
@@ -129,8 +129,8 @@ cdk_factory/utilities/lambda_function_utilities.py,sha256=S1GvBsY_q2cyUiaud3HORJ
|
|
|
129
129
|
cdk_factory/utilities/os_execute.py,sha256=5Op0LY_8Y-pUm04y1k8MTpNrmQvcLmQHPQITEP7EuSU,1019
|
|
130
130
|
cdk_factory/utils/api_gateway_utilities.py,sha256=If7Xu5s_UxmuV-kL3JkXxPLBdSVUKoLtohm0IUFoiV8,4378
|
|
131
131
|
cdk_factory/workload/workload_factory.py,sha256=mM8GU_5mKq_0OyK060T3JrUSUiGAcKf0eqNlT9mfaws,6028
|
|
132
|
-
cdk_factory-0.13.
|
|
133
|
-
cdk_factory-0.13.
|
|
134
|
-
cdk_factory-0.13.
|
|
135
|
-
cdk_factory-0.13.
|
|
136
|
-
cdk_factory-0.13.
|
|
132
|
+
cdk_factory-0.13.2.dist-info/METADATA,sha256=LyiWwDWEaFep_Koxoo46zUxv3Lza_88v4T45EK7zNeg,2451
|
|
133
|
+
cdk_factory-0.13.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
134
|
+
cdk_factory-0.13.2.dist-info/entry_points.txt,sha256=S1DPe0ORcdiwEALMN_WIo3UQrW_g4YdQCLEsc_b0Swg,53
|
|
135
|
+
cdk_factory-0.13.2.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
|
|
136
|
+
cdk_factory-0.13.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|