Class: Vkit::Policy::BundleCompiler
- Inherits:
-
Object
- Object
- Vkit::Policy::BundleCompiler
- Defined in:
- lib/vkit/policy/bundle_compiler.rb
Constant Summary collapse
- FORMAT_VERSION =
"v1"
Class Method Summary collapse
-
.canonical_json(obj) ⇒ Object
Canonicalization.
- .compile!(org_slug:, bundle_version:, policies_dir:, registry_dir:, source: {}) ⇒ Object
- .extract_approval(p) ⇒ Object
- .extract_masking(p) ⇒ Object
- .load_installed_packs ⇒ Object
-
.load_policies(dir) ⇒ Object
Loading.
- .load_registry(dir) ⇒ Object
- .normalize_action(action) ⇒ Object
- .normalize_fields(fields) ⇒ Object
- .normalize_mask_method(method) ⇒ Object
- .normalize_policies(policies) ⇒ Object
- .normalize_registry(raw) ⇒ Object
-
.normalize_source(source) ⇒ Object
Normalization.
- .sort_keys_deep(value) ⇒ Object
Class Method Details
.canonical_json(obj) ⇒ Object
Canonicalization
177 178 179 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 177 def self.canonical_json(obj) JSON.generate(sort_keys_deep(obj)) end |
.compile!(org_slug:, bundle_version:, policies_dir:, registry_dir:, source: {}) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 12 def self.compile!(org_slug:, bundle_version:, policies_dir:, registry_dir:, source: {}) policies = load_policies(policies_dir) registry = load_registry(registry_dir) bundle = { "bundle" => { "format_version" => FORMAT_VERSION, "org_slug" => org_slug, "bundle_version" => bundle_version, "created_at" => Time.now.utc.iso8601, "source" => normalize_source(source), "checksum" => "" # filled below }, "registry" => registry, "policies" => normalize_policies(policies), "signing" => nil } bundle["bundle"]["installed_packs"] = load_installed_packs canonical = canonical_json(bundle) bundle["bundle"]["checksum"] = Digest::SHA256.hexdigest(canonical) bundle end |
.extract_approval(p) ⇒ Object
100 101 102 103 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 100 def self.extract_approval(p) return unless p.dig("action", "require_approval") { "approver_role" => p.dig("action", "approver_role") } end |
.extract_masking(p) ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 105 def self.extract_masking(p) return unless p.dig("action", "mask") raw = p["masking"] || p.dig("action", "masking") || {} default_method = normalize_mask_method(raw["default_method"]) if raw["default_method"] rules = case raw["rules"] when Hash raw["rules"].each_with_object({}) do |(field, method), acc| acc[field.to_s] = normalize_mask_method(method) end else {} end result = {} result["default_method"] = default_method if default_method result["rules"] = rules if rules.any? return if result.empty? result end |
.load_installed_packs ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 192 def self.load_installed_packs tracking_path = File.join(".vkit", "packs.yaml") return [] unless File.exist?(tracking_path) data = YAML.safe_load(File.read(tracking_path), permitted_classes: [], permitted_symbols: [], aliases: true) packs = data["installed_packs"] || {} packs.map do |name, | { "name" => name, "version" => ["version"] } end rescue [] end |
.load_policies(dir) ⇒ Object
Loading
38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 38 def self.load_policies(dir) files = Dir[File.join(dir, "*.y{a,}ml")].sort raise "No policy files found in #{dir}" if files.empty? files.map do |f| data = YAML.load_file(f) raise "Policy file #{f} must be a Hash" unless data.is_a?(Hash) PolicyValidator.validate!(data, file: File.basename(f)) data.merge("__file" => File.basename(f)) end end |
.load_registry(dir) ⇒ Object
52 53 54 55 56 57 58 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 52 def self.load_registry(dir) path = File.join(dir, "registry.yaml") raise "Missing datasets/registry.yaml" unless File.exist?(path) raw = YAML.load_file(path) normalize_registry(raw) end |
.normalize_action(action) ⇒ Object
91 92 93 94 95 96 97 98 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 91 def self.normalize_action(action) return "allow" unless action.is_a?(Hash) return "deny" if action["deny"] return "require_approval" if action["require_approval"] return "mask" if action["mask"] "allow" end |
.normalize_fields(fields) ⇒ Object
155 156 157 158 159 160 161 162 163 164 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 155 def self.normalize_fields(fields) fields.map do |name, | { "name" => name.to_s, "type" => ["type"], "sensitivity" => ["sensitivity"].to_s, "tags" => [["category"]].compact.map(&:to_s) } end end |
.normalize_mask_method(method) ⇒ Object
166 167 168 169 170 171 172 173 174 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 166 def self.normalize_mask_method(method) case method.to_s when "redact" then "full" when "hash" then "hash" when "truncate" then "partial" when "nullify" then "full" else "full" end end |
.normalize_policies(policies) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 65 def self.normalize_policies(policies) seen = {} normalized = policies.map do |p| id = p["id"].to_s.strip raise "Policy id missing in #{p["__file"]}" if id.empty? raise "Duplicate policy id: #{id}" if seen[id] seen[id] = true { "id" => id, "description" => p["description"], "match" => p["match"], "when" => p["context"], # ADAPT authoring → runtime "action" => normalize_action(p["action"]), "reason" => p.dig("action", "reason"), "approval" => extract_approval(p), "masking" => extract_masking(p), "ttl_seconds" => p.dig("action", "ttl"), "priority" => p["priority"] }.compact end normalized.sort_by { |p| [-(p["priority"] || 0), p["id"]] } end |
.normalize_registry(raw) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 134 def self.normalize_registry(raw) datasets = raw.map do |name, data| { "name" => name.to_s, "datasource" => data["datasource"].to_s, "fields" => normalize_fields(data["fields"] || {}) } end datasources = datasets .map { |d| d["datasource"] } .uniq .map { |ds| { "name" => ds, "type" => "postgres", "config" => {} } } { "datasets" => datasets, "datasources" => datasources } end |
.normalize_source(source) ⇒ Object
Normalization
61 62 63 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 61 def self.normalize_source(source) { "type" => "git" }.merge(source.transform_keys(&:to_s)) end |
.sort_keys_deep(value) ⇒ Object
181 182 183 184 185 186 187 188 189 190 |
# File 'lib/vkit/policy/bundle_compiler.rb', line 181 def self.sort_keys_deep(value) case value when Hash value.keys.sort.each_with_object({}) { |k, h| h[k] = sort_keys_deep(value[k]) } when Array value.map { |v| sort_keys_deep(v) } else value end end |