Files
ubicloud/routes/project/token.rb
Jeremy Evans 8cada58820 Add audit logging to all route actions
Audit logging is designed to be simple to use.  You call the audit_log
method with the object affected and the action taken on it:

```ruby
audit_log(vm, "create")
```

If additional objects are affected by the same action, then you can
include them in the third argument:

```ruby
audit_log(ps, "connect", subnet)
```

This implements an audit logging check similar to the recently
added authorization check.  All successful non-GET requests to the
application are checked for audit logging, and will fail in
non-frozen tests if an audit logging method is not called.

Currently, only create/destroy actions are logged.  Audit logging
in other cases is ignored.  However, this approach was necessary
to check that no cases that should result in audit logging were
missed. It also allows for easily flipping the switch to audit log
all route actions.
2025-05-13 06:38:49 +09:00

118 lines
4.0 KiB
Ruby

# frozen_string_literal: true
class Clover
hash_branch(:project_prefix, "token") do |r|
r.web do
authorize("Project:token", @project.id)
token_ds = current_account
.api_keys_dataset
.where(project_id: @project.id)
.reverse(:created_at)
r.is do
r.get do
@tokens = token_ds.all
view "project/token"
end
r.post do
pat = nil
DB.transaction do
pat = ApiKey.create_personal_access_token(current_account, project: @project)
SubjectTag[project_id: @project.id, name: "Admin"].add_subject(pat.id)
audit_log(pat, "create")
end
flash["notice"] = "Created personal access token with id #{pat.ubid}"
r.redirect "#{@project.path}/token"
end
end
r.on String do |ubid|
@token = token = token_ds.with_pk(UBID.to_uuid(ubid))
r.delete true do
if token
DB.transaction do
token.destroy
@project.disassociate_subject(token.id)
audit_log(token, "destroy")
end
flash["notice"] = "Personal access token deleted successfully"
end
204
end
next unless token
r.post %w[unrestrict-access restrict-access] do |action|
DB.transaction do
if action == "restrict-access"
token.restrict_token_for_project(@project.id)
audit_log(token, "restrict")
flash["notice"] = "Restricted personal access token"
else
token.unrestrict_token_for_project(@project.id)
audit_log(token, "unrestrict")
flash["notice"] = "Token access is now unrestricted"
end
end
r.redirect "#{@project.path}/token/#{token.ubid}/access-control"
end
r.on "access-control" do
r.get true do
uuids = {}
aces = @project.access_control_entries_dataset.where(subject_id: token.id).all
aces.each do |ace|
uuids[ace.action_id] = nil if ace.action_id
uuids[ace.object_id] = nil if ace.object_id
end
UBID.resolve_map(uuids)
@aces = aces.map do |ace|
[ace.ubid, [uuids[ace.action_id], uuids[ace.object_id]], true]
end
sort_aces!(@aces)
@action_options = {nil => [["", "All Actions"]], **ActionTag.options_for_project(@project)}
@object_options = {nil => [["", "All Objects"]], **ObjectTag.options_for_project(@project)}
view "project/access-control"
end
r.post true do
DB.transaction do
typecast_params.array!(:Hash, "aces").each do
ubid, deleted, action_id, object_id = it.values_at("ubid", "deleted", "action", "object")
action_id = nil if action_id == ""
object_id = nil if object_id == ""
if ubid == "template"
next if deleted == "true" || (action_id.nil? && object_id.nil?)
ace = AccessControlEntry.new_with_id(project_id: @project.id, subject_id: token.id)
audit_action = "create"
else
next unless (ace = AccessControlEntry[project_id: @project.id, subject_id: token.id, id: UBID.to_uuid(ubid)])
if deleted == "true"
ace.destroy
audit_log(ace, "destroy")
next
end
audit_action = "update"
end
ace.update_from_ubids(action_id:, object_id:)
audit_log(ace, audit_action, [token, action_id, object_id])
end
end
no_audit_log # Possibly no changes
flash["notice"] = "Token access control entries saved successfully"
r.redirect "#{@project_data[:path]}/token/#{token.ubid}/access-control"
end
end
end
end
end
end