strata-cli 0.1.11 → 0.1.12
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 +4 -4
- data/lib/strata/cli/generators/policy.rb +68 -0
- data/lib/strata/cli/generators/project.rb +15 -2
- data/lib/strata/cli/generators/templates/AGENTS.md +4 -2
- data/lib/strata/cli/generators/templates/security.yml +77 -0
- data/lib/strata/cli/helpers/prompts.rb +15 -0
- data/lib/strata/cli/sub_commands/audit.rb +1 -1
- data/lib/strata/cli/sub_commands/create.rb +103 -0
- data/lib/strata/cli/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 060e97cfbb307efe69eff087983dc1d28e3e4008bd13f5529a7560e5d7e00f06
|
|
4
|
+
data.tar.gz: 134dbf05ad0b5870433fcb3c9a90e50853ed0a9f97c3378a97d208b4f6866dae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf09f28b188e03929870b14515efb76d86f9aab034d048a3e2e5c49751f722718cf6a4d37a8c7d7e2966bd080aac758956df51951758a73b98a885003cbd5bb4
|
|
7
|
+
data.tar.gz: ab72ddfa33b44cc8da63cb10c2920575030fa53a4216d8d75eb9e2a38f2eebdc3f36afd3264c6dc81c00b440c6df4eb029df86126e600d16455def0cf311efb5
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "group"
|
|
4
|
+
require_relative "../output"
|
|
5
|
+
|
|
6
|
+
module Strata
|
|
7
|
+
module CLI
|
|
8
|
+
module Generators
|
|
9
|
+
# Appends a new policy entry to the project's security.yml.
|
|
10
|
+
# Receives a fully-resolved attrs hash from the Create subcommand.
|
|
11
|
+
class Policy < Group
|
|
12
|
+
argument :attrs, type: :hash
|
|
13
|
+
|
|
14
|
+
def append_policy_to_security_yml
|
|
15
|
+
security_file = "security.yml"
|
|
16
|
+
|
|
17
|
+
unless File.exist?(security_file)
|
|
18
|
+
raise Strata::CommandError,
|
|
19
|
+
"security.yml not found in the current directory. Run 'strata init' first."
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
content = File.read(security_file)
|
|
23
|
+
new_entry = format_policy_yaml(attrs)
|
|
24
|
+
|
|
25
|
+
updated = if content.match?(/^policies:\s*\[\]/)
|
|
26
|
+
# First policy — replace inline empty array with block sequence
|
|
27
|
+
content.sub(/^policies:\s*\[\]/, "policies:\n#{new_entry}")
|
|
28
|
+
else
|
|
29
|
+
# Subsequent policy — append after existing entries
|
|
30
|
+
content.rstrip + "\n#{new_entry}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
File.write(security_file, updated)
|
|
34
|
+
Output.print_status(:updated, security_file, type: :success, context: self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def format_policy_yaml(a)
|
|
40
|
+
lines = []
|
|
41
|
+
lines << " - name: #{a[:name]}"
|
|
42
|
+
lines << " mode: #{a[:mode]}"
|
|
43
|
+
lines << " mask_value: \"#{a[:mask_value]}\"" if a[:mode] == "mask_data"
|
|
44
|
+
lines << " triggers:"
|
|
45
|
+
|
|
46
|
+
tags = Array(a[:field_tags])
|
|
47
|
+
lines << " field_tags: [#{tags.join(", ")}]"
|
|
48
|
+
|
|
49
|
+
field_names = Array(a[:field_names]).reject(&:empty?)
|
|
50
|
+
unless field_names.empty?
|
|
51
|
+
lines << " field_names:"
|
|
52
|
+
field_names.each { |n| lines << " - #{n}" }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
lines << " context_dimension: #{a[:context_dimension]}"
|
|
56
|
+
lines << " permission_resolution:"
|
|
57
|
+
lines << " source: #{a[:permission_source]}"
|
|
58
|
+
lines << " value_from: #{a[:value_from]}"
|
|
59
|
+
lines << " tag_key: #{a[:tag_key]}" if a[:tag_key].to_s.strip.length > 0
|
|
60
|
+
lines << " bypass:"
|
|
61
|
+
lines << " system_admin: #{a[:bypass_system_admin]}"
|
|
62
|
+
lines << " project_admin: #{a[:bypass_project_admin]}"
|
|
63
|
+
lines.join("\n") + "\n"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -57,7 +57,7 @@ module Strata
|
|
|
57
57
|
def create_project_structure
|
|
58
58
|
return if cloned_from_git?
|
|
59
59
|
|
|
60
|
-
empty_directory uid
|
|
60
|
+
empty_directory uid unless existing_directory?
|
|
61
61
|
empty_directory File.join(uid, "models")
|
|
62
62
|
empty_directory File.join(uid, "tests")
|
|
63
63
|
end
|
|
@@ -102,6 +102,12 @@ module Strata
|
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
def create_security_file
|
|
106
|
+
return if cloned_from_git?
|
|
107
|
+
|
|
108
|
+
copy_file "security.yml", "#{uid}/security.yml"
|
|
109
|
+
end
|
|
110
|
+
|
|
105
111
|
def initialize_git
|
|
106
112
|
return if cloned_from_git?
|
|
107
113
|
|
|
@@ -113,7 +119,8 @@ module Strata
|
|
|
113
119
|
|
|
114
120
|
def setup_datasource
|
|
115
121
|
return if cloned_from_git?
|
|
116
|
-
return if options.key?(:datasource)
|
|
122
|
+
return if options.key?(:datasource)
|
|
123
|
+
return if existing_directory?
|
|
117
124
|
|
|
118
125
|
print_status(:setup, "Let's configure your first datasource", type: :info)
|
|
119
126
|
print_info("\n")
|
|
@@ -140,6 +147,10 @@ module Strata
|
|
|
140
147
|
@cloned_from_git ||= false
|
|
141
148
|
end
|
|
142
149
|
|
|
150
|
+
def existing_directory?
|
|
151
|
+
File.directory?(name.to_s)
|
|
152
|
+
end
|
|
153
|
+
|
|
143
154
|
def fetch_project_info
|
|
144
155
|
conn = Faraday.new(url: options[:source]) do |f|
|
|
145
156
|
f.request :authorization, "Bearer", options[:api_key]
|
|
@@ -180,6 +191,8 @@ module Strata
|
|
|
180
191
|
def uid
|
|
181
192
|
@uid ||= if options.key?(:source) && @project_info
|
|
182
193
|
@project_info["uid"] || options[:source].split("/").last
|
|
194
|
+
elsif existing_directory?
|
|
195
|
+
name.to_s
|
|
183
196
|
else
|
|
184
197
|
Utils.url_safe_str(name)
|
|
185
198
|
end
|
|
@@ -28,11 +28,13 @@ Read this file end-to-end before creating or editing `models/**/*.yml`. Do not m
|
|
|
28
28
|
|
|
29
29
|
These are non-negotiable. Violations break deploy, query planning, or production reports.
|
|
30
30
|
|
|
31
|
-
1. **Every field `name` is unique project-wide** — one name = one dimension or measure entity across all `tbl.*.yml` files. Strata has no `table.field` namespaces; bracket references like `[Total Revenue]` resolve by name alone.
|
|
31
|
+
1. **Every field `name` is unique project-wide per dimension or measure** — one name = one dimension or measure entity across all `tbl.*.yml` files. Strata has no `table.field` namespaces; bracket references like `[Total Revenue]` resolve by name alone.
|
|
32
32
|
2. **`many_to_many` join cardinality is not supported.** Use a junction/bridge table with two relationships (`one_to_many` + `many_to_one`) instead.
|
|
33
33
|
3. **Measures on unrelated detail facts must have distinct names** — e.g. `Store Gross Sales`, `Catalog Gross Sales`, not one `Gross Sales` on three channel facts. See [Naming conventions](#naming-conventions).
|
|
34
|
-
4. **Dimensions may share names** when the business role is the same; Strata picks among tables at query time. **Measures do not combine that way** — duplicate measure names
|
|
34
|
+
4. **Dimensions may share names** when the business role is the same; Strata picks among tables at query time. **Measures do not combine that way** — duplicate measure names implies varying levels of aggregation of the same basic fact. The correct table and measure combination will be determined by context (i.e. what dimensions are present in the same query.)
|
|
35
35
|
5. **Cross-fact totals use compound measures or blending** — not the same measure name on multiple facts. See [Combined metrics across facts](#combined-metrics-across-facts).
|
|
36
|
+
6. Security policies can be implemented in the security.yml file. Use the guidelines documented therin.
|
|
37
|
+
7. Strata supports role playing or aliasing of dimension tables. For example, user wants date_dim table to also be used when query "Order Return Date". In that case, we simply point to the same physical_name "date_dim" but set the table name to "Order Return Date". If the user asks to alias an dimension table from an existing model we can simply copy the existing model, change the table name, and prefix all the dimensions in it with the appropriate prefix representing the role.
|
|
36
38
|
|
|
37
39
|
## Unsupported or impossible requirements
|
|
38
40
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Semantic Security Policies
|
|
2
|
+
#
|
|
3
|
+
# This file defines row-level security policies enforced at query planning time.
|
|
4
|
+
# Policies are branch-scoped and deployed alongside your semantic model.
|
|
5
|
+
#
|
|
6
|
+
# Two enforcement modes:
|
|
7
|
+
#
|
|
8
|
+
# mask_data — Sensitive column values are replaced with a mask string.
|
|
9
|
+
# Rows remain in the result set; users can see that restricted
|
|
10
|
+
# data exists but cannot read it.
|
|
11
|
+
#
|
|
12
|
+
# filter_data — Rows for context values the user cannot access are removed
|
|
13
|
+
# entirely. Restricted data is invisible to the user.
|
|
14
|
+
#
|
|
15
|
+
# How it works:
|
|
16
|
+
# 1. A policy is triggered when a query projects a field tagged with one of
|
|
17
|
+
# the trigger tags (e.g. "pii").
|
|
18
|
+
# 2. The context_dimension defines the security boundary (e.g. "Call Center ID").
|
|
19
|
+
# It must be reachable in every universe that contains a triggered field,
|
|
20
|
+
# otherwise the query is rejected with a hard error.
|
|
21
|
+
# 3. Permission resolution determines which context dimension values the current
|
|
22
|
+
# user is allowed to see, derived from their group memberships or user record.
|
|
23
|
+
#
|
|
24
|
+
# Tagging fields: add a tags key to any dimension in your table YAML files:
|
|
25
|
+
#
|
|
26
|
+
# fields:
|
|
27
|
+
# - type: dimension
|
|
28
|
+
# name: Agent Name
|
|
29
|
+
# tags: [pii]
|
|
30
|
+
# data_type: string
|
|
31
|
+
# expression:
|
|
32
|
+
# sql: agent_name
|
|
33
|
+
|
|
34
|
+
policies: []
|
|
35
|
+
|
|
36
|
+
# ── Example: mask PII dimensions scoped by call center ──────────────────────
|
|
37
|
+
#
|
|
38
|
+
# - name: PII Call Center Masking
|
|
39
|
+
# mode: mask_data # mask_data | filter_data
|
|
40
|
+
# mask_value: "#######" # Value shown to users without access (mask_data only)
|
|
41
|
+
#
|
|
42
|
+
# triggers:
|
|
43
|
+
# field_tags: [pii] # Triggers on any field tagged "pii"
|
|
44
|
+
# # field_names: # Optional: trigger on specific fields by display name
|
|
45
|
+
# # - Agent Name # Resolved to UIDs at deploy time
|
|
46
|
+
#
|
|
47
|
+
# # Dimension that scopes visibility. Resolved by name at deploy time.
|
|
48
|
+
# # Must be reachable in the universe for any query that includes a triggered field.
|
|
49
|
+
# context_dimension: Call Center ID
|
|
50
|
+
#
|
|
51
|
+
# permission_resolution:
|
|
52
|
+
# source: groups # groups | user
|
|
53
|
+
# value_from: tag # groups: name | tag / user: email | tag
|
|
54
|
+
# tag_key: call_center_id # Required when value_from is "tag".
|
|
55
|
+
# # Extracts the value from group tags formatted as "call_center_id:TMNT"
|
|
56
|
+
#
|
|
57
|
+
# bypass:
|
|
58
|
+
# system_admin: true # System admins bypass this policy entirely
|
|
59
|
+
# project_admin: false
|
|
60
|
+
#
|
|
61
|
+
# ── Example: filter rows so each user sees only their own employee record ────
|
|
62
|
+
#
|
|
63
|
+
# - name: Employee Self-Service
|
|
64
|
+
# mode: filter_data
|
|
65
|
+
#
|
|
66
|
+
# triggers:
|
|
67
|
+
# field_tags: [employee_pii]
|
|
68
|
+
#
|
|
69
|
+
# context_dimension: Employee Email
|
|
70
|
+
#
|
|
71
|
+
# permission_resolution:
|
|
72
|
+
# source: user # Resolve from the current user's own attributes
|
|
73
|
+
# value_from: email # Allowed value = the authenticated user's email address
|
|
74
|
+
#
|
|
75
|
+
# bypass:
|
|
76
|
+
# system_admin: true
|
|
77
|
+
# project_admin: false
|
|
@@ -28,6 +28,21 @@ module Strata
|
|
|
28
28
|
MSG_MODELS_LIST_HEADER = "\n Semantic Models:\n"
|
|
29
29
|
MSG_MODELS_COUNT = "\n Total: %d model(s)\n"
|
|
30
30
|
|
|
31
|
+
# Policy Command Prompts
|
|
32
|
+
MSG_POLICY_NAME = " Policy name:"
|
|
33
|
+
MSG_POLICY_MODE = " Enforcement mode:"
|
|
34
|
+
MSG_POLICY_MASK_VALUE = " Mask value (shown for restricted data):"
|
|
35
|
+
MSG_POLICY_FIELD_TAGS = " Field tags that trigger this policy (comma-separated, e.g. pii,hipaa):"
|
|
36
|
+
MSG_POLICY_FIELD_NAMES = " Specific field names (comma-separated, optional — leave blank to skip):"
|
|
37
|
+
MSG_POLICY_CONTEXT_DIM = " Context dimension name:"
|
|
38
|
+
MSG_POLICY_PERM_SOURCE = " Permission source:"
|
|
39
|
+
MSG_POLICY_VALUE_FROM = " Resolve allowed values from:"
|
|
40
|
+
MSG_POLICY_TAG_KEY = " Tag key (e.g. call_center_id):"
|
|
41
|
+
MSG_POLICY_BYPASS_ADMIN = " Bypass for system admins?"
|
|
42
|
+
MSG_POLICY_BYPASS_PROJECT = " Bypass for project admins?"
|
|
43
|
+
MSG_POLICY_CREATED = "\n✔ Policy '%s' added to security.yml"
|
|
44
|
+
MSG_POLICY_HINT = "\n💡 Run 'strata deploy' to activate this policy"
|
|
45
|
+
|
|
31
46
|
# Migration hook options
|
|
32
47
|
MIGRATION_HOOK_OPTIONS = {
|
|
33
48
|
"pre (before deployment)" => "pre",
|
|
@@ -30,7 +30,7 @@ module Strata
|
|
|
30
30
|
ALLOWED_FIELD_KEYS = %w[
|
|
31
31
|
type name description hidden grains data_type display_type format
|
|
32
32
|
secure disable_listing value_list_size snapshot exclusion_type
|
|
33
|
-
exclusions inclusions extended_blend_group synonyms expression
|
|
33
|
+
exclusions inclusions extended_blend_group synonyms expression tags
|
|
34
34
|
].freeze
|
|
35
35
|
ALLOWED_EXPRESSION_KEYS = %w[sql array lookup primary_key].freeze
|
|
36
36
|
EXPRESSION_BOOLEAN_KEYS = %w[array lookup primary_key].freeze
|
|
@@ -51,6 +51,21 @@ module Strata
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
desc "policy [NAME]", "Scaffold a new security policy in security.yml"
|
|
55
|
+
method_option :interactive, aliases: "-i", type: :boolean, default: false,
|
|
56
|
+
desc: "Walk through all options interactively"
|
|
57
|
+
method_option :mode, type: :string, desc: "Enforcement mode: mask_data or filter_data"
|
|
58
|
+
method_option :tags, type: :array, desc: "Field tags that trigger this policy"
|
|
59
|
+
method_option :context, type: :string, desc: "Context dimension name"
|
|
60
|
+
|
|
61
|
+
def policy(name = nil)
|
|
62
|
+
attrs = policy_interactive_mode? ? collect_policy_interactive(name) : collect_policy_fast(name)
|
|
63
|
+
require_relative "../generators/policy"
|
|
64
|
+
Generators::Policy.new([attrs]).invoke_all
|
|
65
|
+
say MSG_POLICY_CREATED % attrs[:name], :green
|
|
66
|
+
say MSG_POLICY_HINT, :yellow
|
|
67
|
+
end
|
|
68
|
+
|
|
54
69
|
desc "relation RELATION_PATH", "Create a relation (join) definition file"
|
|
55
70
|
long_desc_from_file("create/relation")
|
|
56
71
|
|
|
@@ -122,6 +137,94 @@ module Strata
|
|
|
122
137
|
|
|
123
138
|
private
|
|
124
139
|
|
|
140
|
+
# ── Policy helpers ────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
def policy_interactive_mode?
|
|
143
|
+
options[:interactive] ||
|
|
144
|
+
(options[:mode].nil? && options[:tags].nil? && options[:context].nil?)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def collect_policy_fast(name)
|
|
148
|
+
{
|
|
149
|
+
name: name || "New Policy",
|
|
150
|
+
mode: options[:mode] || "mask_data",
|
|
151
|
+
mask_value: "#######",
|
|
152
|
+
field_tags: Array(options[:tags]),
|
|
153
|
+
field_names: [],
|
|
154
|
+
context_dimension: options[:context] || "<context_dimension>",
|
|
155
|
+
permission_source: "groups",
|
|
156
|
+
value_from: "tag",
|
|
157
|
+
tag_key: "<tag_key>",
|
|
158
|
+
bypass_system_admin: true,
|
|
159
|
+
bypass_project_admin: false
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def collect_policy_interactive(name)
|
|
164
|
+
say "\n Policy\n", :bold
|
|
165
|
+
policy_name = name || prompt.ask(MSG_POLICY_NAME) { |q| q.required true }
|
|
166
|
+
|
|
167
|
+
mode = prompt.select(MSG_POLICY_MODE, [
|
|
168
|
+
{name: "mask_data — replace restricted values with a mask", value: "mask_data"},
|
|
169
|
+
{name: "filter_data — remove restricted rows entirely", value: "filter_data"}
|
|
170
|
+
])
|
|
171
|
+
|
|
172
|
+
mask_value = if mode == "mask_data"
|
|
173
|
+
prompt.ask(MSG_POLICY_MASK_VALUE, default: "#######")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
say "\n Triggers\n", :bold
|
|
177
|
+
tags_raw = prompt.ask(MSG_POLICY_FIELD_TAGS, default: "")
|
|
178
|
+
field_tags = tags_raw.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
179
|
+
|
|
180
|
+
names_raw = prompt.ask(MSG_POLICY_FIELD_NAMES, default: "")
|
|
181
|
+
field_names = names_raw.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
182
|
+
|
|
183
|
+
say "\n Context\n", :bold
|
|
184
|
+
context_dimension = prompt.ask(MSG_POLICY_CONTEXT_DIM) { |q| q.required true }
|
|
185
|
+
|
|
186
|
+
say "\n Permission Resolution\n", :bold
|
|
187
|
+
permission_source = prompt.select(MSG_POLICY_PERM_SOURCE, [
|
|
188
|
+
{name: "groups — derive from the user's group memberships", value: "groups"},
|
|
189
|
+
{name: "user — derive from the user's own attributes", value: "user"}
|
|
190
|
+
])
|
|
191
|
+
|
|
192
|
+
value_from_choices = if permission_source == "groups"
|
|
193
|
+
[
|
|
194
|
+
{name: "tag — extract value from a group tag (e.g. call_center_id:TMNT)", value: "tag"},
|
|
195
|
+
{name: "name — use the group's display name as the allowed value", value: "name"}
|
|
196
|
+
]
|
|
197
|
+
else
|
|
198
|
+
[
|
|
199
|
+
{name: "email — the user's email address", value: "email"},
|
|
200
|
+
{name: "tag — extract value from a user tag", value: "tag"}
|
|
201
|
+
]
|
|
202
|
+
end
|
|
203
|
+
value_from = prompt.select(MSG_POLICY_VALUE_FROM, value_from_choices)
|
|
204
|
+
|
|
205
|
+
tag_key = if value_from == "tag"
|
|
206
|
+
prompt.ask(MSG_POLICY_TAG_KEY) { |q| q.required true }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
say "\n Bypass\n", :bold
|
|
210
|
+
bypass_system_admin = prompt.yes?(MSG_POLICY_BYPASS_ADMIN, default: true)
|
|
211
|
+
bypass_project_admin = prompt.yes?(MSG_POLICY_BYPASS_PROJECT, default: false)
|
|
212
|
+
|
|
213
|
+
{
|
|
214
|
+
name: policy_name,
|
|
215
|
+
mode: mode,
|
|
216
|
+
mask_value: mask_value,
|
|
217
|
+
field_tags: field_tags,
|
|
218
|
+
field_names: field_names,
|
|
219
|
+
context_dimension: context_dimension,
|
|
220
|
+
permission_source: permission_source,
|
|
221
|
+
value_from: value_from,
|
|
222
|
+
tag_key: tag_key,
|
|
223
|
+
bypass_system_admin: bypass_system_admin,
|
|
224
|
+
bypass_project_admin: bypass_project_admin
|
|
225
|
+
}
|
|
226
|
+
end
|
|
227
|
+
|
|
125
228
|
def handle_table_creation_with_path(table_path)
|
|
126
229
|
# Parse the path to extract schema, physical name, and model path
|
|
127
230
|
parsed = parse_table_path(table_path)
|
data/lib/strata/cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: strata-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ajo Abraham
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.4.
|
|
19
|
+
version: 0.4.4
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.4.
|
|
26
|
+
version: 0.4.4
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: aws-sdk-athena
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -268,6 +268,7 @@ files:
|
|
|
268
268
|
- lib/strata/cli/generators/datasource.rb
|
|
269
269
|
- lib/strata/cli/generators/group.rb
|
|
270
270
|
- lib/strata/cli/generators/migration.rb
|
|
271
|
+
- lib/strata/cli/generators/policy.rb
|
|
271
272
|
- lib/strata/cli/generators/project.rb
|
|
272
273
|
- lib/strata/cli/generators/relation.rb
|
|
273
274
|
- lib/strata/cli/generators/table.rb
|
|
@@ -286,6 +287,7 @@ files:
|
|
|
286
287
|
- lib/strata/cli/generators/templates/migration.swap.yml
|
|
287
288
|
- lib/strata/cli/generators/templates/project.yml
|
|
288
289
|
- lib/strata/cli/generators/templates/rel.domain.yml
|
|
290
|
+
- lib/strata/cli/generators/templates/security.yml
|
|
289
291
|
- lib/strata/cli/generators/templates/strata.yml
|
|
290
292
|
- lib/strata/cli/generators/templates/table.table_name.yml
|
|
291
293
|
- lib/strata/cli/generators/templates/test.yml
|