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.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "lib"
6
+ t.libs << "test"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ t.verbose = true
9
+ end
10
+
11
+ task default: :test
@@ -0,0 +1,110 @@
1
+ module PamDsl
2
+ # Represents a consent requirement
3
+ class ConsentRequirement
4
+ attr_reader :purpose, :required, :granular, :withdrawable, :description, :expires_after
5
+
6
+ def initialize(purpose)
7
+ @purpose = purpose.to_sym
8
+ @required = true
9
+ @granular = false
10
+ @withdrawable = true
11
+ @description = ""
12
+ @expires_after = nil
13
+ end
14
+
15
+ # Set if consent is required
16
+ def required!(value = true)
17
+ @required = value
18
+ self
19
+ end
20
+
21
+ # Enable granular consent
22
+ def granular!(value = true)
23
+ @granular = value
24
+ self
25
+ end
26
+
27
+ # Set if consent is withdrawable
28
+ def withdrawable!(value = true)
29
+ @withdrawable = value
30
+ self
31
+ end
32
+
33
+ # Set description
34
+ def describe(text)
35
+ @description = text
36
+ self
37
+ end
38
+
39
+ # Set expiration duration
40
+ def expires_in(duration)
41
+ @expires_after = duration
42
+ self
43
+ end
44
+
45
+ # Check if consent is required
46
+ def required?
47
+ @required
48
+ end
49
+
50
+ # Check if consent is granular
51
+ def granular?
52
+ @granular
53
+ end
54
+
55
+ # Check if consent is withdrawable
56
+ def withdrawable?
57
+ @withdrawable
58
+ end
59
+
60
+ # Check if consent has expired
61
+ def expired?(granted_at)
62
+ return false unless @expires_after
63
+ granted_at < (Time.current - @expires_after)
64
+ end
65
+ end
66
+
67
+ # Container for consent requirements
68
+ class ConsentPolicy
69
+ attr_reader :requirements
70
+
71
+ def initialize
72
+ @requirements = []
73
+ end
74
+
75
+ # Define consent requirement for a purpose
76
+ def for_purpose(purpose, &block)
77
+ requirement = ConsentRequirement.new(purpose)
78
+ requirement.instance_eval(&block) if block_given?
79
+ @requirements << requirement
80
+ requirement
81
+ end
82
+
83
+ # Get consent requirement for a purpose
84
+ def requirement_for(purpose)
85
+ @requirements.find { |req| req.purpose == purpose.to_sym }
86
+ end
87
+
88
+ # Check if consent is required for a purpose
89
+ def required_for?(purpose)
90
+ requirement = requirement_for(purpose)
91
+ requirement ? requirement.required? : false
92
+ end
93
+
94
+ # Validate consent for a purpose
95
+ def validate!(purpose, granted_at: nil, granted: false)
96
+ requirement = requirement_for(purpose)
97
+ return true unless requirement&.required?
98
+
99
+ unless granted
100
+ raise ConsentRequiredError, "Consent required for purpose: #{purpose}"
101
+ end
102
+
103
+ if granted_at && requirement.expired?(granted_at)
104
+ raise ConsentRequiredError, "Consent expired for purpose: #{purpose}"
105
+ end
106
+
107
+ true
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,76 @@
1
+ module PamDsl
2
+ # Represents a PII field definition
3
+ class Field
4
+ attr_reader :name, :type, :sensitivity, :purposes, :transformations, :metadata
5
+
6
+ SENSITIVITY_LEVELS = [:public, :internal, :confidential, :restricted].freeze
7
+
8
+ PII_TYPES = [
9
+ :email, :name, :phone, :address, :ssn, :date_of_birth,
10
+ :ip_address, :credit_card, :financial, :health, :biometric,
11
+ :location, :identifier, :credential, :token, :payment_token, :custom
12
+ ].freeze
13
+
14
+ def initialize(name, type:, sensitivity: :internal)
15
+ @name = name.to_sym
16
+ @type = type.to_sym
17
+ @sensitivity = sensitivity.to_sym
18
+ @purposes = []
19
+ @transformations = {}
20
+ @metadata = {}
21
+
22
+ validate!
23
+ end
24
+
25
+ # Define allowed purposes for this field
26
+ def allow_for(*purpose_names)
27
+ @purposes.concat(purpose_names.map(&:to_sym))
28
+ self
29
+ end
30
+
31
+ # Define transformation rules for different contexts
32
+ def transform(context, &block)
33
+ @transformations[context.to_sym] = block
34
+ self
35
+ end
36
+
37
+ # Add metadata
38
+ def meta(key, value)
39
+ @metadata[key] = value
40
+ self
41
+ end
42
+
43
+ # Check if field is allowed for a purpose
44
+ def allowed_for?(purpose)
45
+ @purposes.empty? || @purposes.include?(purpose.to_sym)
46
+ end
47
+
48
+ # Apply transformation if defined
49
+ def apply_transformation(context, value)
50
+ transformation = @transformations[context.to_sym]
51
+ transformation ? transformation.call(value) : value
52
+ end
53
+
54
+ # Check if field is sensitive
55
+ def sensitive?
56
+ [:confidential, :restricted].include?(@sensitivity)
57
+ end
58
+
59
+ # Check if field is highly restricted
60
+ def restricted?
61
+ @sensitivity == :restricted
62
+ end
63
+
64
+ private
65
+
66
+ def validate!
67
+ unless SENSITIVITY_LEVELS.include?(@sensitivity)
68
+ raise InvalidFieldError, "Invalid sensitivity level: #{@sensitivity}. Must be one of #{SENSITIVITY_LEVELS.join(', ')}"
69
+ end
70
+
71
+ unless PII_TYPES.include?(@type)
72
+ raise InvalidFieldError, "Invalid PII type: #{@type}. Must be one of #{PII_TYPES.join(', ')}"
73
+ end
74
+ end
75
+ end
76
+ end