cdk-factory 0.7.27__py3-none-any.whl → 0.7.28__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.

@@ -349,113 +349,36 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
349
349
 
350
350
  def _validate_authorization_configuration(self, route, has_cognito_authorizer):
351
351
  """
352
- Validate authorization configuration for security and clarity.
353
-
354
- This method implements 'secure by default' with explicit overrides:
355
- - If Cognito is available and route wants NONE auth, requires explicit override
356
- - If Cognito is not available and route wants COGNITO auth, raises error
357
- - Provides verbose warnings for monitoring and security awareness
358
-
359
- Args:
360
- route (dict): Route configuration
361
- has_cognito_authorizer (bool): Whether a Cognito authorizer is configured
362
-
363
- Raises:
364
- ValueError: When there are security conflicts without explicit overrides
352
+ Validate authorization configuration using the shared utility method.
353
+
354
+ This delegates to the ApiGatewayIntegrationUtility for consistent validation
355
+ across both API Gateway stack and Lambda stack patterns.
365
356
  """
366
- import logging
367
-
368
- auth_type = str(route.get("authorization_type", "COGNITO")).upper()
369
- explicit_override = (
370
- str(route.get("allow_public_override", False)).lower() == "true"
357
+ # Convert route dict to ApiGatewayConfigRouteConfig for utility validation
358
+ # Map "path" to "route" for compatibility with the config object
359
+ route_config_dict = dict(route) # Create a copy
360
+ if "path" in route_config_dict:
361
+ route_config_dict["route"] = route_config_dict["path"]
362
+
363
+ api_route_config = ApiGatewayConfigRouteConfig(route_config_dict)
364
+
365
+ # Use the utility's enhanced validation method
366
+ validated_config = self.integration_utility._validate_and_adjust_authorization_configuration(
367
+ api_route_config, has_cognito_authorizer
371
368
  )
372
- route_path = route.get("path", "unknown")
373
- method = route.get("method", "unknown")
374
-
375
- logger = logging.getLogger(__name__)
376
-
377
- # Case 1: Cognito available + NONE requested + No explicit override = ERROR
378
- if has_cognito_authorizer and auth_type == "NONE" and not explicit_override:
379
- error_msg = (
380
- f"🚨 SECURITY CONFLICT DETECTED for route {route_path} ({method}):\n"
381
- f" ❌ Cognito authorizer is configured (manual or auto-import)\n"
382
- f" ❌ authorization_type is set to 'NONE' (public access)\n"
383
- f" ❌ This creates a security risk - public endpoint with auth available\n\n"
384
- f"💡 SOLUTIONS:\n"
385
- f" 1. Remove Cognito configuration if you want public access\n"
386
- f" 2. Add 'allow_public_override': true to explicitly allow public access\n"
387
- f" 3. Remove 'authorization_type': 'NONE' to use secure Cognito auth\n\n"
388
- f"🔒 This prevents accidental public endpoints when authentication is available.\n\n"
389
- f"👉 ApiGatewayStack documentation for more details: https://github.com/your-repo/api-gateway-stack"
390
- )
391
- raise ValueError(error_msg)
392
-
393
- # Case 2: No Cognito + COGNITO explicitly requested = ERROR
394
- # Only error if COGNITO was explicitly requested, not if it's the default
395
- if not has_cognito_authorizer and route.get("authorization_type") == "COGNITO":
396
- error_msg = (
397
- f"🚨 CONFIGURATION ERROR for route {route_path} ({method}):\n"
398
- f" ❌ authorization_type is explicitly set to 'COGNITO' but no Cognito authorizer configured\n"
399
- f" ❌ Cannot secure endpoint without authentication provider\n\n"
400
- f"💡 SOLUTIONS:\n"
401
- f" 1. Add Cognito configuration to enable authentication\n"
402
- f" 2. Set authorization_type to 'NONE' for public access\n"
403
- f" 3. Configure SSM auto-import for user_pool_arn\n"
404
- f" 4. Remove explicit authorization_type to use default behavior"
405
- )
406
- raise ValueError(error_msg)
407
-
408
- # Case 3: Cognito available + NONE requested + Explicit override = WARN
409
- if has_cognito_authorizer and auth_type == "NONE" and explicit_override:
410
- warning_msg = (
411
- f"⚠️ PUBLIC ENDPOINT CONFIGURED: {route_path} ({method})\n"
412
- f" 🔓 This endpoint is intentionally public (allow_public_override: true)\n"
413
- f" 🔐 Cognito authentication is available but overridden\n"
414
- f" 📊 Consider monitoring this endpoint for unexpected usage patterns\n"
415
- f" 🔍 Review periodically: Should this endpoint be secured?"
416
- )
417
-
418
- # Print to console during deployment for visibility
419
- print(warning_msg)
420
-
421
- # Structured logging for monitoring and metrics
422
- logger.warning(
423
- "Public endpoint configured with Cognito available",
424
- extra={
425
- "route": route_path,
426
- "method": method,
427
- "security_override": True,
428
- "cognito_available": True,
429
- "authorization_type": "NONE",
430
- "metric_name": "public_endpoint_with_cognito",
431
- "security_decision": "intentional_public",
432
- "recommendation": "review_periodically",
433
- },
434
- )
435
-
436
- # Case 4: No Cognito + NONE = INFO (expected for public-only APIs)
437
- if not has_cognito_authorizer and auth_type == "NONE":
438
- logger.info(
439
- f"Public endpoint configured (no Cognito available): {route_path} ({method})",
440
- extra={
441
- "route": route_path,
442
- "method": method,
443
- "authorization_type": "NONE",
444
- "cognito_available": False,
445
- "security_decision": "public_only_api",
446
- },
447
- )
369
+
370
+ # Return the validated authorization type for use in the stack
371
+ return validated_config.authorization_type
448
372
 
449
373
  def _setup_lambda_integration(
450
374
  self, api_gateway, api_id, route, lambda_fn, authorizer, suffix
451
375
  ):
452
376
  """Setup Lambda integration for a route"""
453
- import logging
454
-
455
377
  route_path = route["path"]
456
- # Secure by default: require Cognito authorization unless explicitly set to NONE
378
+
379
+ # Handle authorization type fallback logic before validation
457
380
  authorization_type = route.get("authorization_type", "COGNITO")
458
-
381
+
459
382
  # If no Cognito authorizer available and default COGNITO, fall back to NONE
460
383
  if (
461
384
  not authorizer
@@ -463,14 +386,22 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
463
386
  and "authorization_type" not in route
464
387
  ):
465
388
  authorization_type = "NONE"
389
+ import logging
466
390
  logger = logging.getLogger(__name__)
467
391
  logger.info(
468
392
  f"No Cognito authorizer available for route {route_path} ({route.get('method', 'unknown')}), "
469
393
  f"defaulting to public access (NONE authorization)"
470
394
  )
471
-
472
- # Validate authorization configuration for security
473
- self._validate_authorization_configuration(route, authorizer is not None)
395
+
396
+ # Create a route config with the resolved authorization type for validation
397
+ route_for_validation = dict(route)
398
+ route_for_validation["authorization_type"] = authorization_type
399
+
400
+ # Validate authorization configuration using the utility
401
+ validated_authorization_type = self._validate_authorization_configuration(route_for_validation, authorizer is not None)
402
+
403
+ # Use the validated authorization type
404
+ authorization_type = validated_authorization_type
474
405
 
475
406
  # If set to NONE (explicitly or by fallback), skip authorization
476
407
  if authorization_type == "NONE":
@@ -487,6 +418,7 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
487
418
  "user_pool_id": (
488
419
  os.getenv("COGNITO_USER_POOL_ID") if authorizer else None
489
420
  ),
421
+ "allow_public_override": route.get("allow_public_override", False),
490
422
  }
