mcp-security-framework 0.1.0__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.
Files changed (76) hide show
  1. mcp_security_framework/__init__.py +96 -0
  2. mcp_security_framework/cli/__init__.py +18 -0
  3. mcp_security_framework/cli/cert_cli.py +511 -0
  4. mcp_security_framework/cli/security_cli.py +791 -0
  5. mcp_security_framework/constants.py +209 -0
  6. mcp_security_framework/core/__init__.py +61 -0
  7. mcp_security_framework/core/auth_manager.py +1011 -0
  8. mcp_security_framework/core/cert_manager.py +1663 -0
  9. mcp_security_framework/core/permission_manager.py +735 -0
  10. mcp_security_framework/core/rate_limiter.py +602 -0
  11. mcp_security_framework/core/security_manager.py +943 -0
  12. mcp_security_framework/core/ssl_manager.py +735 -0
  13. mcp_security_framework/examples/__init__.py +75 -0
  14. mcp_security_framework/examples/django_example.py +615 -0
  15. mcp_security_framework/examples/fastapi_example.py +472 -0
  16. mcp_security_framework/examples/flask_example.py +506 -0
  17. mcp_security_framework/examples/gateway_example.py +803 -0
  18. mcp_security_framework/examples/microservice_example.py +690 -0
  19. mcp_security_framework/examples/standalone_example.py +576 -0
  20. mcp_security_framework/middleware/__init__.py +250 -0
  21. mcp_security_framework/middleware/auth_middleware.py +292 -0
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
  23. mcp_security_framework/middleware/fastapi_middleware.py +757 -0
  24. mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
  25. mcp_security_framework/middleware/flask_middleware.py +591 -0
  26. mcp_security_framework/middleware/mtls_middleware.py +439 -0
  27. mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
  28. mcp_security_framework/middleware/security_middleware.py +507 -0
  29. mcp_security_framework/schemas/__init__.py +109 -0
  30. mcp_security_framework/schemas/config.py +694 -0
  31. mcp_security_framework/schemas/models.py +709 -0
  32. mcp_security_framework/schemas/responses.py +686 -0
  33. mcp_security_framework/tests/__init__.py +0 -0
  34. mcp_security_framework/utils/__init__.py +121 -0
  35. mcp_security_framework/utils/cert_utils.py +525 -0
  36. mcp_security_framework/utils/crypto_utils.py +475 -0
  37. mcp_security_framework/utils/validation_utils.py +571 -0
  38. mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
  39. mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
  40. mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
  41. mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
  42. mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
  43. tests/__init__.py +0 -0
  44. tests/test_cli/__init__.py +0 -0
  45. tests/test_cli/test_cert_cli.py +379 -0
  46. tests/test_cli/test_security_cli.py +657 -0
  47. tests/test_core/__init__.py +0 -0
  48. tests/test_core/test_auth_manager.py +582 -0
  49. tests/test_core/test_cert_manager.py +795 -0
  50. tests/test_core/test_permission_manager.py +395 -0
  51. tests/test_core/test_rate_limiter.py +626 -0
  52. tests/test_core/test_security_manager.py +841 -0
  53. tests/test_core/test_ssl_manager.py +532 -0
  54. tests/test_examples/__init__.py +8 -0
  55. tests/test_examples/test_fastapi_example.py +264 -0
  56. tests/test_examples/test_flask_example.py +238 -0
  57. tests/test_examples/test_standalone_example.py +292 -0
  58. tests/test_integration/__init__.py +0 -0
  59. tests/test_integration/test_auth_flow.py +502 -0
  60. tests/test_integration/test_certificate_flow.py +527 -0
  61. tests/test_integration/test_fastapi_integration.py +341 -0
  62. tests/test_integration/test_flask_integration.py +398 -0
  63. tests/test_integration/test_standalone_integration.py +493 -0
  64. tests/test_middleware/__init__.py +0 -0
  65. tests/test_middleware/test_fastapi_middleware.py +523 -0
  66. tests/test_middleware/test_flask_middleware.py +582 -0
  67. tests/test_middleware/test_security_middleware.py +493 -0
  68. tests/test_schemas/__init__.py +0 -0
  69. tests/test_schemas/test_config.py +811 -0
  70. tests/test_schemas/test_models.py +879 -0
  71. tests/test_schemas/test_responses.py +1054 -0
  72. tests/test_schemas/test_serialization.py +493 -0
  73. tests/test_utils/__init__.py +0 -0
  74. tests/test_utils/test_cert_utils.py +510 -0
  75. tests/test_utils/test_crypto_utils.py +603 -0
  76. tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,791 @@
