orfeas_pam_dsl 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.
@@ -0,0 +1,102 @@
1
+ module PamDsl
2
+ # Represents a data retention rule
3
+ class RetentionRule
4
+ attr_reader :model_class, :duration, :field_overrides, :conditions, :deletion_strategy
5
+
6
+ DELETION_STRATEGIES = [:hard_delete, :soft_delete, :anonymize, :archive].freeze
7
+
8
+ def initialize(model_class)
9
+ @model_class = model_class.to_s
10
+ @duration = nil
11
+ @field_overrides = {}
12
+ @conditions = []
13
+ @deletion_strategy = :soft_delete
14
+ end
15
+
16
+ # Set retention duration
17
+ def keep_for(duration)
18
+ @duration = duration
19
+ self
20
+ end
21
+
22
+ # Override retention for specific fields
23
+ def field(field_name, duration:)
24
+ @field_overrides[field_name.to_sym] = duration
25
+ self
26
+ end
27
+
28
+ # Add condition for retention
29
+ def when(&block)
30
+ @conditions << block
31
+ self
32
+ end
33
+
34
+ # Set deletion strategy
35
+ def on_expiry(strategy)
36
+ strategy_sym = strategy.to_sym
37
+ unless DELETION_STRATEGIES.include?(strategy_sym)
38
+ raise Error, "Invalid deletion strategy: #{strategy}. Must be one of #{DELETION_STRATEGIES.join(', ')}"
39
+ end
40
+ @deletion_strategy = strategy_sym
41
+ self
42
+ end
43
+
44
+ # Check if retention period has expired for a timestamp
45
+ def expired?(timestamp)
46
+ return false unless @duration
47
+ timestamp < (Time.current - @duration)
48
+ end
49
+
50
+ # Get retention duration for a specific field
51
+ def duration_for_field(field_name)
52
+ @field_overrides[field_name.to_sym] || @duration
53
+ end
54
+
55
+ # Check if conditions match for given context
56
+ def applies_to?(context)
57
+ return true if @conditions.empty?
58
+ @conditions.all? { |condition| condition.call(context) }
59
+ end
60
+ end
61
+
62
+ # Container for retention rules
63
+ class RetentionPolicy
64
+ attr_reader :rules, :default_duration
65
+
66
+ def initialize
67
+ @rules = []
68
+ @default_duration = 7.years
69
+ end
70
+
71
+ # Set default retention duration
72
+ def default(duration)
73
+ @default_duration = duration
74
+ self
75
+ end
76
+
77
+ # Define retention rule for a model
78
+ def for_model(model_class, &block)
79
+ rule = RetentionRule.new(model_class)
80
+ rule.instance_eval(&block) if block_given?
81
+ @rules << rule
82
+ rule
83
+ end
84
+
85
+ # Get retention rule for a model
86
+ def rule_for(model_class)
87
+ @rules.find { |rule| rule.model_class == model_class.to_s }
88
+ end
89
+
90
+ # Get retention duration for a model and field
91
+ def duration_for(model_class, field_name: nil)
92
+ rule = rule_for(model_class)
93
+ return @default_duration unless rule
94
+
95
+ if field_name
96
+ rule.duration_for_field(field_name) || rule.duration || @default_duration
97
+ else
98
+ rule.duration || @default_duration
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ # PAM DSL Privacy Tasks
4
+ #
5
+ # These tasks are automatically loaded in Rails apps that include the pam_dsl gem.
6
+ # Configure via config/initializers/pam_dsl.rb
7
+
8
+ namespace :pam_dsl do
9
+ namespace :report do
10
+ desc "Generate full privacy compliance report"
11
+ task full: :environment do
12
+ reporter = build_reporter
13
+ reporter.full_report
14
+ end
15
+
16
+ desc "Show PAM DSL policy summary"
17
+ task policy: :environment do
18
+ reporter = build_reporter
19
+ reporter.policy_summary
20
+ end
21
+
22
+ desc "Analyze PII in event store (requires Lyra)"
23
+ task pii: :environment do
24
+ reporter = build_reporter
25
+ reporter.pii_analysis
26
+ end
27
+
28
+ desc "Check retention compliance (requires Lyra)"
29
+ task retention: :environment do
30
+ reporter = build_reporter
31
+ reporter.retention_check
32
+ end
33
+
34
+ desc "Show PII access patterns (requires Lyra)"
35
+ task access_patterns: :environment do
36
+ reporter = build_reporter
37
+ reporter.access_patterns
38
+ end
39
+
40
+ desc "Generate GDPR Article 30 report"
41
+ task article_30: :environment do
42
+ reporter = build_reporter
43
+ reporter.article_30_report
44
+ end
45
+
46
+ desc "Export privacy report to JSON"
47
+ task :export, [:output_path] => :environment do |_t, args|
48
+ output_path = args[:output_path] || "tmp/privacy_report_#{Time.current.strftime('%Y%m%d_%H%M%S')}.json"
49
+ reporter = build_reporter
50
+ reporter.export_json(output_path)
51
+ end
52
+
53
+ desc "Compare two policies and generate a report"
54
+ task :compare, [:policy1, :policy2, :output_path] => :environment do |_t, args|
55
+ policy1_name = args[:policy1]&.to_sym
56
+ policy2_name = args[:policy2]&.to_sym
57
+
58
+ unless policy1_name && policy2_name
59
+ puts "Usage: rake pam_dsl:report:compare[policy1,policy2,output_path]"
60
+ puts ""
61
+ puts "Available policies:"
62
+ PamDsl.registry.policies.keys.each { |name| puts " - #{name}" }
63
+ abort
64
+ end
65
+
66
+ output_path = args[:output_path] || "reports/policy_comparison.md"
67
+
68
+ begin
69
+ comparator = PamDsl::PolicyComparator.new(policy1_name, policy2_name)
70
+ comparator.generate_report(output_path: output_path)
71
+ puts "Comparison report written to: #{output_path}"
72
+ rescue PamDsl::PolicyNotFoundError => e
73
+ abort "Error: #{e.message}"
74
+ end
75
+ end
76
+
77
+ def build_reporter
78
+ config = Rails.application.config.pam_dsl
79
+
80
+ policy_name = config.default_policy
81
+ unless policy_name
82
+ # Try to find any defined policy
83
+ policy_name = PamDsl.registry.policies.keys.first
84
+ if policy_name
85
+ puts "Using policy: #{policy_name}"
86
+ else
87
+ abort "No PAM DSL policy found. Define one or set config.pam_dsl.default_policy"
88
+ end
89
+ end
90
+
91
+ # Try to get Lyra's event store if available
92
+ event_store = nil
93
+ if defined?(Lyra) && Lyra.respond_to?(:event_store)
94
+ event_store = Lyra.event_store
95
+ end
96
+
97
+ PamDsl::Reporter.new(
98
+ policy_name,
99
+ organization: config.organization,
100
+ dpo_contact: config.dpo_contact,
101
+ event_store: event_store
102
+ )
103
+ end
104
+ end
105
+
106
+ namespace :generate do
107
+ desc "Generate a PAM DSL policy file with sensible defaults"
108
+ task :policy, [:name] => :environment do |_t, args|
109
+ name = args[:name] || "application"
110
+ generator = PamDsl::PolicyGenerator.new(name)
111
+ generator.generate
112
+ end
113
+
114
+ desc "Generate policy from existing ActiveRecord models"
115
+ task :from_models, [:name] => :environment do |_t, args|
116
+ name = args[:name] || "application"
117
+ generator = PamDsl::PolicyGenerator.new(name)
118
+ generator.generate_from_models
119
+ end
120
+ end
121
+ end
122
+
123
+ # Shorthand aliases
124
+ namespace :privacy do
125
+ desc "Generate full privacy compliance report (alias for pam_dsl:report:full)"
126
+ task report: "pam_dsl:report:full"
127
+
128
+ desc "Show policy summary (alias for pam_dsl:report:policy)"
129
+ task policy: "pam_dsl:report:policy"
130
+
131
+ desc "Check retention compliance (alias for pam_dsl:report:retention)"
132
+ task retention: "pam_dsl:report:retention"
133
+
134
+ desc "Generate Article 30 report (alias for pam_dsl:report:article_30)"
135
+ task article_30: "pam_dsl:report:article_30"
136
+
137
+ desc "Export report to JSON (alias for pam_dsl:report:export)"
138
+ task :export, [:output_path] => "pam_dsl:report:export"
139
+ end
@@ -0,0 +1,3 @@
1
+ module PamDsl
2
+ VERSION = "0.6.0"
3
+ end
data/lib/pam_dsl.rb ADDED
@@ -0,0 +1,67 @@
1
+ require "active_support"
2
+ require "active_support/core_ext"
3
+
4
+ require_relative "pam_dsl/version"
5
+ require_relative "pam_dsl/pii_detector"
6
+ require_relative "pam_dsl/pii_masker"
7
+ require_relative "pam_dsl/gdpr_compliance"
8
+ require_relative "pam_dsl/field"
9
+ require_relative "pam_dsl/purpose"
10
+ require_relative "pam_dsl/retention"
11
+ require_relative "pam_dsl/consent"
12
+ require_relative "pam_dsl/policy"
13
+ require_relative "pam_dsl/registry"
14
+ require_relative "pam_dsl/reporter"
15
+ require_relative "pam_dsl/policy_generator"
16
+ require_relative "pam_dsl/policy_comparator"
17
+
18
+ # Load Rails integration if Rails is available
19
+ require_relative "pam_dsl/railtie" if defined?(Rails::Railtie)
20
+
21
+ module PamDsl
22
+ class Error < StandardError; end
23
+ class PolicyNotFoundError < Error; end
24
+ class InvalidFieldError < Error; end
25
+ class ConsentRequiredError < Error; end
26
+
27
+ class << self
28
+ # Rails configuration (set by Railtie)
29
+ attr_accessor :rails_config
30
+
31
+ # Global registry for policies
32
+ def registry
33
+ @registry ||= Registry.new
34
+ end
35
+
36
+ # Define a privacy policy using block DSL
37
+ def define_policy(name, &block)
38
+ policy = Policy.new(name)
39
+ policy.instance_eval(&block)
40
+ registry.register(name, policy)
41
+ policy
42
+ end
43
+
44
+ # Get a defined policy
45
+ def policy(name)
46
+ registry.get(name) || raise(PolicyNotFoundError, "Policy '#{name}' not found")
47
+ end
48
+
49
+ # Reset all policies (useful for testing)
50
+ def reset!
51
+ @registry = Registry.new
52
+ end
53
+
54
+ # Convenience method to create a reporter
55
+ def reporter(policy_name = nil, **options)
56
+ policy_name ||= rails_config&.default_policy || registry.policies.keys.first
57
+ raise PolicyNotFoundError, "No policy defined" unless policy_name
58
+
59
+ Reporter.new(
60
+ policy_name,
61
+ organization: options[:organization] || rails_config&.organization,
62
+ dpo_contact: options[:dpo_contact] || rails_config&.dpo_contact,
63
+ event_store: options[:event_store]
64
+ )
65
+ end
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: orfeas_pam_dsl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ platform: ruby
6
+ authors:
7
+ - Michail Pantelelis
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '6.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: minitest
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: minitest-reporters
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.5'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.5'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '13.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '13.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: simplecov
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.22'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.22'
82
+ description: A declarative DSL for defining privacy policies, PII fields, consent
83
+ requirements, and retention rules using the Privacy Attribute Matrix (PAM) model
84
+ for privacy-aware event monitoring. Part of the ORFEAS (Object-Relational to Event-Sourcing
85
+ Architecture) framework.
86
+ email:
87
+ - mpantel@aegean.gr
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - CHANGELOG.md
93
+ - MIT-LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - lib/pam_dsl.rb
97
+ - lib/pam_dsl/consent.rb
98
+ - lib/pam_dsl/field.rb
99
+ - lib/pam_dsl/gdpr_compliance.rb
100
+ - lib/pam_dsl/pii_detector.rb
101
+ - lib/pam_dsl/pii_masker.rb
102
+ - lib/pam_dsl/policy.rb
103
+ - lib/pam_dsl/policy_comparator.rb
104
+ - lib/pam_dsl/policy_generator.rb
105
+ - lib/pam_dsl/purpose.rb
106
+ - lib/pam_dsl/railtie.rb
107
+ - lib/pam_dsl/registry.rb
108
+ - lib/pam_dsl/reporter.rb
109
+ - lib/pam_dsl/retention.rb
110
+ - lib/pam_dsl/tasks/privacy.rake
111
+ - lib/pam_dsl/version.rb
112
+ homepage: https://github.com/mpantel/lyra-engine/gems/pam-dsl
113
+ licenses:
114
+ - MIT
115
+ metadata:
116
+ homepage_uri: https://github.com/mpantel/lyra-engine/gems/pam-dsl
117
+ source_code_uri: https://github.com/mpantel/lyra-engine/gems/pam-dsl/tree/main/gems/pam_dsl
118
+ changelog_uri: https://github.com/mpantel/lyra-engine/gems/pam-dsl/blob/main/gems/pam_dsl/CHANGELOG.md
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubygems_version: 4.0.3
134
+ specification_version: 4
135
+ summary: Privacy Attribute Matrix (PAM) DSL for ORFEAS Framework
136
+ test_files: []