491
423
  )
492
424
 
@@ -508,12 +440,11 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
508
440
  self, api_gateway, route, lambda_fn, authorizer, api_id, suffix
509
441
  ):
510
442
  """Setup fallback Lambda integration for routes without src"""
511
- import logging
512
-
513
443
  route_path = route["path"]
514
- # Secure by default: require Cognito authorization unless explicitly set to NONE
444
+
445
+ # Handle authorization type fallback logic before validation
515
446
  authorization_type = route.get("authorization_type", "COGNITO")
516
-
447
+
517
448
  # If no Cognito authorizer available and default COGNITO, fall back to NONE
518
449
  if (
519
450
  not authorizer
@@ -521,14 +452,22 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
521
452
  and "authorization_type" not in route
522
453
  ):
523
454
  authorization_type = "NONE"
455
+ import logging
524
456
  logger = logging.getLogger(__name__)
525
457
  logger.info(
526
458
  f"No Cognito authorizer available for route {route_path} ({route.get('method', 'unknown')}), "
527
459
  f"defaulting to public access (NONE authorization)"
528
460
  )
529
-
530
- # Validate authorization configuration for security
531
- self._validate_authorization_configuration(route, authorizer is not None)
461
+
462
+ # Create a route config with the resolved authorization type for validation
463
+ route_for_validation = dict(route)
464
+ route_for_validation["authorization_type"] = authorization_type
465
+
466
+ # Validate authorization configuration using the utility
467
+ validated_authorization_type = self._validate_authorization_configuration(route_for_validation, authorizer is not None)
468
+
469
+ # Use the validated authorization type
470
+ authorization_type = validated_authorization_type
532
471
 
