Files
ubicloud/routes/project/location/load_balancer.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

121 lines
3.7 KiB
Ruby

# frozen_string_literal: true
class Clover
hash_branch(:project_location_prefix, "load-balancer") do |r|
r.get api? do
load_balancer_list
end
r.on LOAD_BALANCER_NAME_OR_UBID do |lb_name, lb_id|
if lb_name
r.post api? do
check_visible_location
load_balancer_post(lb_name)
end
filter = {Sequel[:load_balancer][:name] => lb_name}
else
filter = {Sequel[:load_balancer][:id] => UBID.to_uuid(lb_id)}
end
filter[:private_subnet_id] = @project.private_subnets_dataset.where(location_id: @location.id).select(Sequel[:private_subnet][:id])
lb = LoadBalancer.first(filter)
check_found_object(lb)
r.post %w[attach-vm detach-vm] do |action|
authorize("LoadBalancer:edit", lb.id)
params = check_required_web_params(%w[vm_id])
unless (vm = Vm.from_ubid(params["vm_id"]))
fail Validation::ValidationFailed.new("vm_id" => "VM not found")
end
authorize("Vm:view", vm.id)
actioned = nil
DB.transaction do
if action == "attach-vm"
if vm.load_balancer
fail Validation::ValidationFailed.new("vm_id" => "VM is already attached to a load balancer")
end
lb.add_vm(vm)
audit_log(lb, "attach_vm", vm)
actioned = "attached to"
else
lb.detach_vm(vm)
audit_log(lb, "detach_vm", vm)
actioned = "detached from"
end
end
if api?
Serializers::LoadBalancer.serialize(lb, {detailed: true})
else
flash["notice"] = "VM is #{actioned} the load balancer"
r.redirect "#{@project.path}#{lb.path}"
end
end
r.get true do
authorize("LoadBalancer:view", lb.id)
@lb = Serializers::LoadBalancer.serialize(lb, {detailed: true, vms_serialized: !api?})
if api?
@lb
else
vms = dataset_authorize(lb.private_subnet.vms_dataset.eager(:location), "Vm:view").exclude(Sequel[:vm][:id] => lb.vms_dataset.select(Sequel[:vm][:id])).all
@attachable_vms = Serializers::Vm.serialize(vms)
view "networking/load_balancer/show"
end
end
r.delete true do
authorize("LoadBalancer:delete", lb.id)
DB.transaction do
lb.incr_destroy
audit_log(lb, "destroy")
end
204
end
r.patch api? do
authorize("LoadBalancer:edit", lb.id)
params = check_required_web_params(%w[algorithm src_port dst_port health_check_endpoint vms])
DB.transaction do
lb.update(
algorithm: params["algorithm"],
health_check_endpoint: params["health_check_endpoint"]
)
lb.ports.first.update(src_port: Validation.validate_port(:src_port, params["src_port"]),
dst_port: Validation.validate_port(:dst_port, params["dst_port"]))
end
new_vms = params["vms"].map { Vm.from_ubid(it.delete("\"")) }
new_vms.each do |vm|
unless vm
fail Validation::ValidationFailed.new("vms" => "VM not found")
end
authorize("Vm:view", vm.id)
if vm.load_balancer
next if vm.load_balancer.id == lb.id
fail Validation::ValidationFailed.new("vms" => "VM is already attached to a load balancer")
end
lb.add_vm(vm)
end
lb.vms.each do |vm|
next if new_vms.any? { it.id == vm.id }
lb.evacuate_vm(vm)
lb.remove_vm(vm)
end
lb.incr_update_load_balancer
audit_log(lb, "update")
Serializers::LoadBalancer.serialize(lb.reload, {detailed: true})
end
end
end
end