1
+ """
2
+ Security Management CLI Module
3
+
4
+ This module provides command-line interface for security management
5
+ operations including authentication, authorization, and security configuration.
6
+
7
+ Key Features:
8
+ - API key management
9
+ - User authentication testing
10
+ - Permission validation
11
+ - Security configuration management
12
+ - Rate limiting management
13
+ - Security status monitoring
14
+
15
+ Commands:
16
+ auth: Authentication operations
17
+ permissions: Permission management
18
+ rate-limit: Rate limiting operations
19
+ config: Configuration management
20
+ status: Security status monitoring
21
+
22
+ Author: MCP Security Team
23
+ Version: 1.0.0
24
+ License: MIT
25
+ """
26
+
27
+ import click
28
+ import json
29
+ import os
30
+ from pathlib import Path
31
+ from typing import Optional, List
32
+ from datetime import datetime
33
+
34
+ from ..core.security_manager import SecurityManager
35
+ from ..schemas.config import SecurityConfig, AuthConfig, RateLimitConfig, SSLConfig, PermissionConfig
36
+ from ..schemas.models import AuthResult, AuthStatus
37
+
38
+
39
+ @click.group()
40
+ @click.option('--config', '-c', 'config_path',
41
+ help='Path to security configuration file')
42
+ @click.option('--verbose', '-v', is_flag=True,
43
+ help='Enable verbose output')
44
+ @click.pass_context
45
+ def security_cli(ctx, config_path: Optional[str], verbose: bool):
46
+ """
47
+ Security Management CLI
48
+
49
+ Manage security operations including authentication, authorization,
50
+ and security configuration.
51
+ """
52
+ ctx.ensure_object(dict)
53
+ ctx.obj['verbose'] = verbose
54
+
55
+ # Load configuration
56
+ if config_path and os.path.exists(config_path):
57
+ with open(config_path, 'r') as f:
58
+ config_data = json.load(f)
59
+ ctx.obj['config'] = SecurityConfig(**config_data)
60
+ else:
61
+ # Use default configuration
62
+ ctx.obj['config'] = SecurityConfig(
63
+ auth=AuthConfig(enabled=True),
64
+ rate_limit=RateLimitConfig(enabled=True),
65
+ ssl=SSLConfig(enabled=False),
66
+ permissions=PermissionConfig(enabled=True)
67
+ )
68
+
69
+ # Create security manager
70
+ ctx.obj['security_manager'] = SecurityManager(ctx.obj['config'])
71
+
72
+
73
+ @security_cli.group()
74
+ @click.pass_context
75
+ def auth(ctx):
76
+ """
77
+ Authentication operations.
78
+
79
+ Manage authentication including API keys, user authentication,
80
+ and authentication testing.
81
+ """
82
+ pass
83
+
84
+
85
+ @auth.command()
86
+ @click.option('--username', '-u', required=True,
87
+ help='Username for the API key')
88
+ @click.option('--api-key', '-k', required=True,
89
+ help='API key value')
90
+ @click.pass_context
91
+ def add_api_key(ctx, username: str, api_key: str):
92
+ """
93
+ Add an API key for a user.
94
+
95
+ This command adds an API key to the authentication system
96
+ for the specified user.
97
+ """
98
+ try:
99
+ security_manager = ctx.obj['security_manager']
100
+ verbose = ctx.obj['verbose']
101
+
102
+ if verbose:
103
+ click.echo(f"Adding API key for user: {username}")
104
+
105
+ # Add API key
106
+ success = security_manager.auth_manager.add_api_key(username, api_key)
107
+
108
+ if success:
109
+ click.echo(f"✅ API key added successfully for user: {username}")
110
+ else:
111
+ click.echo(f"❌ Failed to add API key for user: {username}", err=True)
112
+ raise click.Abort()
113
+
114
+ except Exception as e:
115
+ click.echo(f"❌ Failed to add API key: {str(e)}", err=True)
116
+ raise click.Abort()
117
+
118
+
119
+ @auth.command()
120
+ @click.option('--username', '-u', required=True,
121
+ help='Username to remove API key for')
122
+ @click.pass_context
123
+ def remove_api_key(ctx, username: str):
124
+ """
125
+ Remove an API key for a user.
126
+
127
+ This command removes an API key from the authentication system
128
+ for the specified user.
129
+ """
130
+ try:
131
+ security_manager = ctx.obj['security_manager']
132
+ verbose = ctx.obj['verbose']
133
+
134
+ if verbose:
135
+ click.echo(f"Removing API key for user: {username}")
136
+
137
+ # Remove API key
138
+ success = security_manager.auth_manager.remove_api_key(username)
139
+
140
+ if success:
141
+ click.echo(f"✅ API key removed successfully for user: {username}")
142
+ else:
143
+ click.echo(f"❌ Failed to remove API key for user: {username}", err=True)
144
+ raise click.Abort()
145
+
146
+ except Exception as e:
147
+ click.echo(f"❌ Failed to remove API key: {str(e)}", err=True)
148
+ raise click.Abort()
149
+
150
+
151
+ @auth.command()
152
+ @click.option('--api-key', '-k', required=True,
153
+ help='API key to test')
154
+ @click.pass_context
155
+ def test_api_key(ctx, api_key: str):
156
+ """
157
+ Test API key authentication.
158
+
159
+ This command tests if an API key is valid and returns
160
+ authentication information.
161
+ """
162
+ try:
163
+ security_manager = ctx.obj['security_manager']
164
+ verbose = ctx.obj['verbose']
165
+
166
+ if verbose:
167
+ click.echo(f"Testing API key authentication")
168
+
169
+ # Test API key
170
+ auth_result = security_manager.auth_manager.authenticate_api_key(api_key)
171
+
172
+ if auth_result.is_valid:
173
+ click.echo(f"✅ API key authentication successful!")
174
+ click.echo(f" Username: {auth_result.username}")
175
+ click.echo(f" Roles: {', '.join(auth_result.roles)}")
176
+ click.echo(f" Auth Method: {auth_result.auth_method}")
177
+ else:
178
+ click.echo(f"❌ API key authentication failed!", err=True)
179
+ click.echo(f" Error: {auth_result.error_message}")
180
+ raise click.Abort()
181
+
182
+ except Exception as e:
183
+ click.echo(f"❌ API key authentication failed: {str(e)}", err=True)
184
+ raise click.Abort()
185
+
186
+
187
+ @auth.command()
188
+ @click.option('--token', '-t', required=True,
189
+ help='JWT token to test')
190
+ @click.pass_context
191
+ def test_jwt(ctx, token: str):
192
+ """
193
+ Test JWT token authentication.
194
+
195
+ This command tests if a JWT token is valid and returns
196
+ authentication information.
197
+ """
198
+ try:
199
+ security_manager = ctx.obj['security_manager']
200
+ verbose = ctx.obj['verbose']
201
+
202
+ if verbose:
203
+ click.echo(f"Testing JWT token authentication")
204
+
205
+ # Test JWT token
206
+ auth_result = security_manager.auth_manager.authenticate_jwt_token(token)
207
+
208
+ if auth_result.is_valid:
209
+ click.echo(f"✅ JWT token authentication successful!")
210
+ click.echo(f" Username: {auth_result.username}")
211
+ click.echo(f" Roles: {', '.join(auth_result.roles)}")
212
+ click.echo(f" Auth Method: {auth_result.auth_method}")
213
+ else:
214
+ click.echo(f"❌ JWT token authentication failed!", err=True)
215
+ click.echo(f" Error: {auth_result.error_message}")
216
+ raise click.Abort()
217
+
218
+ except Exception as e:
219
+ click.echo(f"❌ JWT token authentication failed: {str(e)}", err=True)
220
+ raise click.Abort()
221
+
222
+
223
+ @security_cli.group()
224
+ @click.pass_context
225
+ def permissions(ctx):
226
+ """
227
+ Permission management operations.
228
+
229
+ Manage permissions including role assignment, permission validation,
230
+ and permission testing.
231
+ """
232
+ pass
233
+
234
+
235
+ @permissions.command()
236
+ @click.option('--username', '-u', required=True,
237
+ help='Username to check permissions for')
238
+ @click.option('--permissions', '-p', multiple=True, required=True,
239
+ help='Permissions to check (can be specified multiple times)')
240
+ @click.pass_context
241
+ def check(ctx, username: str, permissions: tuple):
242
+ """
243
+ Check user permissions.
244
+
245
+ This command checks if a user has the specified permissions.
246
+ """
247
+ try:
248
+ security_manager = ctx.obj['security_manager']
249
+ verbose = ctx.obj['verbose']
250
+ permissions_list = list(permissions)
251
+
252
+ if verbose:
253
+ click.echo(f"Checking permissions for user: {username}")
254
+ click.echo(f"Required permissions: {', '.join(permissions_list)}")
255
+
256
+ # Get user roles
257
+ user_roles = security_manager.auth_manager._get_user_roles(username)
258
+
259
+ if verbose:
260
+ click.echo(f"User roles: {', '.join(user_roles)}")
261
+
262
+ # Check permissions
263
+ has_permissions = security_manager.permission_manager.validate_access(
264
+ user_roles, permissions_list
265
+ )
266
+
267
+ if has_permissions.is_valid:
268
+ click.echo(f"✅ User has all required permissions!")
269
+ click.echo(f" Username: {username}")
270
+ click.echo(f" Roles: {', '.join(user_roles)}")
271
+ click.echo(f" Permissions: {', '.join(permissions_list)}")
272
+ else:
273
+ click.echo(f"❌ User does not have required permissions!", err=True)
274
+ click.echo(f" Username: {username}")
275
+ click.echo(f" Roles: {', '.join(user_roles)}")
276
+ click.echo(f" Missing permissions: {', '.join(has_permissions.missing_permissions)}")
277
+ raise click.Abort()
278
+
279
+ except Exception as e:
280
+ click.echo(f"❌ Permission check failed: {str(e)}", err=True)
281
+ raise click.Abort()
282
+
283
+
284
+ @permissions.command()
285
+ @click.option('--role', '-r', required=True,
286
+ help='Role name')
287
+ @click.pass_context
288
+ def list_role_permissions(ctx, role: str):
289
+ """
290
+ List permissions for a role.
291
+
292
+ This command displays all permissions assigned to a specific role.
293
+ """
294
+ try:
295
+ security_manager = ctx.obj['security_manager']
296
+ verbose = ctx.obj['verbose']
297
+
298
+ if verbose:
299
+ click.echo(f"Listing permissions for role: {role}")
300
+
301
+ # Get role permissions
302
+ permissions = security_manager.permission_manager.get_role_permissions(role)
303
+
304
+ if permissions:
305
+ click.echo(f"Permissions for role '{role}':")
306
+ for permission in permissions:
307
+ click.echo(f" - {permission}")
308
+ else:
309
+ click.echo(f"No permissions found for role: {role}")
310
+
311
+ except Exception as e:
312
+ click.echo(f"❌ Failed to list role permissions: {str(e)}", err=True)
313
+ raise click.Abort()
314
+
315
+
316
+ @security_cli.group()
317
+ @click.pass_context
318
+ def rate_limit(ctx):
319
+ """
320
+ Rate limiting operations.
321
+
322
+ Manage rate limiting including checking limits, resetting limits,
323
+ and monitoring rate limit status.
324
+ """
325
+ pass
326
+
327
+
328
+ @rate_limit.command()
329
+ @click.option('--identifier', '-i', required=True,
330
+ help='Rate limit identifier (IP, user ID, etc.)')
331
+ @click.pass_context
332
+ def check(ctx, identifier: str):
333
+ """
334
+ Check rate limit status.
335
+
336
+ This command checks the current rate limit status for an identifier.
337
+ """
338
+ try:
339
+ security_manager = ctx.obj['security_manager']
340
+ verbose = ctx.obj['verbose']
341
+
342
+ if verbose:
343
+ click.echo(f"Checking rate limit for identifier: {identifier}")
344
+
345
+ # Check rate limit
346
+ is_allowed = security_manager.rate_limiter.check_rate_limit(identifier)
347
+
348
+ if is_allowed:
349
+ click.echo(f"✅ Rate limit check passed for: {identifier}")
350
+ else:
351
+ click.echo(f"❌ Rate limit exceeded for: {identifier}", err=True)
352
+ raise click.Abort()
353
+
354
+ except Exception as e:
355
+ click.echo(f"❌ Rate limit check failed: {str(e)}", err=True)
356
+ raise click.Abort()
357
+
358
+
359
+ @rate_limit.command()
360
+ @click.option('--identifier', '-i', required=True,
361
+ help='Rate limit identifier to reset')
362
+ @click.pass_context
363
+ def reset(ctx, identifier: str):
364
+ """
365
+ Reset rate limit for an identifier.
366
+
367
+ This command resets the rate limit counter for an identifier.
368
+ """
369
+ try:
370
+ security_manager = ctx.obj['security_manager']
371
+ verbose = ctx.obj['verbose']
372
+
373
+ if verbose:
374
+ click.echo(f"Resetting rate limit for identifier: {identifier}")
375
+
376
+ # Reset rate limit
377
+ security_manager.rate_limiter.reset_rate_limit(identifier)
378
+
379
+ click.echo(f"✅ Rate limit reset successfully for: {identifier}")
380
+
381
+ except Exception as e:
382
+ click.echo(f"❌ Failed to reset rate limit: {str(e)}", err=True)
383
+ raise click.Abort()
384
+
385
+
386
+ @rate_limit.command()
387
+ @click.option('--identifier', '-i', required=True,
388
+ help='Rate limit identifier to get status for')
389
+ @click.pass_context
390
+ def status(ctx, identifier: str):
391
+ """
392
+ Get rate limit status for an identifier.
393
+
394
+ This command displays detailed rate limit status information.
395
+ """
396
+ try:
397
+ security_manager = ctx.obj['security_manager']
398
+ verbose = ctx.obj['verbose']
399
+
400
+ if verbose:
401
+ click.echo(f"Getting rate limit status for identifier: {identifier}")
402
+
403
+ # Get rate limit status
404
+ status = security_manager.rate_limiter.get_rate_limit_status(identifier)
405
+
406
+ click.echo(f"Rate Limit Status for '{identifier}':")
407
+ click.echo(f" Current Count: {status.current_count}")
408
+ click.echo(f" Limit: {status.limit}")
409
+ click.echo(f" Window Start: {status.window_start}")
410
+ click.echo(f" Window End: {status.window_end}")
411
+ click.echo(f" Is Allowed: {status.is_allowed}")
412
+
413
+ except Exception as e:
414
+ click.echo(f"❌ Failed to get rate limit status: {str(e)}", err=True)
415
+ raise click.Abort()
416
+
417
+
418
+ @security_cli.group()
419
+ @click.pass_context
420
+ def config(ctx):
421
+ """
422
+ Configuration management operations.
423
+
424
+ Manage security configuration including validation, export,
425
+ and configuration testing.
426
+ """
427
+ pass
428
+
429
+
430
+ @config.command()
431
+ @click.pass_context
432
+ def validate(ctx):
433
+ """
434
+ Validate security configuration.
435
+
436
+ This command validates the current security configuration
437
+ and reports any issues.
438
+ """
439
+ try:
440
+ config = ctx.obj['config']
441
+ verbose = ctx.obj['verbose']
442
+
443
+ if verbose:
444
+ click.echo("Validating security configuration...")
445
+
446
+ # Validate configuration
447
+ issues = []
448
+
449
+ # Check authentication configuration
450
+ if config.auth and config.auth.enabled:
451
+ if not config.auth.methods:
452
+ issues.append("Authentication enabled but no methods specified")
453
+
454
+ # Check rate limiting configuration
455
+ if config.rate_limit and config.rate_limit.enabled:
456
+ if config.rate_limit.default_requests_per_minute <= 0:
457
+ issues.append("Invalid rate limit: requests per minute must be positive")
458
+
459
+ # Check SSL configuration
460
+ if config.ssl and config.ssl.enabled:
461
+ if not config.ssl.cert_file or not config.ssl.key_file:
462
+ issues.append("SSL enabled but certificate or key file not specified")
463
+
464
+ if issues:
465
+ click.echo(f"❌ Configuration validation failed!", err=True)
466
+ for issue in issues:
467
+ click.echo(f" - {issue}")
468
+ raise click.Abort()
469
+ else:
470
+ click.echo(f"✅ Configuration validation passed!")
471
+
472
+ except Exception as e:
473
+ click.echo(f"❌ Configuration validation failed: {str(e)}", err=True)
474
+ raise click.Abort()
475
+
476
+
477
+ @config.command()
478
+ @click.option('--output', '-o', type=click.Path(),
479
+ help='Output file path (default: stdout)')
480
+ @click.pass_context
481
+ def export(ctx, output: Optional[str]):
482
+ """
483
+ Export security configuration.
484
+
485
+ This command exports the current security configuration
486
+ to JSON format.
487
+ """
488
+ try:
489
+ config = ctx.obj['config']
490
+ verbose = ctx.obj['verbose']
491
+
492
+ if verbose:
493
+ click.echo("Exporting security configuration...")
494
+
495
+ # Export configuration
496
+ config_json = config.model_dump_json(indent=2)
497
+
498
+ if output:
499
+ with open(output, 'w') as f:
500
+ f.write(config_json)
501
+ click.echo(f"✅ Configuration exported to: {output}")
502
+ else:
503
+ click.echo(config_json)
504
+
505
+ except Exception as e:
506
+ click.echo(f"❌ Failed to export configuration: {str(e)}", err=True)
507
+ raise click.Abort()
508
+
509
+
510
+ @security_cli.command()
511
+ @click.option('--output', '-o', type=click.Path(),
512
+ help='Output file path for roles configuration')
513
+ @click.option('--template', '-t', is_flag=True,
514
+ help='Generate template roles configuration')
515
+ @click.pass_context
516
+ def generate_roles(ctx, output: Optional[str], template: bool):
517
+ """
518
+ Generate roles configuration file.
519
+
520
+ This command generates a roles configuration file with default
521
+ roles and permissions or a template for customization.
522
+ """
523
+ try:
524
+ security_manager = ctx.obj['security_manager']
525
+ verbose = ctx.obj['verbose']
526
+
527
+ if verbose:
528
+ click.echo("Generating roles configuration...")
529
+
530
+ if template:
531
+ # Generate template roles configuration
532
+ template_roles = {
533
+ "roles": {
534
+ "admin": {
535
+ "description": "Administrator role with full access",
536
+ "permissions": ["*"],
537
+ "parent_roles": []
538
+ },
539
+ "user": {
540
+ "description": "Standard user role",
541
+ "permissions": ["read:own", "write:own"],
542
+ "parent_roles": []
543
+ },
544
+ "guest": {
545
+ "description": "Guest role with limited access",
546
+ "permissions": ["read:public"],
547
+ "parent_roles": []
548
+ }
549
+ },
550
+ "permissions": {
551
+ "read:own": "Read own resources",
552
+ "write:own": "Write own resources",
553
+ "read:public": "Read public resources",
554
+ "*": "All permissions (wildcard)"
555
+ }
556
+ }
557
+
558
+ roles_json = json.dumps(template_roles, indent=2)
559
+
560
+ if output:
561
+ with open(output, 'w') as f:
562
+ f.write(roles_json)
563
+ click.echo(f"✅ Template roles configuration generated: {output}")
564
+ else:
565
+ click.echo(roles_json)
566
+ else:
567
+ # Generate current roles configuration
568
+ roles_config = security_manager.permission_manager.export_roles_config()
569
+
570
+ if output:
571
+ with open(output, 'w') as f:
572
+ json.dump(roles_config, f, indent=2)
573
+ click.echo(f"✅ Current roles configuration exported: {output}")
574
+ else:
575
+ click.echo(json.dumps(roles_config, indent=2))
576
+
577
+ except Exception as e:
578
+ click.echo(f"❌ Failed to generate roles configuration: {str(e)}", err=True)
579
+ raise click.Abort()
580
+
581
+
582
+ @security_cli.command()
583
+ @click.option('--output', '-o', type=click.Path(),
584
+ help='Output file path for audit report')
585
+ @click.option('--format', '-f', type=click.Choice(['json', 'text']), default='text',
586
+ help='Output format for audit report')
587
+ @click.pass_context
588
+ def security_audit(ctx, output: Optional[str], format: str):
589
+ """
590
+ Perform security audit.
591
+
592
+ This command performs a comprehensive security audit of the
593
+ system configuration and components.
594
+ """
595
+ try:
596
+ config = ctx.obj['config']
597
+ security_manager = ctx.obj['security_manager']
598
+ verbose = ctx.obj['verbose']
599
+
600
+ if verbose:
601
+ click.echo("Performing security audit...")
602
+
603
+ # Perform security audit
604
+ audit_results = {
605
+ "timestamp": datetime.now().isoformat(),
606
+ "configuration": {
607
+ "authentication": {
608
+ "enabled": config.auth.enabled if config.auth else False,
609
+ "methods": config.auth.methods if config.auth else [],
610
+ "issues": []
611
+ },
612
+ "rate_limiting": {
613
+ "enabled": config.rate_limit.enabled if config.rate_limit else False,
614
+ "default_limit": config.rate_limit.default_requests_per_minute if config.rate_limit else None,
615
+ "issues": []
616
+ },
617
+ "ssl_tls": {
618
+ "enabled": config.ssl.enabled if config.ssl else False,
619
+ "cert_file": config.ssl.cert_file if config.ssl else None,
620
+ "key_file": config.ssl.key_file if config.ssl else None,
621
+ "issues": []
622
+ },
623
+ "permissions": {
624
+ "enabled": config.permissions.enabled if config.permissions else False,
625
+ "roles_file": config.permissions.roles_file if config.permissions else None,
626
+ "issues": []
627
+ }
628
+ },
629
+ "components": {
630
+ "auth_manager": {
631
+ "status": "✅ Initialized" if security_manager.auth_manager else "❌ Not Initialized",
632
+ "api_keys_count": len(security_manager.auth_manager.api_keys) if security_manager.auth_manager else 0
633
+ },
634
+ "permission_manager": {
635
+ "status": "✅ Initialized" if security_manager.permission_manager else "❌ Not Initialized",
636
+ "roles_count": len(security_manager.permission_manager.roles) if security_manager.permission_manager else 0
637
+ },
638
+ "rate_limiter": {
639
+ "status": "✅ Initialized" if security_manager.rate_limiter else "❌ Not Initialized"
640
+ },
641
+ "ssl_manager": {
642
+ "status": "✅ Initialized" if security_manager.ssl_manager else "❌ Not Initialized"
643
+ },
644
+ "cert_manager": {
645
+ "status": "✅ Initialized" if security_manager.cert_manager else "❌ Not Initialized"
646
+ }
647
+ },
648
+ "recommendations": []
649
+ }
650
+
651
+ # Add security recommendations
652
+ if not config.auth.enabled:
653
+ audit_results["recommendations"].append("Enable authentication for better security")
654
+
655
+ if not config.rate_limit.enabled:
656
+ audit_results["recommendations"].append("Enable rate limiting to prevent abuse")
657
+
658
+ if not config.ssl.enabled:
659
+ audit_results["recommendations"].append("Enable SSL/TLS for secure communication")
660
+
661
+ if not config.permissions.enabled:
662
+ audit_results["recommendations"].append("Enable permissions for access control")
663
+
664
+ # Output audit results
665
+ if format == 'json':
666
+ audit_json = json.dumps(audit_results, indent=2)
667
+ if output:
668
+ with open(output, 'w') as f:
669
+ f.write(audit_json)
670
+ click.echo(f"✅ Security audit report saved: {output}")
671
+ else:
672
+ click.echo(audit_json)
673
+ else:
674
+ # Text format
675
+ click.echo("Security Audit Report")
676
+ click.echo("=" * 50)
677
+ click.echo(f"Timestamp: {audit_results['timestamp']}")
678
+ click.echo()
679
+
680
+ click.echo("Configuration:")
681
+ click.echo("-" * 20)
682
+ auth_config = audit_results['configuration']['authentication']
683
+ click.echo(f"Authentication: {'✅ Enabled' if auth_config['enabled'] else '❌ Disabled'}")
684
+ if auth_config['methods']:
685
+ click.echo(f" Methods: {', '.join(auth_config['methods'])}")
686
+
687
+ rate_config = audit_results['configuration']['rate_limiting']
688
+ click.echo(f"Rate Limiting: {'✅ Enabled' if rate_config['enabled'] else '❌ Disabled'}")
689
+ if rate_config['default_limit']:
690
+ click.echo(f" Default Limit: {rate_config['default_limit']} requests/minute")
691
+
692
+ ssl_config = audit_results['configuration']['ssl_tls']
693
+ click.echo(f"SSL/TLS: {'✅ Enabled' if ssl_config['enabled'] else '❌ Disabled'}")
694
+
695
+ perm_config = audit_results['configuration']['permissions']
696
+ click.echo(f"Permissions: {'✅ Enabled' if perm_config['enabled'] else '❌ Disabled'}")
697
+
698
+ click.echo()
699
+ click.echo("Components:")
700
+ click.echo("-" * 20)
701
+ for name, status in audit_results['components'].items():
702
+ click.echo(f"{name.replace('_', ' ').title()}: {status['status']}")
703
+ if 'api_keys_count' in status:
704
+ click.echo(f" API Keys: {status['api_keys_count']}")
705
+ if 'roles_count' in status:
706
+ click.echo(f" Roles: {status['roles_count']}")
707
+
708
+ if audit_results['recommendations']:
709
+ click.echo()
710
+ click.echo("Recommendations:")
711
+ click.echo("-" * 20)
712
+ for rec in audit_results['recommendations']:
713
+ click.echo(f"• {rec}")
714
+
715
+ if output:
716
+ with open(output, 'w') as f:
717
+ f.write(json.dumps(audit_results, indent=2))
718
+ click.echo(f"\n✅ Detailed audit report saved: {output}")
719
+
720
+ except Exception as e:
721
+ click.echo(f"❌ Failed to perform security audit: {str(e)}", err=True)
722
+ raise click.Abort()
723
+
724
+
725
+ @security_cli.command()
726
+ @click.pass_context
727
+ def status(ctx):
728
+ """
729
+ Display security status.
730
+
731
+ This command displays the current status of all security
732
+ components and their configuration.
733
+ """
734
+ try:
735
+ config = ctx.obj['config']
736
+ security_manager = ctx.obj['security_manager']
737
+ verbose = ctx.obj['verbose']
738
+
739
+ if verbose:
740
+ click.echo("Getting security status...")
741
+
742
+ click.echo("Security Status:")
743
+ click.echo("=" * 50)
744
+
745
+ # Authentication status
746
+ auth_enabled = config.auth.enabled if config.auth else False
747
+ auth_methods = config.auth.methods if config.auth else []
748
+ click.echo(f"Authentication: {'✅ Enabled' if auth_enabled else '❌ Disabled'}")
749
+ if auth_methods:
750
+ click.echo(f" Methods: {', '.join(auth_methods)}")
751
+
752
+ # Rate limiting status
753
+ rate_limit_enabled = config.rate_limit.enabled if config.rate_limit else False
754
+ click.echo(f"Rate Limiting: {'✅ Enabled' if rate_limit_enabled else '❌ Disabled'}")
755
+ if rate_limit_enabled and config.rate_limit:
756
+ click.echo(f" Default Limit: {config.rate_limit.default_requests_per_minute} requests/minute")
757
+
758
+ # SSL/TLS status
759
+ ssl_enabled = config.ssl.enabled if config.ssl else False
760
+ click.echo(f"SSL/TLS: {'✅ Enabled' if ssl_enabled else '❌ Disabled'}")
761
+
762
+ # Permissions status
763
+ permissions_enabled = config.permissions.enabled if config.permissions else False
764
+ click.echo(f"Permissions: {'✅ Enabled' if permissions_enabled else '❌ Disabled'}")
765
+
766
+ # Component status
767
+ click.echo("\nComponent Status:")
768
+ click.echo("-" * 30)
769
+
770
+ # Check if components are properly initialized
771
+ components = [
772
+ ("Auth Manager", security_manager.auth_manager),
773
+ ("Permission Manager", security_manager.permission_manager),
774
+ ("Rate Limiter", security_manager.rate_limiter),
775
+ ("SSL Manager", security_manager.ssl_manager),
776
+ ("Certificate Manager", security_manager.cert_manager),
777
+ ]
778
+
779
+ for name, component in components:
780
+ if component:
781
+ click.echo(f"{name}: ✅ Initialized")
782
+ else:
783
+ click.echo(f"{name}: ❌ Not Initialized")
784
+
785
+ except Exception as e:
786
+ click.echo(f"❌ Failed to get security status: {str(e)}", err=True)
787
+ raise click.Abort()
788
+
789
+
790
+ if __name__ == '__main__':
791
+ security_cli()