533
472
  resource = (
534
473
  api_gateway.root.resource_for_path(route_path)
@@ -1382,15 +1382,19 @@ class ApiGatewayIntegrationUtility:
1382
1382
  modified_config = deepcopy(api_config)
1383
1383
 
1384
1384
  auth_type = str(getattr(api_config, "authorization_type", "COGNITO")).upper()
1385
-
1386
- # Check for explicit override flag
1387
- explicit_override = (
1388
- str(getattr(api_config, "allow_public_override", False)).lower() == "true"
1389
- )
1390
-
1391
1385
  route_path = getattr(api_config, "routes", "unknown")
1392
1386
  method = getattr(api_config, "method", "unknown")
1387
+
1388
+ logger = logging.getLogger(__name__)
1393
1389
 
1390
+ # Check for explicit override flag
1391
+ explicit_override = getattr(api_config, "allow_public_override", False)
1392
+ # Handle both boolean and string values
1393
+ if isinstance(explicit_override, str):
1394
+ explicit_override = explicit_override.lower() in ("true", "1", "yes")
1395
+ else:
1396
+ explicit_override = bool(explicit_override)
1397
+
1394
1398
  logger = logging.getLogger(__name__)
1395
1399
 
1396
1400
  # Case 1: Cognito available + NONE requested + No explicit override = ERROR
cdk_factory/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.7.27"
1
+ __version__ = "0.7.28"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdk_factory
3
- Version: 0.7.27
3
+ Version: 0.7.28
4
4
  Summary: CDK Factory. A QuickStarter and best practices setup for CDK projects
5
5
  Author-email: Eric Wilson <eric.wilson@geekcafe.com>
6
6
  License: MIT License
@@ -1,7 +1,7 @@
1
1
  cdk_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cdk_factory/app.py,sha256=xv863N7O6HPKznB68_t7O4la9JacrkG87t9TjoDUk7s,2827
3
3
  cdk_factory/cdk.json,sha256=SKZKhJ2PBpFH78j-F8S3VDYW-lf76--Q2I3ON-ZIQfw,3106
4
- cdk_factory/version.py,sha256=fHi3FiqQRMkjl3o8ATyV9gZrBSNTVvt5m0WxJ-NYHTA,23
4
+ cdk_factory/version.py,sha256=zxRa3gUEpxj8fTU5qOT60NDmCfmdbg7_Z6ZkpKAhrNg,23
5
5
  cdk_factory/builds/README.md,sha256=9BBWd7bXpyKdMU_g2UljhQwrC9i5O_Tvkb6oPvndoZk,90
6
6
  cdk_factory/commands/command_loader.py,sha256=QbLquuP_AdxtlxlDy-2IWCQ6D-7qa58aphnDPtp_uTs,3744
7
7
  cdk_factory/configurations/base_config.py,sha256=JKjhNsy0RCUZy1s8n5D_aXXI-upR9izaLtCTfKYiV9k,9624
@@ -72,7 +72,7 @@ cdk_factory/stack/stack_module_registry.py,sha256=J14-A75VZESzRQa8p-Fepdap7Z8T7m
72
72
  cdk_factory/stack/stack_modules.py,sha256=kgEK-j0smZPozVwTCfM1g1V17EyTBT0TXAQZq4vZz0o,784
73
73
  cdk_factory/stack_library/__init__.py,sha256=5Y9TpIe8ZK1688G60PGcuP-hM0RvYEY_3Hl2qJCJJrw,581
74
74
  cdk_factory/stack_library/stack_base.py,sha256=tTleSFmlf26DuKVF_ytftf8P7IVWb5iex8cYfYupfvQ,4940
75
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=-K9ybNHpjycw4wtaiWbMm9dLCYPYoaqlakgBIQyotCk,35632
75
+ cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=83oV1pSFfb68mH4CH3mz0SERiiihpbPCsKK9zHgjvyE,32490
76
76
  cdk_factory/stack_library/auto_scaling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py,sha256=UsFqUb_3XPJAlmZ6F75nXna3elOggD1KuFmmdmhi0Lg,19070
78
78
  cdk_factory/stack_library/aws_lambdas/lambda_stack.py,sha256=xT8-799fSXMpYaRhRKytxsbpb5EG3g2h6L822_RpRSk,22956
@@ -97,7 +97,7 @@ cdk_factory/stack_library/vpc/__init__.py,sha256=7pIqP97Gf2AJbv9Ebp1WbQGHYhgEbWJ
97
97
  cdk_factory/stack_library/vpc/vpc_stack.py,sha256=zdDiGilf03esxuya5Z8zVYSVMAIuZBeD-ZKgfnEd6aw,10077
98
98
  cdk_factory/stack_library/websites/static_website_stack.py,sha256=KBQiV6PI09mpHGtH-So5Hk3uhfFLDepoXInGbfin0cY,7938
99
99
  cdk_factory/stages/websites/static_website_stage.py,sha256=X4fpKXkhb0zIbSHx3QyddBhVSLBryb1vf1Cg2fMTqog,755
100
- cdk_factory/utilities/api_gateway_integration_utility.py,sha256=LSfw2lNtJWMsRiaHf4FLiGHRgFPm-fOUhN2YJNHtxnI,62346
100
+ cdk_factory/utilities/api_gateway_integration_utility.py,sha256=LvsgUaipghTte4FZj1xHRn_O1KoFJGivGhtHxFJI2sc,62607
101
101
  cdk_factory/utilities/commandline_args.py,sha256=0FiNEJFbWVN8Ct7r0VHnJEx7rhUlaRKT7R7HMNJBSTI,2216
102
102
  cdk_factory/utilities/configuration_loader.py,sha256=z0ZdGLNbTO4_yfluB9zUh_i_Poc9qj-7oRyjMRlNkN8,1522
103
103
  cdk_factory/utilities/docker_utilities.py,sha256=9r8C-lXYpymqEfi3gTeWCQzHldvfjttPqn6p3j2khTE,8111
@@ -109,7 +109,7 @@ cdk_factory/utilities/lambda_function_utilities.py,sha256=j3tBdv_gC2MdEwBINDwAqY
109
109
  cdk_factory/utilities/os_execute.py,sha256=5Op0LY_8Y-pUm04y1k8MTpNrmQvcLmQHPQITEP7EuSU,1019
110
110
  cdk_factory/utils/api_gateway_utilities.py,sha256=If7Xu5s_UxmuV-kL3JkXxPLBdSVUKoLtohm0IUFoiV8,4378
111
111
  cdk_factory/workload/workload_factory.py,sha256=yBUDGIuB8-5p_mGcVFxsD2ZoZIziak3yh3LL3JvS0M4,5903
112
- cdk_factory-0.7.27.dist-info/METADATA,sha256=kkqu-N4uFgjOYSenuas-fjiwtIcTNoxmRK8NR6CBsyw,2451
113
- cdk_factory-0.7.27.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
114
- cdk_factory-0.7.27.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
115
- cdk_factory-0.7.27.dist-info/RECORD,,
112
+ cdk_factory-0.7.28.dist-info/METADATA,sha256=kkMbOLCrFJA_nNSeJiE_s3w6egr9KlKXq2etML92R1U,2451
113
+ cdk_factory-0.7.28.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
114
+ cdk_factory-0.7.28.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
115
+ cdk_factory-0.7.28.dist-info/RECORD,,