orfeas_lyra 0.6.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +222 -0
- data/LICENSE +21 -0
- data/README.md +1165 -0
- data/Rakefile +728 -0
- data/app/controllers/lyra/application_controller.rb +23 -0
- data/app/controllers/lyra/dashboard_controller.rb +624 -0
- data/app/controllers/lyra/flow_controller.rb +224 -0
- data/app/controllers/lyra/privacy_controller.rb +182 -0
- data/app/views/lyra/dashboard/audit_trail.html.erb +324 -0
- data/app/views/lyra/dashboard/discrepancies.html.erb +125 -0
- data/app/views/lyra/dashboard/event_graph_view.html.erb +525 -0
- data/app/views/lyra/dashboard/heatmap_view.html.erb +155 -0
- data/app/views/lyra/dashboard/index.html.erb +119 -0
- data/app/views/lyra/dashboard/model_overview.html.erb +115 -0
- data/app/views/lyra/dashboard/projections.html.erb +302 -0
- data/app/views/lyra/dashboard/schema.html.erb +283 -0
- data/app/views/lyra/dashboard/schema_history.html.erb +78 -0
- data/app/views/lyra/dashboard/schema_version.html.erb +340 -0
- data/app/views/lyra/dashboard/verification.html.erb +370 -0
- data/app/views/lyra/flow/crud_mapping.html.erb +125 -0
- data/app/views/lyra/flow/timeline.html.erb +260 -0
- data/app/views/lyra/privacy/pii_detection.html.erb +148 -0
- data/app/views/lyra/privacy/policy.html.erb +188 -0
- data/app/workflows/es_async_mode_workflow.rb +80 -0
- data/app/workflows/es_sync_mode_workflow.rb +64 -0
- data/app/workflows/hijack_mode_workflow.rb +54 -0
- data/app/workflows/lifecycle_workflow.rb +43 -0
- data/app/workflows/monitor_mode_workflow.rb +39 -0
- data/config/privacy_policies.rb +273 -0
- data/config/routes.rb +48 -0
- data/lib/lyra/aggregate.rb +131 -0
- data/lib/lyra/associations/event_aware.rb +225 -0
- data/lib/lyra/command.rb +81 -0
- data/lib/lyra/command_handler.rb +155 -0
- data/lib/lyra/configuration.rb +124 -0
- data/lib/lyra/consistency/read_your_writes.rb +91 -0
- data/lib/lyra/correlation.rb +144 -0
- data/lib/lyra/dual_view.rb +231 -0
- data/lib/lyra/engine.rb +67 -0
- data/lib/lyra/event.rb +71 -0
- data/lib/lyra/event_analyzer.rb +135 -0
- data/lib/lyra/event_flow.rb +449 -0
- data/lib/lyra/event_mapper.rb +106 -0
- data/lib/lyra/event_store_adapter.rb +72 -0
- data/lib/lyra/id_generator.rb +137 -0
- data/lib/lyra/interceptors/association_interceptor.rb +169 -0
- data/lib/lyra/interceptors/crud_interceptor.rb +543 -0
- data/lib/lyra/privacy/gdpr_compliance.rb +161 -0
- data/lib/lyra/privacy/pii_detector.rb +85 -0
- data/lib/lyra/privacy/pii_masker.rb +66 -0
- data/lib/lyra/privacy/policy_integration.rb +253 -0
- data/lib/lyra/projection.rb +94 -0
- data/lib/lyra/projections/async_projection_job.rb +63 -0
- data/lib/lyra/projections/cached_projection.rb +322 -0
- data/lib/lyra/projections/cached_relation.rb +757 -0
- data/lib/lyra/projections/event_store_reader.rb +127 -0
- data/lib/lyra/projections/model_projection.rb +143 -0
- data/lib/lyra/schema/diff.rb +331 -0
- data/lib/lyra/schema/event_class_registrar.rb +63 -0
- data/lib/lyra/schema/generator.rb +190 -0
- data/lib/lyra/schema/reporter.rb +188 -0
- data/lib/lyra/schema/store.rb +156 -0
- data/lib/lyra/schema/validator.rb +100 -0
- data/lib/lyra/strict_data_access.rb +363 -0
- data/lib/lyra/verification/crud_lifecycle_workflow.rb +456 -0
- data/lib/lyra/verification/workflow_generator.rb +540 -0
- data/lib/lyra/version.rb +3 -0
- data/lib/lyra/visualization/activity_heatmap.rb +215 -0
- data/lib/lyra/visualization/event_graph.rb +310 -0
- data/lib/lyra/visualization/timeline.rb +398 -0
- data/lib/lyra.rb +150 -0
- data/lib/tasks/dist.rake +391 -0
- data/lib/tasks/gems.rake +185 -0
- data/lib/tasks/lyra_schema.rake +231 -0
- data/lib/tasks/lyra_workflows.rake +452 -0
- data/lib/tasks/public_release.rake +351 -0
- data/lib/tasks/stats.rake +175 -0
- data/lib/tasks/testbed.rake +479 -0
- data/lib/tasks/version.rake +159 -0
- metadata +221 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Auto-generated by Lyra::Verification::WorkflowGenerator
|
|
3
|
+
# Generated at: 2026-01-04 17:33:03 UTC
|
|
4
|
+
# Updated: Fork pattern for true async parallelism
|
|
5
|
+
|
|
6
|
+
# Event Sourcing Async Mode Workflow
|
|
7
|
+
# Full event sourcing with asynchronous (non-blocking) projection
|
|
8
|
+
#
|
|
9
|
+
# Uses Petri net FORK pattern to model true parallelism:
|
|
10
|
+
# After events are stored, a single fork transition places tokens in
|
|
11
|
+
# BOTH response_returned AND job_processing simultaneously.
|
|
12
|
+
# This correctly models async behavior where:
|
|
13
|
+
# 1. Response returns immediately to caller (non-blocking)
|
|
14
|
+
# 2. Background worker processes projection independently
|
|
15
|
+
class EsAsyncModeWorkflow < PetriFlow::Workflow
|
|
16
|
+
workflow_name "Event Sourcing Async Mode Workflow"
|
|
17
|
+
|
|
18
|
+
places :idle, :command_received, :aggregate_loading, :aggregate_loaded,
|
|
19
|
+
:command_applying, :events_generated, :events_storing, :events_stored,
|
|
20
|
+
:response_returned, :job_processing, :projecting_async, :projection_complete
|
|
21
|
+
initial_place :idle
|
|
22
|
+
terminal_places :response_returned, :projection_complete
|
|
23
|
+
|
|
24
|
+
# Command processing flow (sequential)
|
|
25
|
+
transition :receive_command,
|
|
26
|
+
from: :idle,
|
|
27
|
+
to: :command_received,
|
|
28
|
+
trigger: "CommandHandler receives command"
|
|
29
|
+
|
|
30
|
+
transition :load_aggregate,
|
|
31
|
+
from: :command_received,
|
|
32
|
+
to: :aggregate_loading,
|
|
33
|
+
trigger: "Load aggregate from event stream"
|
|
34
|
+
|
|
35
|
+
transition :aggregate_ready,
|
|
36
|
+
from: :aggregate_loading,
|
|
37
|
+
to: :aggregate_loaded,
|
|
38
|
+
trigger: "Aggregate hydrated from events"
|
|
39
|
+
|
|
40
|
+
transition :apply_command,
|
|
41
|
+
from: :aggregate_loaded,
|
|
42
|
+
to: :command_applying,
|
|
43
|
+
trigger: "Aggregate.apply(command)"
|
|
44
|
+
|
|
45
|
+
transition :generate_events,
|
|
46
|
+
from: :command_applying,
|
|
47
|
+
to: :events_generated,
|
|
48
|
+
trigger: "Domain events created"
|
|
49
|
+
|
|
50
|
+
transition :store_events,
|
|
51
|
+
from: :events_generated,
|
|
52
|
+
to: :events_storing,
|
|
53
|
+
trigger: "EventStore.append_to_stream"
|
|
54
|
+
|
|
55
|
+
transition :events_persisted,
|
|
56
|
+
from: :events_storing,
|
|
57
|
+
to: :events_stored,
|
|
58
|
+
trigger: "Events committed to store"
|
|
59
|
+
|
|
60
|
+
# FORK: Parallel split - one transition produces tokens in TWO places
|
|
61
|
+
# This models the async behavior where both happen simultaneously:
|
|
62
|
+
# - Response returns immediately to caller
|
|
63
|
+
# - Background job is enqueued for processing
|
|
64
|
+
transition :async_fork,
|
|
65
|
+
from: :events_stored,
|
|
66
|
+
to: [:response_returned, :job_processing],
|
|
67
|
+
trigger: "Enqueue job & return response (parallel)"
|
|
68
|
+
|
|
69
|
+
# Background projection flow (runs independently after fork)
|
|
70
|
+
transition :project_async,
|
|
71
|
+
from: :job_processing,
|
|
72
|
+
to: :projecting_async,
|
|
73
|
+
trigger: "ModelProjection.apply (async)"
|
|
74
|
+
|
|
75
|
+
transition :async_complete,
|
|
76
|
+
from: :projecting_async,
|
|
77
|
+
to: :projection_complete,
|
|
78
|
+
trigger: "Read model eventually consistent"
|
|
79
|
+
|
|
80
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Auto-generated by Lyra::Verification::WorkflowGenerator
|
|
3
|
+
# Generated at: 2026-01-04 17:33:03 UTC
|
|
4
|
+
|
|
5
|
+
# Event Sourcing Sync Mode Workflow
|
|
6
|
+
# Full event sourcing with synchronous (blocking) projection
|
|
7
|
+
class EsSyncModeWorkflow < PetriFlow::Workflow
|
|
8
|
+
workflow_name "Event Sourcing Sync Mode Workflow"
|
|
9
|
+
|
|
10
|
+
places :idle, :command_received, :aggregate_loading, :aggregate_loaded, :command_applying, :events_generated, :events_storing, :events_stored, :projecting_sync, :projection_complete, :completed
|
|
11
|
+
initial_place :idle
|
|
12
|
+
terminal_places :completed
|
|
13
|
+
|
|
14
|
+
transition :receive_command,
|
|
15
|
+
from: :idle,
|
|
16
|
+
to: :command_received,
|
|
17
|
+
trigger: "CommandHandler receives command"
|
|
18
|
+
|
|
19
|
+
transition :load_aggregate,
|
|
20
|
+
from: :command_received,
|
|
21
|
+
to: :aggregate_loading,
|
|
22
|
+
trigger: "Load aggregate from event stream"
|
|
23
|
+
|
|
24
|
+
transition :aggregate_ready,
|
|
25
|
+
from: :aggregate_loading,
|
|
26
|
+
to: :aggregate_loaded,
|
|
27
|
+
trigger: "Aggregate hydrated from events"
|
|
28
|
+
|
|
29
|
+
transition :apply_command,
|
|
30
|
+
from: :aggregate_loaded,
|
|
31
|
+
to: :command_applying,
|
|
32
|
+
trigger: "Aggregate.apply(command)"
|
|
33
|
+
|
|
34
|
+
transition :generate_events,
|
|
35
|
+
from: :command_applying,
|
|
36
|
+
to: :events_generated,
|
|
37
|
+
trigger: "Domain events created"
|
|
38
|
+
|
|
39
|
+
transition :store_events,
|
|
40
|
+
from: :events_generated,
|
|
41
|
+
to: :events_storing,
|
|
42
|
+
trigger: "EventStore.append_to_stream"
|
|
43
|
+
|
|
44
|
+
transition :events_persisted,
|
|
45
|
+
from: :events_storing,
|
|
46
|
+
to: :events_stored,
|
|
47
|
+
trigger: "Events committed to store"
|
|
48
|
+
|
|
49
|
+
transition :project_sync,
|
|
50
|
+
from: :events_stored,
|
|
51
|
+
to: :projecting_sync,
|
|
52
|
+
trigger: "ModelProjection.apply (synchronous)"
|
|
53
|
+
|
|
54
|
+
transition :sync_complete,
|
|
55
|
+
from: :projecting_sync,
|
|
56
|
+
to: :projection_complete,
|
|
57
|
+
trigger: "Read model updated"
|
|
58
|
+
|
|
59
|
+
transition :finalize,
|
|
60
|
+
from: :projection_complete,
|
|
61
|
+
to: :completed,
|
|
62
|
+
trigger: "Response returned to caller"
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Auto-generated by Lyra::Verification::WorkflowGenerator
|
|
3
|
+
# Generated at: 2026-01-04 17:33:03 UTC
|
|
4
|
+
|
|
5
|
+
# Hijack Mode Workflow
|
|
6
|
+
# Intercepts CRUD operations and converts to event sourcing
|
|
7
|
+
class HijackModeWorkflow < PetriFlow::Workflow
|
|
8
|
+
workflow_name "Hijack Mode Workflow"
|
|
9
|
+
|
|
10
|
+
places :idle, :crud_intercepted, :command_created, :command_validating, :command_valid, :event_created, :event_stored, :projecting, :completed
|
|
11
|
+
initial_place :idle
|
|
12
|
+
terminal_places :completed
|
|
13
|
+
|
|
14
|
+
transition :intercept_crud,
|
|
15
|
+
from: :idle,
|
|
16
|
+
to: :crud_intercepted,
|
|
17
|
+
trigger: "before_* callback intercepts operation"
|
|
18
|
+
|
|
19
|
+
transition :create_command,
|
|
20
|
+
from: :crud_intercepted,
|
|
21
|
+
to: :command_created,
|
|
22
|
+
trigger: "Convert CRUD to Lyra::Command"
|
|
23
|
+
|
|
24
|
+
transition :validate_command,
|
|
25
|
+
from: :command_created,
|
|
26
|
+
to: :command_validating,
|
|
27
|
+
trigger: "CommandHandler.validate"
|
|
28
|
+
|
|
29
|
+
transition :command_passes,
|
|
30
|
+
from: :command_validating,
|
|
31
|
+
to: :command_valid,
|
|
32
|
+
trigger: "Validation passes"
|
|
33
|
+
|
|
34
|
+
transition :emit_event,
|
|
35
|
+
from: :command_valid,
|
|
36
|
+
to: :event_created,
|
|
37
|
+
trigger: "CommandHandler.execute creates event"
|
|
38
|
+
|
|
39
|
+
transition :store_event,
|
|
40
|
+
from: :event_created,
|
|
41
|
+
to: :event_stored,
|
|
42
|
+
trigger: "RailsEventStore.publish"
|
|
43
|
+
|
|
44
|
+
transition :project_state,
|
|
45
|
+
from: :event_stored,
|
|
46
|
+
to: :projecting,
|
|
47
|
+
trigger: "Projection.apply(event)"
|
|
48
|
+
|
|
49
|
+
transition :projection_complete,
|
|
50
|
+
from: :projecting,
|
|
51
|
+
to: :completed,
|
|
52
|
+
trigger: "Model state updated from event"
|
|
53
|
+
|
|
54
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Auto-generated by Lyra::Verification::WorkflowGenerator
|
|
3
|
+
# Generated at: 2026-01-04 17:33:03 UTC
|
|
4
|
+
|
|
5
|
+
# CRUD Entity Lifecycle (Generated)
|
|
6
|
+
class LifecycleWorkflow < PetriFlow::Workflow
|
|
7
|
+
workflow_name "CRUD Entity Lifecycle (Generated)"
|
|
8
|
+
|
|
9
|
+
places :nonexistent, :created, :persisted, :updated, :destroyed, :deleted
|
|
10
|
+
initial_place :nonexistent
|
|
11
|
+
terminal_places :deleted
|
|
12
|
+
|
|
13
|
+
transition :create,
|
|
14
|
+
from: :nonexistent,
|
|
15
|
+
to: :created,
|
|
16
|
+
trigger: "after_create"
|
|
17
|
+
|
|
18
|
+
transition :emit_created_event,
|
|
19
|
+
from: :created,
|
|
20
|
+
to: :persisted,
|
|
21
|
+
trigger: "publish Created event"
|
|
22
|
+
|
|
23
|
+
transition :update,
|
|
24
|
+
from: :persisted,
|
|
25
|
+
to: :updated,
|
|
26
|
+
trigger: "after_update"
|
|
27
|
+
|
|
28
|
+
transition :emit_updated_event,
|
|
29
|
+
from: :updated,
|
|
30
|
+
to: :persisted,
|
|
31
|
+
trigger: "publish Updated event"
|
|
32
|
+
|
|
33
|
+
transition :destroy,
|
|
34
|
+
from: :persisted,
|
|
35
|
+
to: :destroyed,
|
|
36
|
+
trigger: "after_destroy"
|
|
37
|
+
|
|
38
|
+
transition :emit_destroyed_event,
|
|
39
|
+
from: :destroyed,
|
|
40
|
+
to: :deleted,
|
|
41
|
+
trigger: "publish Destroyed event"
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Auto-generated by Lyra::Verification::WorkflowGenerator
|
|
3
|
+
# Generated at: 2026-01-04 17:33:03 UTC
|
|
4
|
+
|
|
5
|
+
# Monitor Mode Workflow
|
|
6
|
+
# Passively observes CRUD operations without modification
|
|
7
|
+
class MonitorModeWorkflow < PetriFlow::Workflow
|
|
8
|
+
workflow_name "Monitor Mode Workflow"
|
|
9
|
+
|
|
10
|
+
places :idle, :crud_executing, :crud_completed, :event_building, :event_publishing, :completed
|
|
11
|
+
initial_place :idle
|
|
12
|
+
terminal_places :completed
|
|
13
|
+
|
|
14
|
+
transition :receive_crud,
|
|
15
|
+
from: :idle,
|
|
16
|
+
to: :crud_executing,
|
|
17
|
+
trigger: "ActiveRecord callback triggered"
|
|
18
|
+
|
|
19
|
+
transition :crud_success,
|
|
20
|
+
from: :crud_executing,
|
|
21
|
+
to: :crud_completed,
|
|
22
|
+
trigger: "CRUD operation completes successfully"
|
|
23
|
+
|
|
24
|
+
transition :build_event,
|
|
25
|
+
from: :crud_completed,
|
|
26
|
+
to: :event_building,
|
|
27
|
+
trigger: "Extract changes from model"
|
|
28
|
+
|
|
29
|
+
transition :publish_event,
|
|
30
|
+
from: :event_building,
|
|
31
|
+
to: :event_publishing,
|
|
32
|
+
trigger: "Lyra::Event.publish"
|
|
33
|
+
|
|
34
|
+
transition :store_event,
|
|
35
|
+
from: :event_publishing,
|
|
36
|
+
to: :completed,
|
|
37
|
+
trigger: "RailsEventStore.publish"
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Privacy Policies Configuration for Lyra
|
|
2
|
+
# This file defines privacy policies using PAM DSL
|
|
3
|
+
|
|
4
|
+
# University System Privacy Policy
|
|
5
|
+
PamDsl.define_policy :university_system do
|
|
6
|
+
meta :version, "1.0.0"
|
|
7
|
+
meta :effective_date, "2025-01-01"
|
|
8
|
+
meta :organization, "University Payment System"
|
|
9
|
+
|
|
10
|
+
# Define PII fields
|
|
11
|
+
field :email, type: :email, sensitivity: :internal do
|
|
12
|
+
allow_for :authentication, :communication, :enrollment, :payment_processing
|
|
13
|
+
transform :display do |value|
|
|
14
|
+
local, domain = value.split('@')
|
|
15
|
+
"#{local[0]}***@#{domain}"
|
|
16
|
+
end
|
|
17
|
+
transform :log do |value|
|
|
18
|
+
"***EMAIL***"
|
|
19
|
+
end
|
|
20
|
+
meta :required, true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
field :student_id, type: :identifier, sensitivity: :internal do
|
|
24
|
+
allow_for :enrollment, :academic_records, :payment_processing
|
|
25
|
+
transform :display do |value|
|
|
26
|
+
"***#{value[-4..]}"
|
|
27
|
+
end
|
|
28
|
+
meta :unique, true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
field :name, type: :name, sensitivity: :internal do
|
|
32
|
+
allow_for :enrollment, :academic_records, :communication, :certificates
|
|
33
|
+
transform :display do |value|
|
|
34
|
+
parts = value.split(' ')
|
|
35
|
+
"#{parts.first} ***"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
field :phone, type: :phone, sensitivity: :confidential do
|
|
40
|
+
allow_for :emergency_contact, :communication
|
|
41
|
+
transform :display do |value|
|
|
42
|
+
"***-***-#{value[-4..]}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
field :address, type: :address, sensitivity: :confidential do
|
|
47
|
+
allow_for :enrollment, :correspondence, :legal_compliance
|
|
48
|
+
transform :display do |value|
|
|
49
|
+
"***REDACTED***"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
field :ssn, type: :ssn, sensitivity: :restricted do
|
|
54
|
+
allow_for :legal_compliance, :financial_aid
|
|
55
|
+
transform :display do |value|
|
|
56
|
+
"***-**-#{value[-4..]}"
|
|
57
|
+
end
|
|
58
|
+
meta :encryption_required, true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
field :date_of_birth, type: :date_of_birth, sensitivity: :confidential do
|
|
62
|
+
allow_for :enrollment, :age_verification, :legal_compliance
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
field :payment_method, type: :financial, sensitivity: :restricted do
|
|
66
|
+
allow_for :payment_processing
|
|
67
|
+
transform :display do |value|
|
|
68
|
+
"****-****-****-#{value[-4..]}"
|
|
69
|
+
end
|
|
70
|
+
meta :pci_dss_scope, true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
field :bank_account, type: :financial, sensitivity: :restricted do
|
|
74
|
+
allow_for :payment_processing, :refunds
|
|
75
|
+
transform :display do |value|
|
|
76
|
+
"***REDACTED***"
|
|
77
|
+
end
|
|
78
|
+
meta :encryption_required, true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
field :academic_records, type: :custom, sensitivity: :confidential do
|
|
82
|
+
allow_for :academic_records, :transcripts, :legal_compliance
|
|
83
|
+
meta :ferpa_protected, true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Define processing purposes
|
|
87
|
+
purpose :enrollment do
|
|
88
|
+
describe "Student enrollment and registration"
|
|
89
|
+
basis :contract
|
|
90
|
+
requires :email, :name, :student_id, :date_of_birth
|
|
91
|
+
optionally :phone, :address
|
|
92
|
+
meta :retention_reason, "Academic records requirement"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
purpose :authentication do
|
|
96
|
+
describe "User authentication and session management"
|
|
97
|
+
basis :contract
|
|
98
|
+
requires :email
|
|
99
|
+
optionally :student_id
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
purpose :payment_processing do
|
|
103
|
+
describe "Processing tuition and fee payments"
|
|
104
|
+
basis :contract
|
|
105
|
+
requires :email, :student_id, :payment_method
|
|
106
|
+
optionally :bank_account
|
|
107
|
+
meta :compliance, ["PCI-DSS", "SOX"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
purpose :communication do
|
|
111
|
+
describe "Important academic and administrative communications"
|
|
112
|
+
basis :legitimate_interests
|
|
113
|
+
requires :email
|
|
114
|
+
optionally :phone, :name
|
|
115
|
+
meta :balancing_test_performed, true
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
purpose :academic_records do
|
|
119
|
+
describe "Maintaining academic transcripts and records"
|
|
120
|
+
basis :legal_obligation
|
|
121
|
+
requires :student_id, :name, :academic_records
|
|
122
|
+
meta :compliance, "FERPA"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
purpose :legal_compliance do
|
|
126
|
+
describe "Compliance with educational and tax regulations"
|
|
127
|
+
basis :legal_obligation
|
|
128
|
+
requires :student_id, :name
|
|
129
|
+
optionally :ssn, :address, :date_of_birth
|
|
130
|
+
meta :regulations, ["FERPA", "IRS", "State Education Laws"]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
purpose :marketing do
|
|
134
|
+
describe "Marketing communications about events and programs"
|
|
135
|
+
basis :consent
|
|
136
|
+
requires :email
|
|
137
|
+
optionally :name, :phone
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
purpose :analytics do
|
|
141
|
+
describe "Improving educational services and user experience"
|
|
142
|
+
basis :legitimate_interests
|
|
143
|
+
requires :student_id
|
|
144
|
+
meta :anonymization_applied, true
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Configure retention policies
|
|
148
|
+
retention do
|
|
149
|
+
default 7.years
|
|
150
|
+
|
|
151
|
+
for_model 'Student' do
|
|
152
|
+
keep_for 10.years # Educational records retention
|
|
153
|
+
field :email, duration: 2.years
|
|
154
|
+
field :academic_records, duration: 50.years # Permanent academic record
|
|
155
|
+
on_expiry :archive
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
for_model 'Enrollment' do
|
|
159
|
+
keep_for 10.years
|
|
160
|
+
on_expiry :archive
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
for_model 'Payment' do
|
|
164
|
+
keep_for 10.years # Financial records retention
|
|
165
|
+
on_expiry :archive
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
for_model 'Invoice' do
|
|
169
|
+
keep_for 10.years
|
|
170
|
+
on_expiry :archive
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
for_model 'Course' do
|
|
174
|
+
keep_for 20.years # Course catalog history
|
|
175
|
+
on_expiry :archive
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Configure consent requirements
|
|
180
|
+
consent do
|
|
181
|
+
for_purpose :marketing do
|
|
182
|
+
required!
|
|
183
|
+
granular!
|
|
184
|
+
withdrawable!
|
|
185
|
+
expires_in 2.years
|
|
186
|
+
describe "We'll send you information about upcoming events, programs, and opportunities"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
for_purpose :analytics do
|
|
190
|
+
required! false
|
|
191
|
+
granular!
|
|
192
|
+
withdrawable!
|
|
193
|
+
describe "Help us improve our services by sharing anonymous usage data"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# E-commerce Privacy Policy Example
|
|
199
|
+
PamDsl.define_policy :ecommerce do
|
|
200
|
+
meta :version, "1.0.0"
|
|
201
|
+
meta :organization, "E-commerce Platform"
|
|
202
|
+
|
|
203
|
+
# Customer fields
|
|
204
|
+
field :email, type: :email, sensitivity: :internal do
|
|
205
|
+
allow_for :account_management, :order_fulfillment, :marketing
|
|
206
|
+
transform :display do |value|
|
|
207
|
+
local, domain = value.split('@')
|
|
208
|
+
"#{local[0..1]}***@#{domain}"
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
field :shipping_address, type: :address, sensitivity: :confidential do
|
|
213
|
+
allow_for :order_fulfillment, :shipping
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
field :credit_card, type: :credit_card, sensitivity: :restricted do
|
|
217
|
+
allow_for :payment_processing
|
|
218
|
+
transform :display do |value|
|
|
219
|
+
"****-****-****-#{value[-4..]}"
|
|
220
|
+
end
|
|
221
|
+
meta :pci_dss_required, true
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Purposes
|
|
225
|
+
purpose :account_management do
|
|
226
|
+
describe "Customer account creation and management"
|
|
227
|
+
basis :contract
|
|
228
|
+
requires :email
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
purpose :order_fulfillment do
|
|
232
|
+
describe "Processing and shipping customer orders"
|
|
233
|
+
basis :contract
|
|
234
|
+
requires :email, :shipping_address
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
purpose :payment_processing do
|
|
238
|
+
describe "Processing customer payments"
|
|
239
|
+
basis :contract
|
|
240
|
+
requires :credit_card
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
purpose :marketing do
|
|
244
|
+
describe "Marketing communications and promotions"
|
|
245
|
+
basis :consent
|
|
246
|
+
requires :email
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Retention
|
|
250
|
+
retention do
|
|
251
|
+
default 5.years
|
|
252
|
+
|
|
253
|
+
for_model 'Order' do
|
|
254
|
+
keep_for 7.years
|
|
255
|
+
on_expiry :anonymize
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
for_model 'Payment' do
|
|
259
|
+
keep_for 7.years
|
|
260
|
+
on_expiry :archive
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Consent
|
|
265
|
+
consent do
|
|
266
|
+
for_purpose :marketing do
|
|
267
|
+
required!
|
|
268
|
+
granular!
|
|
269
|
+
withdrawable!
|
|
270
|
+
expires_in 1.year
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Lyra::Engine.routes.draw do
|
|
2
|
+
# Root redirect to dashboard
|
|
3
|
+
root to: redirect('/lyra/dashboard')
|
|
4
|
+
|
|
5
|
+
# Dashboard routes
|
|
6
|
+
get 'dashboard', to: 'dashboard#index'
|
|
7
|
+
get 'dashboard/model/:model_class', to: 'dashboard#model_overview', as: :model_overview
|
|
8
|
+
get 'dashboard/compare/:model_class/:id', to: 'dashboard#compare', as: :compare
|
|
9
|
+
get 'dashboard/discrepancies/:model_class', to: 'dashboard#discrepancies', as: :discrepancies
|
|
10
|
+
get 'dashboard/audit_trail', to: 'dashboard#audit_trail', as: :audit_trail
|
|
11
|
+
get 'dashboard/audit_trail/:model_class/:id', to: 'dashboard#audit_trail_for_record', as: :audit_trail_for_record
|
|
12
|
+
|
|
13
|
+
# Privacy and GDPR routes
|
|
14
|
+
get 'privacy/subject/:subject_type/:subject_id', to: 'privacy#subject_data', as: :subject_data
|
|
15
|
+
get 'privacy/gdpr_report/:subject_type/:subject_id', to: 'privacy#gdpr_report', as: :gdpr_report
|
|
16
|
+
get 'privacy/portable_export/:subject_type/:subject_id', to: 'privacy#portable_export', as: :portable_export
|
|
17
|
+
get 'privacy/pii_inventory/:subject_type/:subject_id', to: 'privacy#pii_inventory', as: :pii_inventory
|
|
18
|
+
get 'privacy/data_lineage/:field_name', to: 'privacy#data_lineage', as: :data_lineage
|
|
19
|
+
get 'privacy/pii_detection', to: 'privacy#pii_detection', as: :pii_detection
|
|
20
|
+
get 'privacy/policy', to: 'privacy#policy', as: :privacy_policy
|
|
21
|
+
|
|
22
|
+
# Configuration and projections routes
|
|
23
|
+
get 'config/projections', to: 'dashboard#projections', as: :projections
|
|
24
|
+
get 'dashboard/schema', to: 'dashboard#schema', as: :schema
|
|
25
|
+
get 'dashboard/schema/history', to: 'dashboard#schema_history', as: :schema_history
|
|
26
|
+
get 'dashboard/schema/:version', to: 'dashboard#schema_version', as: :schema_version
|
|
27
|
+
|
|
28
|
+
# Event flow and visualization routes
|
|
29
|
+
get 'flow/timeline', to: 'flow#timeline', as: :timeline
|
|
30
|
+
get 'flow/event_chain/:model_class/:model_id', to: 'flow#event_chain', as: :event_chain
|
|
31
|
+
get 'flow/crud_mapping', to: 'flow#crud_mapping', as: :crud_mapping
|
|
32
|
+
get 'flow/visualization/:model_class/:model_id', to: 'flow#visualization', as: :visualization
|
|
33
|
+
get 'flow/correlation/:correlation_id', to: 'flow#correlation', as: :correlation
|
|
34
|
+
get 'flow/user_actions/:user_id', to: 'flow#user_actions', as: :user_actions
|
|
35
|
+
|
|
36
|
+
# Visualization routes (JSON API + HTML views)
|
|
37
|
+
get 'visualizations/event_graph', to: 'dashboard#event_graph_view', as: :event_graph_view
|
|
38
|
+
get 'visualizations/heatmap', to: 'dashboard#heatmap_view', as: :heatmap_view
|
|
39
|
+
get 'visualizations/event_graph.json', to: 'dashboard#event_graph', as: :event_graph_data
|
|
40
|
+
get 'visualizations/entity_graph/:model_class/:id.json', to: 'dashboard#entity_graph', as: :entity_graph_data
|
|
41
|
+
get 'visualizations/event_list.json', to: 'dashboard#event_list', as: :event_list_data
|
|
42
|
+
get 'visualizations/heatmap.json', to: 'dashboard#heatmap', as: :heatmap_data
|
|
43
|
+
get 'visualizations/model_heatmap/:model_class.json', to: 'dashboard#model_heatmap', as: :model_heatmap_data
|
|
44
|
+
|
|
45
|
+
# Formal verification routes (requires PetriFlow)
|
|
46
|
+
get 'verification', to: 'dashboard#verification', as: :verification
|
|
47
|
+
get 'verification.json', to: 'dashboard#verification_data', as: :verification_data
|
|
48
|
+
end
|