You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
3.9KB

  1. using System.Collections.Concurrent;
  2. using Campaign_Tracker.Server.Audit;
  3. namespace Campaign_Tracker.Server.Authentication;
  4. /// <summary>
  5. /// Implements IAuthenticationAuditStore by delegating durable writes to the shared
  6. /// IAuditService and maintaining an in-process queue for fast test/review queries.
  7. ///
  8. /// Both the in-memory enqueue and the IAuditService.Record() call happen synchronously.
  9. /// If IAuditService.Record() throws (audit store unavailable), the exception propagates
  10. /// to the caller — auditable actions are blocked rather than silently proceeding (AC #5).
  11. /// </summary>
  12. public sealed class InMemoryAuthenticationAuditStore : IAuthenticationAuditStore
  13. {
  14. private readonly ConcurrentQueue<AuthenticationAuditEvent> _events = new();
  15. private readonly IAuditService _auditService;
  16. public InMemoryAuthenticationAuditStore(IAuditService auditService)
  17. {
  18. _auditService = auditService;
  19. }
  20. public IReadOnlyCollection<AuthenticationAuditEvent> Events => _events.ToArray();
  21. public void RecordSuccess(string subject, string traceIdentifier)
  22. {
  23. var now = DateTimeOffset.UtcNow;
  24. var authEvent = new AuthenticationAuditEvent(
  25. AuthenticationAuditEventType.Success,
  26. subject,
  27. "authenticated",
  28. "authentication",
  29. traceIdentifier,
  30. now);
  31. _auditService.Record(new AuditEvent(
  32. AuditEventType.SessionLogin, subject, "authentication", "success", traceIdentifier, now));
  33. _events.Enqueue(authEvent);
  34. }
  35. public void RecordFailure(string reason, string traceIdentifier)
  36. {
  37. var now = DateTimeOffset.UtcNow;
  38. var authEvent = new AuthenticationAuditEvent(
  39. AuthenticationAuditEventType.Failure,
  40. "anonymous",
  41. reason,
  42. "authentication",
  43. traceIdentifier,
  44. now);
  45. _auditService.Record(new AuditEvent(
  46. AuditEventType.SessionLoginFailure, "anonymous", "authentication", reason, traceIdentifier, now));
  47. _events.Enqueue(authEvent);
  48. }
  49. public void RecordAuthorizationAllowed(string subject, string resource, string traceIdentifier)
  50. {
  51. var now = DateTimeOffset.UtcNow;
  52. var authEvent = new AuthenticationAuditEvent(
  53. AuthenticationAuditEventType.AuthorizationAllowed,
  54. subject,
  55. "authorization allowed",
  56. resource,
  57. traceIdentifier,
  58. now);
  59. _auditService.Record(new AuditEvent(
  60. AuditEventType.AuthorizationAllowed, subject, resource, "allowed", traceIdentifier, now));
  61. _events.Enqueue(authEvent);
  62. }
  63. public void RecordAuthorizationDenied(string subject, string resource, string traceIdentifier)
  64. {
  65. var now = DateTimeOffset.UtcNow;
  66. var authEvent = new AuthenticationAuditEvent(
  67. AuthenticationAuditEventType.AuthorizationDenied,
  68. subject,
  69. "authorization denied",
  70. resource,
  71. traceIdentifier,
  72. now);
  73. _auditService.Record(new AuditEvent(
  74. AuditEventType.AuthorizationDenied, subject, resource, "denied", traceIdentifier, now));
  75. _events.Enqueue(authEvent);
  76. }
  77. public void RecordLogout(string subject, bool succeeded, string traceIdentifier)
  78. {
  79. var now = DateTimeOffset.UtcNow;
  80. var outcome = succeeded ? "session destroyed" : "session destruction failed - keycloak unreachable";
  81. var authEvent = new AuthenticationAuditEvent(
  82. AuthenticationAuditEventType.Logout,
  83. subject,
  84. outcome,
  85. "authentication/logout",
  86. traceIdentifier,
  87. now);
  88. _auditService.Record(new AuditEvent(
  89. AuditEventType.SessionLogout, subject, "authentication/logout",
  90. succeeded ? "success" : "failure", traceIdentifier, now));
  91. _events.Enqueue(authEvent);
  92. }
  93. }

Powered by TurnKey Linux.