Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions app/db/utils/materialized_views.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module DB
module Utils
class MaterializedViews
def initialize(
db: Sequel::Model.db,
logger: Rails.logger
)
@db = db
@logger = logger
end

def refresh()
@logger.debug("Begin refreshing materialized views...")
refresh_all_roles
refresh_resources
@logger.debug("Finished refreshing materialized views.")
end

def refresh_all_roles()
@db.run('REFRESH MATERIALIZED VIEW all_roles_view;')
rescue Sequel::DatabaseError => e
# TODO: handle ERROR: relation "all_roles_view" does not exist
# /var/lib/ruby/lib/ruby/gems/3.0.0/gems/sequel-5.51.0/lib/sequel/adapters/postgres.rb:156:in `exec': ERROR: relation "all_roles_view" does not exist (PG::UndefinedTable)
# ...
@logger.error(
"DB::Utils::MaterializedViews.refresh_all_roles - Error #{e.message}"
)
end

def refresh_resources()
@db.run('REFRESH MATERIALIZED VIEW resources_view;')
rescue Sequel::DatabaseError => e
# TODO: handle ERROR: relation "resources_view" does not exist
# /var/lib/ruby/lib/ruby/gems/3.0.0/gems/sequel-5.51.0/lib/sequel/adapters/postgres.rb:156:in `exec': ERROR: relation "all_roles_view" does not exist (PG::UndefinedTable)
# ...
@logger.error(
"DB::Utils::MaterializedViews.refresh_resources - Error #{e.message}"
)
end
end
end
end
4 changes: 1 addition & 3 deletions app/models/loader/orchestrate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,7 @@ def update_materialized_views
# Materialized views are used to pre-compute recursive role members
# and resource attributes to save time on read. These
# must be refreshed when they underlying tables are updated.
db.run('REFRESH MATERIALIZED VIEW all_roles_view;')
db.run('REFRESH MATERIALIZED VIEW resources_view;')

::DB::Utils::MaterializedViews.new.refresh
end

def insert_policy_log_records(table)
Expand Down
4 changes: 4 additions & 0 deletions app/models/permission.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ def as_json options = {}
end
end
end

def after_create
::DB::Utils::MaterializedViews.new.refresh
end
end
4 changes: 4 additions & 0 deletions app/models/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,8 @@ def secret version: nil
def annotation name
annotations_dataset.where(name: name).select(:value).single_value
end

def after_create
::DB::Utils::MaterializedViews.new.refresh
end
end
4 changes: 4 additions & 0 deletions app/models/role.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,8 @@ def modify_credentials
yield(credentials)
credentials.save(raise_on_save_failure: true)
end

def after_create
::DB::Utils::MaterializedViews.new.refresh
end
end
6 changes: 5 additions & 1 deletion app/models/role_membership.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ def member_of role_ids
where(member_id: subset_roles.to_a)
end
end
end

def after_create
::DB::Utils::MaterializedViews.new.refresh
end
end
7 changes: 7 additions & 0 deletions cucumber/api/features/step_definitions/authz_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
grantee = Role.with_pk!(grantee)
target = Resource.with_pk!(target)
target.permit(privilege, grantee)

Sequel::Model.db << "REFRESH MATERIALIZED VIEW all_roles_view;"
Sequel::Model.db << "REFRESH MATERIALIZED VIEW resources_view;"
end

Given(/^I permit user "([^"]*)" to "([^"]*)" user "([^"]*)"$/) do |grantee, privilege, target|
Expand Down Expand Up @@ -93,6 +96,7 @@

Given(/^I add the secret value(?: "([^"]*)")? to the resource(?: "([^"]*)")?$/) do |value, resource_id|
Secret.create(resource_id: resource_id, value: value)
# TODO: refresh mat views here?
end

Given(/^I permit (user|host) "([^"]*)" to "([^"]*)" it$/) do |role_type, grantee, privilege|
Expand Down Expand Up @@ -120,4 +124,7 @@

grantee = role_type == "user" ? lookup_user(grantee) : lookup_host(grantee)
group.grant_to(grantee)

Sequel::Model.db << "REFRESH MATERIALIZED VIEW all_roles_view;"
Sequel::Model.db << "REFRESH MATERIALIZED VIEW resources_view;"
end
4 changes: 0 additions & 4 deletions spec/models/resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,13 @@
end
it "the corresponding role is listed exactly once in the owner's list of roles" do
the_membership_again
# update materialized view
Sequel::Model.db << "REFRESH MATERIALIZED VIEW all_roles_view;"
expect(the_user.all_roles.reverse_order(:role_id).all.map(&:role_id)).to eq([ the_user, the_role ].map(&:role_id))
end
end
it "the role can still be explicitly granted" do
RoleMembership.create(role: the_role, member: the_user, admin_option: true)
end
it "the corresponding role is in the owner's list of roles" do
# update materialized view
Sequel::Model.db << "REFRESH MATERIALIZED VIEW all_roles_view;"
expect(the_user.all_roles.all).to include(the_role)
end
context "changing the owner" do
Expand Down