omen 0.2.2 → 0.3.1
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/CHANGELOG.md +18 -0
- data/README.md +19 -0
- data/app/models/omen/instructions.md +16 -0
- data/app/models/omen/instructions.rb +4 -1
- data/lib/omen/attributes.rb +35 -0
- data/lib/omen/distance.rb +31 -0
- data/lib/omen/grants.rb +39 -0
- data/lib/omen/inquirer.rb +31 -40
- data/lib/omen/version.rb +1 -1
- data/lib/omen.rb +3 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f96e34cbde627e0781a12488ba4308d704c6896a71fc44b031b7d412512c22eb
|
|
4
|
+
data.tar.gz: 408ad7c0b4a13f8ef3aef1b1c271e81ac116791ff627ffa2d056f5ecb6c45e34
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd53d5b7acb9fea6560bf0edbd5e1fb8f26200d99edaa52dfd9a81c9945dbd154a69514a7ba0028a025f36284bbd04be01e7aa57df61b7c01fa68ceb974638fd
|
|
7
|
+
data.tar.gz: a5b4226127e861f2600ebf62eb1d0c10ae2921886caf8e27b8041b411ac1b904c5e3406a0265f11e1f4c54a80ae9fa33994f396ec9adbc0df74c57b2304946df
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 0.3.1 - 2026-08-24
|
|
11
|
+
|
|
12
|
+
* [Fix] Stop asserting the role attributes only a superuser may set, since `NOSUPERUSER`,
|
|
13
|
+
`NOBYPASSRLS` and `NOREPLICATION` are refused outright by every managed database -- a role is
|
|
14
|
+
created without them anyway, so they are read back and warned about rather than set
|
|
15
|
+
* [Fix] Run each statement of the grant in a savepoint of its own, so one a database refuses no
|
|
16
|
+
longer discards the grants, the revocations and both functions behind it
|
|
17
|
+
|
|
18
|
+
## 0.3.0 - 2026-08-24
|
|
19
|
+
|
|
20
|
+
* [Feature] Create a `miles_between(lat1, lng1, lat2, lng2)` function and name it in the prompt,
|
|
21
|
+
so a radius is four arguments rather than a dozen nested trigonometric calls that a reply
|
|
22
|
+
balances by hand -- and gets wrong
|
|
23
|
+
* [Fix] Tell Claude that text is single-quoted and an apostrophe inside it is doubled, since a
|
|
24
|
+
double-quoted literal is read as a column name and refuses the whole statement
|
|
25
|
+
* [Fix] Tell Claude to declare a `combine` where an encrypted value belongs inside a sentence,
|
|
26
|
+
rather than returning the pieces as columns for the page to draw apart
|
|
27
|
+
|
|
10
28
|
## 0.2.2 - 2026-08-24
|
|
11
29
|
|
|
12
30
|
* [Fix] Run a load hook as each model loads, so a host declares `broadcasts_refreshes` and the
|
data/README.md
CHANGED
|
@@ -50,6 +50,21 @@ never crosses a breaking change.
|
|
|
50
50
|
question ever asked.
|
|
51
51
|
</details>
|
|
52
52
|
|
|
53
|
+
**Two database functions**
|
|
54
|
+
|
|
55
|
+
<details>
|
|
56
|
+
<summary>A timestamp and a distance are read through a function, never an expression. </summary>
|
|
57
|
+
`db:omen:grant` creates both. `eastern()` hands a stored timestamp back in the zone the company
|
|
58
|
+
works in, so every date in the prompt means the same whole days, and
|
|
59
|
+
`miles_between(lat1, lng1, lat2, lng2)` answers a great-circle distance in miles. The prompt
|
|
60
|
+
names each and forbids writing either by hand: a conversion assembled per query drifts, and a
|
|
61
|
+
great-circle expression runs to a dozen nested calls that a reply balances by hand and gets
|
|
62
|
+
wrong. Both are `LANGUAGE sql IMMUTABLE` and executable by anyone, so neither needs a grant.
|
|
63
|
+
An app in another zone renames the first; an app whose tables carry no coordinates simply never
|
|
64
|
+
calls the second. Neither can be a migration: Rails' `:ruby` schema format dumps no functions,
|
|
65
|
+
so `db:schema:load` would drop one a migration had made.
|
|
66
|
+
</details>
|
|
67
|
+
|
|
53
68
|
## Configuration
|
|
54
69
|
|
|
55
70
|
Installing by adding to your Gemfile and running three commands in your terminal:
|
|
@@ -132,6 +147,10 @@ usually does — the role is made by hand and the revocation with it:
|
|
|
132
147
|
REVOKE SELECT ON omen_readings, omen_questions, omen_answers FROM omen_inquirer;
|
|
133
148
|
```
|
|
134
149
|
|
|
150
|
+
A managed database also refuses `ALTER ROLE ... NOSUPERUSER`, since only a superuser may say it.
|
|
151
|
+
Omen skips that statement and carries on rather than stopping, then reads the role back and says
|
|
152
|
+
so if it holds `SUPERUSER`, `BYPASSRLS` or `REPLICATION` -- which a role it created never does.
|
|
153
|
+
|
|
135
154
|
A missed table there means Claude is shown the log of every question ever asked.
|
|
136
155
|
|
|
137
156
|
## License
|
|
@@ -24,6 +24,11 @@ comes back however you write it. A `WITH` clause is fine.
|
|
|
24
24
|
Prefer `count(*)`, `group by` and aggregates over returning raw rows: a question about how many
|
|
25
25
|
or about which is most is answered better by ten rows than by a thousand.
|
|
26
26
|
|
|
27
|
+
Text goes in single quotes, and an apostrophe inside it is written twice: `'it''s'`. Double
|
|
28
|
+
quotes name a column, so `"it's here"` is read as the name of a column, no such column is found
|
|
29
|
+
and the whole statement is refused. It is the one mistake that still looks like text after you
|
|
30
|
+
have made it, and switching quote style to avoid an apostrophe is how you make it.
|
|
31
|
+
|
|
27
32
|
## Reading the schema
|
|
28
33
|
|
|
29
34
|
Timestamps are stored in UTC, and `%{eastern}()` is the one way to read one: it hands the same
|
|
@@ -36,6 +41,12 @@ Today is %{today}. Resolve every relative date yourself; the query has no idea w
|
|
|
36
41
|
month" means, and it must never ask the database what time it is -- `now()` and
|
|
37
42
|
`current_timestamp` are the clock of the machine, not the date above.
|
|
38
43
|
|
|
44
|
+
Where a table carries coordinates, the distance between two points in miles is
|
|
45
|
+
`%{miles}(lat1, lng1, lat2, lng2)`, so a radius reads
|
|
46
|
+
`WHERE %{miles}(l.lat, l.lng, u.lat, u.lng) <= 2`. Never write the trigonometry yourself: a
|
|
47
|
+
great-circle expression built by hand runs to a dozen nested calls, and one bracket out of place
|
|
48
|
+
either refuses the statement or, worse, measures something else and says nothing about it.
|
|
49
|
+
|
|
39
50
|
Every type named in a `create_enum` line at the top of the schema is a Postgres enum, and the
|
|
40
51
|
values it may take are listed on that line. Compare one as text, for example
|
|
41
52
|
`WHERE status::text = 'fulfilled'`.
|
|
@@ -55,6 +66,11 @@ decrypting, and the page draws one column under the name you gave, in place of i
|
|
|
55
66
|
entry of `parts` has to be a header your query really returns. Where nothing needs joining,
|
|
56
67
|
`combine` is `[]` -- and never explain a join in `note` instead of declaring it.
|
|
57
68
|
|
|
69
|
+
Declare it the same way where the value belongs inside a sentence you are building: return the
|
|
70
|
+
text before it, the column itself, and the text after it as three columns, and join them with
|
|
71
|
+
`"separator": ""`. What you must not do is leave the parts as separate columns and let the page
|
|
72
|
+
draw them apart, which is what happens when you work around the ciphertext instead of saying so.
|
|
73
|
+
|
|
58
74
|
These columns are stored encrypted as well, and no page ever reads one back -- either the name
|
|
59
75
|
reads as a credential's, or the value is encrypted in a way no two writes of it agree on -- so
|
|
60
76
|
do not select them: %{refused}.
|
|
@@ -6,6 +6,9 @@ class Omen::Instructions
|
|
|
6
6
|
# The database function a stored timestamp is read through, created by the gem's rake task.
|
|
7
7
|
EASTERN = 'eastern'
|
|
8
8
|
|
|
9
|
+
# The database function a distance in miles is measured with, created by the same task.
|
|
10
|
+
MILES = 'miles_between'
|
|
11
|
+
|
|
9
12
|
# The one shape a reply may take: both keys required, and no others admitted.
|
|
10
13
|
ANSWER = {
|
|
11
14
|
type: 'object', additionalProperties: false, required: %w[ sql note combine ],
|
|
@@ -33,7 +36,7 @@ class Omen::Instructions
|
|
|
33
36
|
# Today's date is said out loud because "last month" is Claude's to resolve, and it has no clock.
|
|
34
37
|
# @return [String] the prose, with the schema, the subclasses and the host's notes filled in.
|
|
35
38
|
def text
|
|
36
|
-
format File.read(PROSE), today: Date.current.to_fs(:long), eastern: EASTERN,
|
|
39
|
+
format File.read(PROSE), today: Date.current.to_fs(:long), eastern: EASTERN, miles: MILES,
|
|
37
40
|
schema: schema, types: types, readable: readable, refused: refused,
|
|
38
41
|
notes: Omen.config.notes
|
|
39
42
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Omen
|
|
2
|
+
# What the role a statement runs as may and may not be. A role is created with every dangerous
|
|
3
|
+
# attribute already off, so this gem sets only what whoever created it may set, and reads the
|
|
4
|
+
# rest back rather than asserting them: saying `NOSUPERUSER` needs the SUPERUSER attribute, so
|
|
5
|
+
# a managed database refuses the very statement that would have made the role safe.
|
|
6
|
+
module Attributes
|
|
7
|
+
# Set on the role, because whoever may create a role may set these.
|
|
8
|
+
SETTABLE = 'NOLOGIN NOCREATEDB NOCREATEROLE'
|
|
9
|
+
|
|
10
|
+
# Read back instead, since saying no to one of these needs the attribute itself.
|
|
11
|
+
DANGEROUS = { rolsuper: 'SUPERUSER', rolbypassrls: 'BYPASSRLS',
|
|
12
|
+
rolreplication: 'REPLICATION', }
|
|
13
|
+
|
|
14
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
15
|
+
# @return [Boolean] whether the role is there at all to be granted anything.
|
|
16
|
+
def self.exists?(connection) = held(connection).present?
|
|
17
|
+
|
|
18
|
+
# Nil where there is no such role, so that an empty list and a missing role read apart.
|
|
19
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
20
|
+
# @return [Hash, nil] the row of what the role is, or nothing where the role is not.
|
|
21
|
+
def self.held(connection)
|
|
22
|
+
connection.select_one <<~SQL.squish
|
|
23
|
+
SELECT #{DANGEROUS.keys.join ', '} FROM pg_roles
|
|
24
|
+
WHERE rolname = #{connection.quote Omen.config.narrow_role}
|
|
25
|
+
SQL
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
29
|
+
# @return [Array<String>] what the role holds that no reading should be able to reach.
|
|
30
|
+
def self.dangerous(connection)
|
|
31
|
+
row = held(connection) || {}
|
|
32
|
+
DANGEROUS.filter_map { |column, name| name if row[column.to_s] }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Omen
|
|
2
|
+
# The database function a reading measures a distance with. Created by the rake task rather
|
|
3
|
+
# than by a migration, for the reason Omen::Eastern is: Rails' :ruby schema format dumps no
|
|
4
|
+
# functions, so db:schema:load would drop one a migration had made.
|
|
5
|
+
module Distance
|
|
6
|
+
# The earth's mean radius in miles, which is what makes the answer miles.
|
|
7
|
+
RADIUS = 3958.7613
|
|
8
|
+
|
|
9
|
+
# DDL, which Active Record has no expression for, and not a query.
|
|
10
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
11
|
+
# @return [Array<String>] the statements to run, in order.
|
|
12
|
+
def self.statements(connection) = [ haversine(connection) ]
|
|
13
|
+
|
|
14
|
+
# Declared over double precision, which numeric, real and integer all cast to implicitly,
|
|
15
|
+
# so a host's own column type does not have to be guessed at. Any argument NULL and the
|
|
16
|
+
# answer is NULL, the way a distance to nowhere ought to read.
|
|
17
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
18
|
+
# @return [String] the statement declaring the function.
|
|
19
|
+
def self.haversine(connection)
|
|
20
|
+
"CREATE OR REPLACE FUNCTION #{name connection}(lat1 double precision, " \
|
|
21
|
+
'lng1 double precision, lat2 double precision, lng2 double precision) ' \
|
|
22
|
+
"RETURNS double precision AS $$ SELECT 2 * #{RADIUS} * asin(sqrt(" \
|
|
23
|
+
'power(sin(radians(lat2 - lat1) / 2), 2) + cos(radians(lat1)) * cos(radians(lat2)) ' \
|
|
24
|
+
'* power(sin(radians(lng2 - lng1) / 2), 2))) $$ LANGUAGE sql IMMUTABLE'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
28
|
+
# @return [String] the function's name, quoted.
|
|
29
|
+
def self.name(connection) = connection.quote_table_name Omen::Instructions::MILES
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/omen/grants.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Omen
|
|
2
|
+
# Everything said to a database to make the role a statement runs as: what it may be, what it
|
|
3
|
+
# may read, and what it may not. Kept apart from the task that runs them, which is about a
|
|
4
|
+
# database it has to find and a refusal it has to survive rather than about privileges.
|
|
5
|
+
module Grants
|
|
6
|
+
# DDL, which Active Record has no expression for, and not a query.
|
|
7
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
8
|
+
# @param members [Array<String>] the roles that may SET LOCAL ROLE to this one. The owner
|
|
9
|
+
# running the task is one of them, and matters in tests, where Rails swaps the reading
|
|
10
|
+
# pool for the writing one and the test connection is the owner.
|
|
11
|
+
# @return [Array<String>] the statements to run, in order.
|
|
12
|
+
def self.statements(connection, members)
|
|
13
|
+
role = connection.quote_table_name Omen.config.narrow_role
|
|
14
|
+
[
|
|
15
|
+
'DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = ' \
|
|
16
|
+
"#{connection.quote Omen.config.narrow_role}) THEN CREATE ROLE #{role} NOLOGIN; " \
|
|
17
|
+
'END IF; END $$',
|
|
18
|
+
"ALTER ROLE #{role} WITH #{Attributes::SETTABLE}",
|
|
19
|
+
"GRANT USAGE ON SCHEMA public TO #{role}",
|
|
20
|
+
"GRANT SELECT ON ALL TABLES IN SCHEMA public TO #{role}",
|
|
21
|
+
"ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO #{role}",
|
|
22
|
+
*members.map { |member| "GRANT #{role} TO #{connection.quote_table_name member}" },
|
|
23
|
+
*revoked(connection, role),
|
|
24
|
+
*Omen::Eastern.statements(connection),
|
|
25
|
+
*Omen::Distance.statements(connection),
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Intersected, so a bare db:create with no table yet to revoke on is not a failure.
|
|
30
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
31
|
+
# @param role [String] the role to hide this feature's own tables from.
|
|
32
|
+
# @return [Array<String>] one REVOKE per table there is.
|
|
33
|
+
def self.revoked(connection, role)
|
|
34
|
+
(connection.tables & Omen.tables).map do |table|
|
|
35
|
+
"REVOKE SELECT ON #{connection.quote_table_name table} FROM #{role}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/omen/inquirer.rb
CHANGED
|
@@ -3,9 +3,6 @@ module Omen
|
|
|
3
3
|
# It needs no database.yml entry of its own: NOLOGIN, it is a privilege container reached
|
|
4
4
|
# with SET LOCAL ROLE and never connected as.
|
|
5
5
|
module Inquirer
|
|
6
|
-
# What this role may never be: a superuser bypasses GRANT outright.
|
|
7
|
-
ATTRIBUTES = 'NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS NOREPLICATION'
|
|
8
|
-
|
|
9
6
|
# Whom to ask, since the role a host reads through is one this gem has no name for.
|
|
10
7
|
WHOEVER = 'SELECT current_user'
|
|
11
8
|
|
|
@@ -13,6 +10,18 @@ module Omen
|
|
|
13
10
|
UNGRANTED = 'No %{role} connection is configured, so nothing was granted to whatever ' \
|
|
14
11
|
'reads through it. Run this again once config/database.yml names one.'
|
|
15
12
|
|
|
13
|
+
# Said where the role could not be made: a managed database never grants CREATEROLE.
|
|
14
|
+
UNMADE = 'Could not make %{role}, so every reading will say this app is misconfigured. Ask ' \
|
|
15
|
+
'for that role, NOLOGIN, granted SELECT on every table but %{tables}.'
|
|
16
|
+
|
|
17
|
+
# Said per statement, since the grants, the revocations and the functions need nothing from
|
|
18
|
+
# one another and one refusal should not discard the rest.
|
|
19
|
+
REFUSED = 'Skipped, refused by the database: %{statement} (%{error})'
|
|
20
|
+
|
|
21
|
+
# Said where a role that already existed is one a reading should not be able to reach through.
|
|
22
|
+
DANGEROUS = '%{role} holds %{held}. This gem cannot take that away without being a superuser ' \
|
|
23
|
+
'itself, so ask for it to be taken away.'
|
|
24
|
+
|
|
16
25
|
# Creates the role and the function, on every database this environment prepares.
|
|
17
26
|
# @return [void]
|
|
18
27
|
def self.grant
|
|
@@ -37,12 +46,26 @@ module Omen
|
|
|
37
46
|
read_by = reader
|
|
38
47
|
warn UNGRANTED % { role: Omen.config.reading_role } unless read_by
|
|
39
48
|
members = [ read_by, connection.select_value(WHOEVER) ].compact
|
|
40
|
-
statements(connection, members).each { |statement| connection
|
|
41
|
-
|
|
49
|
+
Grants.statements(connection, members).each { |statement| attempted connection, statement }
|
|
50
|
+
role = Omen.config.narrow_role
|
|
51
|
+
return warn UNMADE % { role: role, tables: Omen.tables.to_sentence } unless
|
|
52
|
+
Attributes.exists? connection
|
|
53
|
+
|
|
54
|
+
held = Attributes.dangerous connection
|
|
55
|
+
warn DANGEROUS % { role: role, held: held.to_sentence } if held.any?
|
|
56
|
+
puts "Granted SELECT on #{connection.current_database} to #{role}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# One statement at a time, so a database that refuses one still runs the others -- and each
|
|
60
|
+
# inside a savepoint of its own, since a refusal inside a transaction refuses everything
|
|
61
|
+
# after it too, and this task is as likely to be run from a console as from a deploy.
|
|
62
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
63
|
+
# @param statement [String] one of the statements Omen::Grants builds.
|
|
64
|
+
# @return [void]
|
|
65
|
+
def self.attempted(connection, statement)
|
|
66
|
+
connection.transaction(requires_new: true) { connection.execute statement }
|
|
42
67
|
rescue ActiveRecord::StatementInvalid => error
|
|
43
|
-
warn
|
|
44
|
-
'misconfigured. Ask for that role, NOLOGIN, granted SELECT on every table but ' \
|
|
45
|
-
"#{Omen.tables.to_sentence}: #{error.message}"
|
|
68
|
+
warn REFUSED % { statement: statement.squish, error: error.message.lines.first.strip }
|
|
46
69
|
end
|
|
47
70
|
|
|
48
71
|
# Discovered rather than named: SET LOCAL ROLE needs the connecting role to be a member of
|
|
@@ -55,37 +78,5 @@ module Omen
|
|
|
55
78
|
rescue ActiveRecord::ConnectionNotDefined, ActiveRecord::ConnectionNotEstablished
|
|
56
79
|
nil
|
|
57
80
|
end
|
|
58
|
-
|
|
59
|
-
# DDL, which Active Record has no expression for, and not a query.
|
|
60
|
-
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
61
|
-
# @param members [Array<String>] the roles that may SET LOCAL ROLE to this one. The owner
|
|
62
|
-
# running the task is one of them, and matters in tests, where Rails swaps the reading
|
|
63
|
-
# pool for the writing one and the test connection is the owner.
|
|
64
|
-
# @return [Array<String>] the statements to run, in order.
|
|
65
|
-
def self.statements(connection, members)
|
|
66
|
-
role = connection.quote_table_name Omen.config.narrow_role
|
|
67
|
-
[
|
|
68
|
-
'DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = ' \
|
|
69
|
-
"#{connection.quote Omen.config.narrow_role}) THEN CREATE ROLE #{role} NOLOGIN; " \
|
|
70
|
-
'END IF; END $$',
|
|
71
|
-
"ALTER ROLE #{role} WITH NOLOGIN #{ATTRIBUTES}",
|
|
72
|
-
"GRANT USAGE ON SCHEMA public TO #{role}",
|
|
73
|
-
"GRANT SELECT ON ALL TABLES IN SCHEMA public TO #{role}",
|
|
74
|
-
"ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO #{role}",
|
|
75
|
-
*members.map { |member| "GRANT #{role} TO #{connection.quote_table_name member}" },
|
|
76
|
-
*revoked(connection, role),
|
|
77
|
-
*Eastern.statements(connection),
|
|
78
|
-
]
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Intersected, so a bare db:create with no table yet to revoke on is not a failure.
|
|
82
|
-
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] a writing one.
|
|
83
|
-
# @param role [String] the role to hide this feature's own tables from.
|
|
84
|
-
# @return [Array<String>] one REVOKE per table there is.
|
|
85
|
-
def self.revoked(connection, role)
|
|
86
|
-
(connection.tables & Omen.tables).map do |table|
|
|
87
|
-
"REVOKE SELECT ON #{connection.quote_table_name table} FROM #{role}"
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
81
|
end
|
|
91
82
|
end
|
data/lib/omen/version.rb
CHANGED
data/lib/omen.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
require 'active_job/performs'
|
|
2
2
|
require 'anthropic'
|
|
3
3
|
|
|
4
|
+
require 'omen/attributes'
|
|
4
5
|
require 'omen/config'
|
|
6
|
+
require 'omen/distance'
|
|
5
7
|
require 'omen/eastern'
|
|
8
|
+
require 'omen/grants'
|
|
6
9
|
require 'omen/inquirer'
|
|
7
10
|
require 'omen/requirements'
|
|
8
11
|
require 'omen/version'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omen
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Claudio Baccigalupo
|
|
@@ -97,9 +97,12 @@ files:
|
|
|
97
97
|
- lib/generators/omen/install/install_generator.rb
|
|
98
98
|
- lib/generators/omen/install/templates/omen.rb
|
|
99
99
|
- lib/omen.rb
|
|
100
|
+
- lib/omen/attributes.rb
|
|
100
101
|
- lib/omen/config.rb
|
|
102
|
+
- lib/omen/distance.rb
|
|
101
103
|
- lib/omen/eastern.rb
|
|
102
104
|
- lib/omen/engine.rb
|
|
105
|
+
- lib/omen/grants.rb
|
|
103
106
|
- lib/omen/inquirer.rb
|
|
104
107
|
- lib/omen/requirements.rb
|
|
105
108
|
- lib/omen/stubs.rb
|