Module: CommandTower::Audit::Emit
- Defined in:
- lib/command_tower/audit/emit.rb
Class Method Summary collapse
- .assert_subject!(definition, subject) ⇒ Object
- .call(name:, subject: nil, affected_user: nil, changes: {}, metadata: {}, attribution_mode: nil, subject_label: nil, scope_class: :global, host_context: nil) ⇒ Object
- .extract_user_id(value, required:) ⇒ Object
- .normalize_host_context!(host_context, scope_class:) ⇒ Object
- .normalize_scope_class!(value) ⇒ Object
- .normalized_event_name(name) ⇒ Object
- .subject_snapshot(subject, subject_label) ⇒ Object
Class Method Details
.assert_subject!(definition, subject) ⇒ Object
42 43 44 45 46 47 |
# File 'lib/command_tower/audit/emit.rb', line 42 def assert_subject!(definition, subject) return unless definition.subject_required return unless subject.nil? raise MissingSubjectError, "audit event requires a subject" end |
.call(name:, subject: nil, affected_user: nil, changes: {}, metadata: {}, attribution_mode: nil, subject_label: nil, scope_class: :global, host_context: nil) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/command_tower/audit/emit.rb', line 11 def call(name:, subject: nil, affected_user: nil, changes: {}, metadata: {}, attribution_mode: nil, subject_label: nil, scope_class: :global, host_context: nil) registry_name = normalized_event_name(name) definition = CommandTower.config.registry.audit.fetch(registry_name) return nil unless definition.enabled? assert_subject!(definition, subject) affected_user_id = extract_user_id(affected_user, required: definition.affected_user_required) validated_changes = Payload.validate_changes!(changes || {}, allowed_keys: definition.allowed_changes) = Payload.() attribution = Attribution.resolve(affected_user_id:, attribution_mode:) snapshot = subject_snapshot(subject, subject_label) normalized_scope_class = normalize_scope_class!(scope_class) host_context_fields = normalize_host_context!(host_context, scope_class: normalized_scope_class) CommandTower::Events.publish_audit( name: registry_name, envelope: attribution.merge(snapshot).merge(host_context_fields).merge( action: registry_name, occurred_at: Time.now.utc.iso8601(6), scope_class: normalized_scope_class, changes: validated_changes, metadata: ) ) end |
.extract_user_id(value, required:) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/command_tower/audit/emit.rb', line 49 def extract_user_id(value, required:) if value.nil? raise MissingAffectedUserError, "audit event requires an affected user" if required return nil end return value if value.is_a?(Integer) return value.id if value.respond_to?(:id) raise InvalidPayloadError, "affected_user must be a user or id" end |
.normalize_host_context!(host_context, scope_class:) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/command_tower/audit/emit.rb', line 90 def normalize_host_context!(host_context, scope_class:) host_scope = CommandTower::Audit::Event::SCOPE_CLASSES[:host] if scope_class != host_scope return { host_context_type: nil, host_context_identifier: nil } end unless host_context.is_a?(Hash) raise InvalidPayloadError, "host_context is required when scope_class is host" end context_type = host_context[:type] || host_context["type"] context_identifier = host_context[:identifier] || host_context["identifier"] if context_type.to_s.strip.empty? || context_identifier.to_s.strip.empty? raise InvalidPayloadError, "host_context requires type and identifier when scope_class is host" end { host_context_type: context_type.to_s.strip, host_context_identifier: context_identifier.to_s.strip } end |
.normalize_scope_class!(value) ⇒ Object
79 80 81 82 83 84 85 86 87 88 |
# File 'lib/command_tower/audit/emit.rb', line 79 def normalize_scope_class!(value) token = value.to_sym normalized = CommandTower::Audit::Event::SCOPE_CLASSES[token] unless normalized raise InvalidPayloadError, "scope_class must be one of #{CommandTower::Audit::Event::SCOPE_CLASSES.keys.join(", ")}" end normalized end |
.normalized_event_name(name) ⇒ Object
37 38 39 40 |
# File 'lib/command_tower/audit/emit.rb', line 37 def normalized_event_name(name) CommandTower.config.registry.audit.fetch(name) name.to_s.split(".").map { |segment| CommandTower::Events.normalize_segment(segment, field: "name") }.join(".") end |
.subject_snapshot(subject, subject_label) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/command_tower/audit/emit.rb', line 62 def subject_snapshot(subject, subject_label) label = subject_label.nil? ? nil : subject_label.to_s if subject.nil? return { subject_type: nil, subject_id: nil, subject_label: label } end unless subject.respond_to?(:id) raise InvalidPayloadError, "subject must provide an id" end { subject_type: subject.class.name, subject_id: subject.id, subject_label: label } end |