command_tower 0.10.0 → 0.11.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/README.md +7 -3
- data/app/auth/command_tower/auth/auth_context.rb +10 -4
- data/app/controllers/command_tower/admin/application_controller.rb +27 -0
- data/app/controllers/command_tower/admin/audit/events_controller.rb +61 -0
- data/app/controllers/command_tower/admin/messaging/announcements_controller.rb +1 -8
- data/app/controllers/command_tower/admin/users/identities_controller.rb +77 -0
- data/app/controllers/command_tower/admin/users/impersonation_sessions_controller.rb +33 -0
- data/app/controllers/command_tower/admin/users/roles_controller.rb +40 -0
- data/app/controllers/command_tower/admin/users_controller.rb +46 -0
- data/app/controllers/command_tower/admin/workspace_controller.rb +15 -0
- data/app/controllers/command_tower/application_controller.rb +17 -1
- data/app/controllers/command_tower/auth/impersonation_session_controller.rb +27 -0
- data/app/controllers/command_tower/auth/logout_controller.rb +3 -1
- data/app/controllers/command_tower/auth/principal_capabilities_controller.rb +19 -0
- data/app/controllers/command_tower/me/audit_events_controller.rb +59 -0
- data/app/controllers/concerns/command_tower/api/application_response_renderer.rb +5 -0
- data/app/controllers/concerns/command_tower/auth/authentication_boundary.rb +3 -2
- data/app/controllers/concerns/command_tower/execution/http_boundary.rb +65 -0
- data/app/deserializers/command_tower/deserializers/admin/scope_parameter.rb +49 -0
- data/app/deserializers/command_tower/deserializers/admin/users.rb +218 -0
- data/app/deserializers/command_tower/deserializers/audit/events.rb +224 -0
- data/app/deserializers/command_tower/deserializers/clients/types.rb +27 -0
- data/app/errors/command_tower/errors/auth/admin_unavailable_during_impersonation_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/default_membership_assignment_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/impersonation_session_expired_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/impersonation_session_missing_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/nested_impersonation_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/self_impersonation_error.rb +21 -0
- data/app/errors/command_tower/errors/continuation_exhausted_error.rb +25 -0
- data/app/jobs/command_tower/application_job.rb +7 -0
- data/app/jobs/command_tower/execution/job_boundary.rb +19 -0
- data/app/mailers/command_tower/application_mailer.rb +11 -1
- data/app/mailers/command_tower/messaging/channel_mailer.rb +1 -2
- data/app/models/command_tower/audit/event.rb +38 -0
- data/app/models/command_tower/impersonation/session.rb +51 -0
- data/app/serializers/command_tower/serializers/admin/users.rb +69 -0
- data/app/serializers/command_tower/serializers/admin/workspace/manifest_serializer.rb +58 -0
- data/app/serializers/command_tower/serializers/audit/events/filter_options_serializer.rb +44 -0
- data/app/serializers/command_tower/serializers/audit/events.rb +42 -0
- data/app/serializers/command_tower/serializers/auth/principal_capabilities_serializer.rb +15 -0
- data/app/serializers/command_tower/serializers/auth/session_response_serializer.rb +26 -2
- data/app/serializers/command_tower/serializers/impersonation/session_serializer.rb +19 -0
- data/app/services/command_tower/README.md +4 -10
- data/app/services/command_tower/authorize/validate.rb +7 -4
- data/app/services/command_tower/jwt/authenticate_user.rb +8 -2
- data/app/services/command_tower/jwt/authentication_outcome.rb +5 -4
- data/app/services/command_tower/jwt/login_create.rb +7 -4
- data/app/services/command_tower/messaging/accept/operation_logger.rb +3 -3
- data/app/services/command_tower/messaging/contract/observability/correlation.rb +3 -1
- data/app/services/command_tower/messaging/contract/observability/operation_logger.rb +3 -3
- data/app/services/command_tower/messaging/contract/observability/publisher.rb +44 -0
- data/app/services/command_tower/messaging/execution/operation_logger.rb +1 -1
- data/app/services/command_tower/messaging/handoff/operation_logger.rb +1 -1
- data/app/services/command_tower/messaging/inbox/operation_logger.rb +1 -1
- data/app/services/command_tower/messaging/planner/operation_logger.rb +1 -1
- data/app/services/command_tower/messaging/recipient_readiness/evaluate.rb +1 -1
- data/app/services/command_tower/service_base.rb +30 -43
- data/app/services/command_tower/service_logging.rb +2 -36
- data/app/services/command_tower/services/account/clear_phone.rb +8 -0
- data/app/services/command_tower/services/account/phone_verification/verify.rb +1 -0
- data/app/services/command_tower/services/account/update_phone.rb +9 -1
- data/app/services/command_tower/services/admin/users.rb +377 -0
- data/app/services/command_tower/services/admin/workspace/manifest.rb +49 -0
- data/app/services/command_tower/services/application_service.rb +37 -0
- data/app/services/command_tower/services/audit/events/filter_options.rb +75 -0
- data/app/services/command_tower/services/audit/events.rb +199 -0
- data/app/services/command_tower/services/auth/assign_default_membership_role.rb +44 -0
- data/app/services/command_tower/services/auth/authenticate_session.rb +33 -6
- data/app/services/command_tower/services/auth/email_verification/verify.rb +2 -0
- data/app/services/command_tower/services/auth/password_reset/reset.rb +13 -3
- data/app/services/command_tower/services/auth/plain_text/login.rb +9 -1
- data/app/services/command_tower/services/auth/principal_capabilities/project.rb +48 -0
- data/app/services/command_tower/services/impersonation/create.rb +29 -0
- data/app/services/command_tower/services/impersonation/end.rb +28 -0
- data/app/services/command_tower/services/impersonation/record_activity.rb +30 -0
- data/app/services/command_tower/services/impersonation/terminate_open_sessions.rb +51 -0
- data/app/services/command_tower/services/me/change_password.rb +12 -0
- data/app/shared_sequences/command_tower/shared_sequences/admin/users/resolve_scoped_user.rb +42 -0
- data/app/workflows/command_tower/transactional.rb +65 -0
- data/app/workflows/command_tower/workflows/admin/messaging/create_announcement_workflow.rb +11 -0
- data/app/workflows/command_tower/workflows/admin/scope_resolution.rb +17 -0
- data/app/workflows/command_tower/workflows/admin/users/error_mapping.rb +26 -0
- data/app/workflows/command_tower/workflows/admin/users/identity_mutation.rb +30 -0
- data/app/workflows/command_tower/workflows/admin/users/list_assignable_roles_workflow.rb +30 -0
- data/app/workflows/command_tower/workflows/admin/users/list_workflow.rb +54 -0
- data/app/workflows/command_tower/workflows/admin/users/set_email_validated_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/admin/users/show_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/admin/users/update_email_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/admin/users/update_name_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/admin/users/update_roles_workflow.rb +61 -0
- data/app/workflows/command_tower/workflows/admin/users/update_username_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/admin/workspace/manifest_workflow.rb +25 -0
- data/app/workflows/command_tower/workflows/application_workflow.rb +163 -24
- data/app/workflows/command_tower/workflows/audit/error_mapping.rb +24 -0
- data/app/workflows/command_tower/workflows/audit/events/filter_options_for_admin_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/audit/events/filter_options_for_user_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/audit/events/list_for_admin_workflow.rb +74 -0
- data/app/workflows/command_tower/workflows/audit/events/list_for_user_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/audit/events/show_for_admin_workflow.rb +54 -0
- data/app/workflows/command_tower/workflows/audit/events/show_for_user_workflow.rb +42 -0
- data/app/workflows/command_tower/workflows/auth/authenticate_request_workflow.rb +3 -2
- data/app/workflows/command_tower/workflows/auth/authentication_response_effects.rb +5 -1
- data/app/workflows/command_tower/workflows/auth/logout_workflow.rb +41 -1
- data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +2 -0
- data/app/workflows/command_tower/workflows/auth/principal_capabilities/show_workflow.rb +27 -0
- data/app/workflows/command_tower/workflows/auth/register_workflow.rb +56 -24
- data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +15 -1
- data/app/workflows/command_tower/workflows/auth/session_error_status.rb +1 -0
- data/app/workflows/command_tower/workflows/impersonation/error_mapping.rb +27 -0
- data/app/workflows/command_tower/workflows/impersonation/start_workflow.rb +97 -0
- data/app/workflows/command_tower/workflows/impersonation/stop_workflow.rb +66 -0
- data/app/workflows/command_tower/workflows/me/update_name_workflow.rb +1 -0
- data/app/workflows/command_tower/workflows/profile/show_workflow.rb +1 -0
- data/app/workflows/command_tower/workflows/workflow_result.rb +31 -7
- data/config/routes.rb +20 -0
- data/db/migrate/20260816000001_create_command_tower_audit_events.rb +45 -0
- data/db/migrate/20260817000001_add_scope_columns_to_command_tower_audit_events.rb +34 -0
- data/db/migrate/20260817000003_create_command_tower_impersonation_sessions.rb +26 -0
- data/docs/admin_workspace.md +158 -0
- data/docs/api_reference.md +175 -14
- data/docs/architecture.md +5 -0
- data/docs/audit.md +195 -0
- data/docs/authentication.md +14 -0
- data/docs/authentication_authorization_guide.md +15 -11
- data/docs/authorization.md +25 -4
- data/docs/controllers.md +7 -1
- data/docs/eventing.md +179 -0
- data/docs/extending.md +32 -1
- data/docs/host_integration_guide.md +103 -12
- data/docs/initializing.md +2 -2
- data/docs/messaging_integration_guide.md +1 -1
- data/docs/models.md +4 -0
- data/docs/pagination.md +2 -2
- data/docs/principal_capabilities.md +89 -0
- data/docs/upgrades/0.10.0.md +2 -2
- data/docs/upgrades/0.11.0.md +92 -0
- data/docs/upgrades/0.11.1.md +25 -0
- data/docs/upgrades/README.md +2 -0
- data/lib/command_tower/admin_scope/apply_audit_scoping.rb +45 -0
- data/lib/command_tower/admin_scope/apply_users_narrowing.rb +21 -0
- data/lib/command_tower/admin_scope/manifest_projection.rb +102 -0
- data/lib/command_tower/admin_scope/resolve.rb +31 -0
- data/lib/command_tower/admin_scope/scope_context.rb +7 -0
- data/lib/command_tower/admin_scope/scope_option.rb +7 -0
- data/lib/command_tower/admin_scope.rb +20 -0
- data/lib/command_tower/admin_workspace.rb +16 -0
- data/lib/command_tower/audit/attribution.rb +90 -0
- data/lib/command_tower/audit/emit.rb +113 -0
- data/lib/command_tower/audit/masking.rb +41 -0
- data/lib/command_tower/audit/payload.rb +128 -0
- data/lib/command_tower/audit/persistence/subscriber.rb +85 -0
- data/lib/command_tower/audit.rb +27 -0
- data/lib/command_tower/authorization/assignable_roles.rb +36 -0
- data/lib/command_tower/authorization/default.yml +99 -5
- data/lib/command_tower/authorization/effective_entity_grants.rb +55 -0
- data/lib/command_tower/authorization/entity.rb +15 -5
- data/lib/command_tower/authorization/role.rb +5 -4
- data/lib/command_tower/authorization.rb +71 -10
- data/lib/command_tower/configuration/admin_scope/config.rb +104 -0
- data/lib/command_tower/configuration/admin_scope/tool_registration.rb +28 -0
- data/lib/command_tower/configuration/authorization/config.rb +6 -0
- data/lib/command_tower/configuration/config.rb +20 -0
- data/lib/command_tower/configuration/impersonation/config.rb +38 -0
- data/lib/command_tower/configuration/registry/admin_workspace/config.rb +199 -0
- data/lib/command_tower/configuration/registry/admin_workspace/tool_definition.rb +150 -0
- data/lib/command_tower/configuration/registry/audit/config.rb +403 -0
- data/lib/command_tower/configuration/registry/audit/event_definition.rb +155 -0
- data/lib/command_tower/configuration/registry/config.rb +34 -0
- data/lib/command_tower/configuration/registry/principal_capabilities/capability_definition.rb +43 -0
- data/lib/command_tower/configuration/registry/principal_capabilities/config.rb +136 -0
- data/lib/command_tower/current.rb +12 -0
- data/lib/command_tower/engine.rb +19 -4
- data/lib/command_tower/events.rb +184 -0
- data/lib/command_tower/execution.rb +111 -0
- data/lib/command_tower/impersonation/activity_declaration.rb +23 -0
- data/lib/command_tower/impersonation/apply_overlay.rb +88 -0
- data/lib/command_tower/impersonation/clear_overlay_for_audit.rb +23 -0
- data/lib/command_tower/impersonation/establish_identity.rb +44 -0
- data/lib/command_tower/install/baseline.rb +3 -0
- data/lib/command_tower/logging/lifecycle_declaration.rb +27 -0
- data/lib/command_tower/logging/projection.rb +67 -0
- data/lib/command_tower/logging/subscriber.rb +103 -0
- data/lib/command_tower/principal_capabilities.rb +15 -0
- data/lib/command_tower/version.rb +1 -1
- data/lib/command_tower.rb +12 -0
- data/spec/factories/impersonation_session.rb +17 -0
- data/spec/factories/user.rb +16 -0
- metadata +114 -3
- data/app/services/command_tower/messaging/contract/observability/structured_logger.rb +0 -56
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CommandTower
|
|
4
|
+
module Impersonation
|
|
5
|
+
# Temporarily clear overlay identity so impersonation_ended audits attribute
|
|
6
|
+
# as admin_direct to the actor. Lives outside workflows/services so leaf
|
|
7
|
+
# layers do not establish Current execution boundaries themselves.
|
|
8
|
+
module ClearOverlayForAudit
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def call(actor_user_id:)
|
|
12
|
+
CommandTower::Current.set(
|
|
13
|
+
user_id: actor_user_id,
|
|
14
|
+
effective_user_id: actor_user_id,
|
|
15
|
+
originating_administrator_id: nil,
|
|
16
|
+
impersonation_active: false
|
|
17
|
+
) do
|
|
18
|
+
yield
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CommandTower
|
|
4
|
+
module Impersonation
|
|
5
|
+
EstablishedIdentity = Data.define(:user, :actor, :session, :status) do
|
|
6
|
+
def expired?
|
|
7
|
+
status == :expired
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def active?
|
|
11
|
+
status == :active
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module EstablishIdentity
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def call(actor:, impersonation_session_id:, mode: :enforce)
|
|
19
|
+
overlay = ApplyOverlay.call(actor:, session_id: impersonation_session_id, mode:)
|
|
20
|
+
if overlay.status == :expired && mode.to_sym == :enforce
|
|
21
|
+
CommandTower::Execution::IdentityEnrichment.from_user(actor)
|
|
22
|
+
return EstablishedIdentity.new(user: actor, actor:, session: overlay.session, status: :expired)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if overlay.status == :active
|
|
26
|
+
CommandTower::Execution::IdentityEnrichment.from_impersonation_session(
|
|
27
|
+
overlay.session,
|
|
28
|
+
actor: overlay.actor,
|
|
29
|
+
target: overlay.target
|
|
30
|
+
)
|
|
31
|
+
return EstablishedIdentity.new(
|
|
32
|
+
user: overlay.target,
|
|
33
|
+
actor: overlay.actor,
|
|
34
|
+
session: overlay.session,
|
|
35
|
+
status: :active
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
CommandTower::Execution::IdentityEnrichment.from_user(actor)
|
|
40
|
+
EstablishedIdentity.new(user: actor, actor:, session: overlay.session, status: overlay.status)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -11,6 +11,9 @@ module CommandTower
|
|
|
11
11
|
20260805000003_create_command_tower_messaging_core.rb
|
|
12
12
|
20260805000004_create_messaging_notification_preferences.rb
|
|
13
13
|
20260805000005_create_messaging_endpoints_and_pushover_credentials.rb
|
|
14
|
+
20260816000001_create_command_tower_audit_events.rb
|
|
15
|
+
20260817000001_add_scope_columns_to_command_tower_audit_events.rb
|
|
16
|
+
20260817000003_create_command_tower_impersonation_sessions.rb
|
|
14
17
|
].freeze
|
|
15
18
|
|
|
16
19
|
module_function
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CommandTower
|
|
4
|
+
module Logging
|
|
5
|
+
module LifecycleDeclaration
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend ClassMethods
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def log_lifecycle!
|
|
12
|
+
@lifecycle_loggable = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def disable_lifecycle_logging!
|
|
16
|
+
@lifecycle_loggable = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def lifecycle_loggable?
|
|
20
|
+
return @lifecycle_loggable unless @lifecycle_loggable.nil?
|
|
21
|
+
|
|
22
|
+
superclass.respond_to?(:lifecycle_loggable?) ? superclass.lifecycle_loggable? : false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CommandTower
|
|
4
|
+
module Logging
|
|
5
|
+
module Projection
|
|
6
|
+
DROP = %i[event_uuid layer log_lifecycle log_level].freeze
|
|
7
|
+
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def call(event)
|
|
11
|
+
payload = symbolize(event.payload)
|
|
12
|
+
projected = { event: event.name }
|
|
13
|
+
|
|
14
|
+
copy_if_present(projected, payload, :subject)
|
|
15
|
+
copy_if_present(projected, payload, :execution_uuid)
|
|
16
|
+
copy_if_present(projected, payload, :correlation_id)
|
|
17
|
+
copy_if_present(projected, payload, :outcome)
|
|
18
|
+
copy_if_present(projected, payload, :duration_ms)
|
|
19
|
+
copy_if_present(projected, payload, :user_id)
|
|
20
|
+
copy_if_present(projected, payload, :error_class)
|
|
21
|
+
copy_if_present(projected, payload, :error_codes)
|
|
22
|
+
copy_if_present(projected, payload, :causation_id)
|
|
23
|
+
copy_if_present(projected, payload, :originating_administrator_id)
|
|
24
|
+
|
|
25
|
+
request_id = payload[:request_id]
|
|
26
|
+
if !request_id.nil? && request_id != payload[:correlation_id]
|
|
27
|
+
projected[:request_id] = request_id
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
source = payload[:source]
|
|
31
|
+
projected[:source] = source if !source.nil? && source.to_s != "http"
|
|
32
|
+
|
|
33
|
+
effective = payload[:effective_user_id]
|
|
34
|
+
projected[:effective_user_id] = effective if !effective.nil? && effective != payload[:user_id]
|
|
35
|
+
|
|
36
|
+
projected[:impersonation_active] = true if payload[:impersonation_active] == true
|
|
37
|
+
|
|
38
|
+
payload.each do |key, value|
|
|
39
|
+
next if DROP.include?(key)
|
|
40
|
+
next if projected.key?(key)
|
|
41
|
+
next if value.nil?
|
|
42
|
+
next if key == :impersonation_active
|
|
43
|
+
next if key == :source
|
|
44
|
+
next if key == :request_id
|
|
45
|
+
next if key == :effective_user_id
|
|
46
|
+
|
|
47
|
+
projected[key] = value
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
projected
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def symbolize(payload)
|
|
54
|
+
hash = payload.respond_to?(:to_h) ? payload.to_h : {}
|
|
55
|
+
hash.each_with_object({}) do |(key, value), memo|
|
|
56
|
+
memo[key.to_sym] = value
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def copy_if_present(projected, payload, key)
|
|
61
|
+
value = payload[key]
|
|
62
|
+
projected[key] = value unless value.nil?
|
|
63
|
+
end
|
|
64
|
+
private_class_method :symbolize, :copy_if_present
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CommandTower
|
|
4
|
+
module Logging
|
|
5
|
+
class Subscriber < ActiveSupport::LogSubscriber
|
|
6
|
+
PATTERNS = [
|
|
7
|
+
/\Acommand_tower\.lifecycle(?:\.|\z)/,
|
|
8
|
+
/\Acommand_tower\.log(?:\.|\z)/,
|
|
9
|
+
/\Acommand_tower\.messaging(?:\.|\z)/
|
|
10
|
+
].freeze
|
|
11
|
+
SEVERITIES = %i[debug info warn error].freeze
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def logger
|
|
15
|
+
@logger || super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def logger=(value)
|
|
19
|
+
@logger = value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def attach!
|
|
23
|
+
detach!
|
|
24
|
+
subscriber = new
|
|
25
|
+
@subscriptions = PATTERNS.map do |pattern|
|
|
26
|
+
ActiveSupport::Notifications.subscribe(pattern) do |name, started, finished, id, payload|
|
|
27
|
+
event = ActiveSupport::Notifications::Event.new(name, started, finished, id, payload)
|
|
28
|
+
subscriber.call(event)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def detach!
|
|
34
|
+
Array(@subscriptions).each { |subscription| ActiveSupport::Notifications.unsubscribe(subscription) }
|
|
35
|
+
@subscriptions = []
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def subscriptions
|
|
39
|
+
Array(@subscriptions)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def call(event)
|
|
44
|
+
return if logger.nil?
|
|
45
|
+
return unless materialize?(event)
|
|
46
|
+
|
|
47
|
+
severity = severity_for(event)
|
|
48
|
+
logger.public_send(severity, log_entry(event))
|
|
49
|
+
rescue StandardError
|
|
50
|
+
begin
|
|
51
|
+
logger&.error({ event: "command_tower.logging.subscriber_failed", error_class: $!.class.name })
|
|
52
|
+
rescue StandardError
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def logger
|
|
58
|
+
self.class.logger
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def materialize?(event)
|
|
64
|
+
name = event.name
|
|
65
|
+
return true if name.start_with?("command_tower.log.")
|
|
66
|
+
return true if name.start_with?("command_tower.messaging.")
|
|
67
|
+
return false unless name.start_with?("command_tower.lifecycle.")
|
|
68
|
+
return false if name.end_with?(".started")
|
|
69
|
+
|
|
70
|
+
outcome = (event.payload[:outcome] || event.payload["outcome"]).to_s
|
|
71
|
+
return true if %w[failure error].include?(outcome)
|
|
72
|
+
|
|
73
|
+
event.payload[:log_lifecycle] == true || event.payload["log_lifecycle"] == true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def log_entry(event)
|
|
77
|
+
CommandTower::Logging::Projection.call(event)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def severity_for(event)
|
|
81
|
+
name = event.name
|
|
82
|
+
if name.start_with?("command_tower.log.")
|
|
83
|
+
return named_severity(name.split(".").last)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
payload_level = event.payload[:log_level] || event.payload["log_level"]
|
|
87
|
+
return named_severity(payload_level) if payload_level
|
|
88
|
+
|
|
89
|
+
return :debug if name.end_with?(".started")
|
|
90
|
+
|
|
91
|
+
outcome = event.payload[:outcome] || event.payload["outcome"]
|
|
92
|
+
return :error if outcome.to_s == "error"
|
|
93
|
+
|
|
94
|
+
:info
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def named_severity(value)
|
|
98
|
+
level = value.to_sym
|
|
99
|
+
SEVERITIES.include?(level) ? level : :info
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CommandTower
|
|
4
|
+
module PrincipalCapabilities
|
|
5
|
+
class Error < CommandTower::Error; end
|
|
6
|
+
|
|
7
|
+
class UnregisteredCapabilityError < Error; end
|
|
8
|
+
class DuplicateCapabilityError < Error; end
|
|
9
|
+
class HostOverrideError < Error; end
|
|
10
|
+
class InvalidCapabilityNameError < Error; end
|
|
11
|
+
class InvalidCapabilityDefinitionError < Error; end
|
|
12
|
+
class MissingRequiredEntityError < Error; end
|
|
13
|
+
class FrozenRegistryError < Error; end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/command_tower.rb
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
require "command_tower/error"
|
|
2
2
|
require "command_tower/current"
|
|
3
|
+
require "command_tower/execution"
|
|
4
|
+
require "command_tower/events"
|
|
5
|
+
require "command_tower/audit"
|
|
6
|
+
require "command_tower/impersonation/activity_declaration"
|
|
7
|
+
require "command_tower/impersonation/apply_overlay"
|
|
8
|
+
require "command_tower/impersonation/clear_overlay_for_audit"
|
|
9
|
+
require "command_tower/impersonation/establish_identity"
|
|
10
|
+
require "command_tower/admin_workspace"
|
|
11
|
+
require "command_tower/admin_scope"
|
|
12
|
+
require "command_tower/logging/lifecycle_declaration"
|
|
13
|
+
require "command_tower/logging/projection"
|
|
14
|
+
require "command_tower/logging/subscriber"
|
|
3
15
|
|
|
4
16
|
require "command_tower/version"
|
|
5
17
|
require "command_tower/engine"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
FactoryBot.define do
|
|
4
|
+
factory :impersonation_session, class: "CommandTower::Impersonation::Session" do
|
|
5
|
+
association :actor, factory: :user
|
|
6
|
+
association :target, factory: :user
|
|
7
|
+
started_at { Time.current }
|
|
8
|
+
last_activity_at { Time.current }
|
|
9
|
+
idle_expires_at { 10.minutes.from_now }
|
|
10
|
+
absolute_expires_at { 1.hour.from_now }
|
|
11
|
+
|
|
12
|
+
trait :ended do
|
|
13
|
+
ended_at { Time.current }
|
|
14
|
+
end_reason { "manual" }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/spec/factories/user.rb
CHANGED
|
@@ -40,6 +40,22 @@ FactoryBot.define do
|
|
|
40
40
|
roles { ["owner"] }
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
trait :role_impersonation_operator do
|
|
44
|
+
roles { ["impersonation_operator"] }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
trait :role_rbac_admin do
|
|
48
|
+
roles { ["rbac_admin"] }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
trait :role_support_admin do
|
|
52
|
+
roles { ["support_admin"] }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
trait :role_users_identity_admin do
|
|
56
|
+
roles { ["users_identity_admin"] }
|
|
57
|
+
end
|
|
58
|
+
|
|
43
59
|
trait :without_phone do
|
|
44
60
|
phone_number { nil }
|
|
45
61
|
phone_number_validated { false }
|