command_tower 0.4.0 → 0.10.0
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 +102 -29
- data/app/auth/command_tower/auth/auth_context.rb +36 -0
- data/app/auth/command_tower/auth/password_recovery_session_context.rb +11 -0
- data/app/auth/command_tower/auth/signup_session_context.rb +11 -0
- data/app/controllers/command_tower/admin/messaging/announcements_controller.rb +34 -0
- data/app/controllers/command_tower/application_controller.rb +80 -14
- data/app/controllers/command_tower/auth/email/availability_controller.rb +35 -0
- data/app/controllers/command_tower/auth/email_verification/send_controller.rb +24 -0
- data/app/controllers/command_tower/auth/email_verification/verify_controller.rb +41 -0
- data/app/controllers/command_tower/auth/identity_policy_controller.rb +14 -0
- data/app/controllers/command_tower/auth/logout_controller.rb +14 -0
- data/app/controllers/command_tower/auth/password_recovery_session/create_controller.rb +17 -0
- data/app/controllers/command_tower/auth/password_reset/reset_controller.rb +35 -0
- data/app/controllers/command_tower/auth/password_reset/send_controller.rb +40 -0
- data/app/controllers/command_tower/auth/password_reset/validate_controller.rb +35 -0
- data/app/controllers/command_tower/auth/plain_text/login_controller.rb +22 -0
- data/app/controllers/command_tower/auth/register_controller.rb +33 -0
- data/app/controllers/command_tower/auth/session_controller.rb +21 -0
- data/app/controllers/command_tower/auth/signup_session/create_controller.rb +17 -0
- data/app/controllers/command_tower/auth/username/availability_controller.rb +35 -0
- data/app/controllers/command_tower/me/inbox_controller.rb +85 -0
- data/app/controllers/command_tower/me/name_controller.rb +37 -0
- data/app/controllers/command_tower/me/password_controller.rb +37 -0
- data/app/controllers/command_tower/me/phone_controller.rb +44 -0
- data/app/controllers/command_tower/me/phone_verification/verifications_controller.rb +23 -0
- data/app/controllers/command_tower/me/phone_verification/verify_controller.rb +34 -0
- data/app/controllers/command_tower/me/preferences_controller.rb +46 -0
- data/app/controllers/command_tower/me/pushover/verifications_controller.rb +23 -0
- data/app/controllers/command_tower/me/pushover_controller.rb +66 -0
- data/app/controllers/command_tower/me_controller.rb +19 -0
- data/app/controllers/command_tower/profile_controller.rb +19 -0
- data/app/controllers/concerns/command_tower/api/application_response_renderer.rb +82 -0
- data/app/controllers/concerns/command_tower/api/deserializer_failure_details.rb +22 -0
- data/app/controllers/concerns/command_tower/auth/authentication_boundary.rb +34 -0
- data/app/controllers/concerns/command_tower/auth/authorization_boundary.rb +26 -0
- data/app/controllers/concerns/command_tower/auth/invalid_credentials_deserializer_renderer.rb +23 -0
- data/app/controllers/concerns/command_tower/auth/password_recovery_session_boundary.rb +29 -0
- data/app/controllers/concerns/command_tower/auth/signup_session_boundary.rb +29 -0
- data/app/deserializers/command_tower/deserializers/admin/messaging/create_announcement_deserializer.rb +106 -0
- data/app/deserializers/command_tower/deserializers/application_deserializer.rb +148 -0
- data/app/deserializers/command_tower/deserializers/auth/email_availability_deserializer.rb +19 -0
- data/app/deserializers/command_tower/deserializers/auth/email_verification/verify_deserializer.rb +21 -0
- data/app/deserializers/command_tower/deserializers/auth/password_reset/reset_deserializer.rb +31 -0
- data/app/deserializers/command_tower/deserializers/auth/password_reset/send_deserializer.rb +21 -0
- data/app/deserializers/command_tower/deserializers/auth/password_reset/validate_deserializer.rb +23 -0
- data/app/deserializers/command_tower/deserializers/auth/plain_text/login_deserializer.rb +24 -0
- data/app/deserializers/command_tower/deserializers/auth/register_deserializer.rb +42 -0
- data/app/deserializers/command_tower/deserializers/auth/username_availability_deserializer.rb +19 -0
- data/app/deserializers/command_tower/deserializers/clients/errors.rb +45 -0
- data/app/deserializers/command_tower/deserializers/clients/missing.rb +10 -0
- data/app/deserializers/command_tower/deserializers/clients/payload.rb +21 -0
- data/app/deserializers/command_tower/deserializers/clients/result.rb +122 -0
- data/app/deserializers/command_tower/deserializers/clients/types.rb +192 -0
- data/app/deserializers/command_tower/deserializers/coercion_result.rb +35 -0
- data/app/deserializers/command_tower/deserializers/failure.rb +14 -0
- data/app/deserializers/command_tower/deserializers/me/change_password_deserializer.rb +41 -0
- data/app/deserializers/command_tower/deserializers/me/phone_verification/verify_deserializer.rb +29 -0
- data/app/deserializers/command_tower/deserializers/me/pushover/credentials_deserializer.rb +31 -0
- data/app/deserializers/command_tower/deserializers/me/update_name_deserializer.rb +22 -0
- data/app/deserializers/command_tower/deserializers/me/update_phone_deserializer.rb +22 -0
- data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +77 -0
- data/app/deserializers/command_tower/deserializers/messaging/preferences/show_deserializer.rb +17 -0
- data/app/deserializers/command_tower/deserializers/messaging/preferences/update_deserializer.rb +97 -0
- data/app/errors/command_tower/errors/account/phone_already_verified_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_missing_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_code_invalid_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_expired_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_send_failed_error.rb +25 -0
- data/app/errors/command_tower/errors/account/phone_verification_stale_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_throttled_error.rb +24 -0
- data/app/errors/command_tower/errors/account/pushover_already_configured_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_capability_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_not_configured_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_provider_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_verification_failed_error.rb +27 -0
- data/app/errors/command_tower/errors/account/sms_capability_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/application_error.rb +27 -0
- data/app/errors/command_tower/errors/auth/csrf_mismatch_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/csrf_missing_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/email_already_registered_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/email_verification_required_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/invalid_credentials_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_recovery_ip_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_expired_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_invalid_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_missing_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/password_reset_invalid_token_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_reset_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/signup_ip_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/signup_session_expired_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/signup_session_invalid_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/signup_session_missing_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/signup_session_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/verification_code_invalid_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/verification_send_failed_error.rb +25 -0
- data/app/errors/command_tower/errors/forbidden_error.rb +19 -0
- data/app/errors/command_tower/errors/internal_error.rb +19 -0
- data/app/errors/command_tower/errors/messaging/accept_rejected_error.rb +21 -0
- data/app/errors/command_tower/errors/messaging/idempotency_conflict_error.rb +21 -0
- data/app/errors/command_tower/errors/messaging/recipient_unresolved_error.rb +21 -0
- data/app/errors/command_tower/errors/not_found_error.rb +19 -0
- data/app/errors/command_tower/errors/unauthorized_error.rb +19 -0
- data/app/errors/command_tower/errors/validation_error.rb +15 -0
- data/app/jobs/command_tower/messaging/channel_delivery_execution_job.rb +18 -0
- data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +24 -0
- data/app/jobs/command_tower/messaging/execution_recovery_job.rb +13 -0
- data/app/jobs/command_tower/messaging/handoff_job.rb +15 -0
- data/app/jobs/command_tower/messaging/handoff_recovery_job.rb +13 -0
- data/app/mailers/command_tower/email_verification_mailer.rb +5 -3
- data/app/mailers/command_tower/messaging/channel_mailer.rb +25 -0
- data/app/mailers/command_tower/password_reset_mailer.rb +18 -0
- data/app/models/command_tower/messaging/channel_delivery.rb +45 -0
- data/app/models/command_tower/messaging/communication.rb +60 -0
- data/app/models/command_tower/messaging/delivery_attempt.rb +28 -0
- data/app/models/command_tower/messaging/destination_plan.rb +17 -0
- data/app/models/command_tower/messaging/endpoint.rb +98 -0
- data/app/models/command_tower/messaging/endpoint_pushover_credential.rb +18 -0
- data/app/models/command_tower/messaging/inbox_item.rb +52 -0
- data/app/models/command_tower/messaging/notification_preference.rb +21 -0
- data/app/models/user.rb +4 -1
- data/app/serializers/command_tower/serializers/admin/messaging/announcement_response_serializer.rb +41 -0
- data/app/serializers/command_tower/serializers/application/envelope_serializer.rb +25 -0
- data/app/serializers/command_tower/serializers/application/error_entry_serializer.rb +18 -0
- data/app/serializers/command_tower/serializers/application_serializer.rb +26 -0
- data/app/serializers/command_tower/serializers/auth/email_availability_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/auth/email_verification/message_response_serializer.rb +15 -0
- data/app/serializers/command_tower/serializers/auth/identity_policy_serializer.rb +36 -0
- data/app/serializers/command_tower/serializers/auth/login_response_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/auth/logout_response_serializer.rb +13 -0
- data/app/serializers/command_tower/serializers/auth/password_recovery_session_response_serializer.rb +16 -0
- data/app/serializers/command_tower/serializers/auth/password_reset/message_response_serializer.rb +15 -0
- data/app/serializers/command_tower/serializers/auth/password_reset/validate_response_serializer.rb +22 -0
- data/app/serializers/command_tower/serializers/auth/register_response_serializer.rb +16 -0
- data/app/serializers/command_tower/serializers/auth/session_response_serializer.rb +16 -0
- data/app/serializers/command_tower/serializers/auth/signup_session_response_serializer.rb +16 -0
- data/app/serializers/command_tower/serializers/auth/user_serializer.rb +21 -0
- data/app/serializers/command_tower/serializers/auth/username_availability_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/me/account_serializer.rb +45 -0
- data/app/serializers/command_tower/serializers/me/change_password_response_serializer.rb +13 -0
- data/app/serializers/command_tower/serializers/me/pushover_serializer.rb +59 -0
- data/app/serializers/command_tower/serializers/messaging/inbox.rb +45 -0
- data/app/serializers/command_tower/serializers/messaging/preferences/catalog_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/messaging/preferences/category_serializer.rb +23 -0
- data/app/serializers/command_tower/serializers/messaging/preferences/notification_serializer.rb +30 -0
- data/app/serializers/command_tower/serializers/profile/profile_serializer.rb +13 -0
- data/app/services/command_tower/README.md +22 -6
- data/app/services/command_tower/auth/request_context.rb +21 -0
- data/app/services/command_tower/authorize/validate.rb +54 -18
- data/app/services/command_tower/clients/client_base.rb +183 -0
- data/app/services/command_tower/clients/client_result.rb +45 -0
- data/app/services/command_tower/clients/const_resolution.rb +32 -0
- data/app/services/command_tower/clients/decoded_response.rb +16 -0
- data/app/services/command_tower/clients/endpoint_base.rb +242 -0
- data/app/services/command_tower/clients/endpoint_input.rb +18 -0
- data/app/services/command_tower/clients/errors/authentication_error.rb +26 -0
- data/app/services/command_tower/clients/errors/configuration_error.rb +10 -0
- data/app/services/command_tower/clients/errors/deserialization_error.rb +26 -0
- data/app/services/command_tower/clients/errors/discovery_error.rb +10 -0
- data/app/services/command_tower/clients/errors/upstream_error.rb +26 -0
- data/app/services/command_tower/clients/invocation.rb +61 -0
- data/app/services/command_tower/clients/namespace_proxy.rb +75 -0
- data/app/services/command_tower/clients/scoped_client.rb +88 -0
- data/app/services/command_tower/clients/serialized_request.rb +17 -0
- data/app/services/command_tower/clients/transport/error.rb +18 -0
- data/app/services/command_tower/clients/transport/faraday_adapter.rb +129 -0
- data/app/services/command_tower/clients/transport/faraday_config.rb +45 -0
- data/app/services/command_tower/clients/transport/request.rb +50 -0
- data/app/services/command_tower/clients/transport/response.rb +22 -0
- data/app/services/command_tower/clients/url.rb +56 -0
- data/app/services/command_tower/identity/phone/normalizer.rb +41 -0
- data/app/services/command_tower/identity/phone_verification/sms_configuration.rb +58 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/fake_adapter.rb +35 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/log_adapter.rb +34 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/twilio_adapter.rb +60 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport.rb +42 -0
- data/app/services/command_tower/jwt/authenticate_user.rb +61 -34
- data/app/services/command_tower/jwt/authentication_outcome.rb +36 -0
- data/app/services/command_tower/jwt/decode.rb +27 -8
- data/app/services/command_tower/jwt/encode.rb +4 -6
- data/app/services/command_tower/jwt/login_create.rb +6 -7
- data/app/services/command_tower/login_strategy/plain_text/email_verification/required.rb +11 -9
- data/app/services/command_tower/messaging/accept/channel_delivery_result.rb +13 -0
- data/app/services/command_tower/messaging/accept/coordinator.rb +166 -0
- data/app/services/command_tower/messaging/accept/error.rb +9 -0
- data/app/services/command_tower/messaging/accept/excluded_destination.rb +24 -0
- data/app/services/command_tower/messaging/accept/handoff_scheduler.rb +40 -0
- data/app/services/command_tower/messaging/accept/idempotency_conflict_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/illegal_override_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/impossible_mandatory_plan_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/invalid_preference_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/invariant_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/operation_logger.rb +97 -0
- data/app/services/command_tower/messaging/accept/persistence_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/persister.rb +90 -0
- data/app/services/command_tower/messaging/accept/request_normalizer.rb +100 -0
- data/app/services/command_tower/messaging/accept/result.rb +99 -0
- data/app/services/command_tower/messaging/accept/unknown_type_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/channel_detectors.rb +52 -0
- data/app/services/command_tower/messaging/channels/definition.rb +24 -0
- data/app/services/command_tower/messaging/channels/error.rb +9 -0
- data/app/services/command_tower/messaging/channels/unknown_channel_error.rb +9 -0
- data/app/services/command_tower/messaging/channels.rb +85 -0
- data/app/services/command_tower/messaging/contract/communications.rb +29 -0
- data/app/services/command_tower/messaging/contract/error.rb +9 -0
- data/app/services/command_tower/messaging/contract/internal/finder.rb +43 -0
- data/app/services/command_tower/messaging/contract/invariant_error.rb +9 -0
- data/app/services/command_tower/messaging/contract/mappers/channel_delivery_mapper.rb +53 -0
- data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +61 -0
- data/app/services/command_tower/messaging/contract/mappers/delivery_attempt_mapper.rb +27 -0
- data/app/services/command_tower/messaging/contract/mappers/destination_plan_mapper.rb +17 -0
- data/app/services/command_tower/messaging/contract/mappers/inbox_item_mapper.rb +17 -0
- data/app/services/command_tower/messaging/contract/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/contract/observability/correlation.rb +17 -0
- data/app/services/command_tower/messaging/contract/observability/operation_logger.rb +101 -0
- data/app/services/command_tower/messaging/contract/observability/structured_logger.rb +56 -0
- data/app/services/command_tower/messaging/contract/requests/find_communication.rb +21 -0
- data/app/services/command_tower/messaging/contract/results/channel_delivery_result.rb +25 -0
- data/app/services/command_tower/messaging/contract/results/communication_result.rb +22 -0
- data/app/services/command_tower/messaging/contract/results/delivery_attempt_result.rb +21 -0
- data/app/services/command_tower/messaging/contract/results/destination_plan_result.rb +11 -0
- data/app/services/command_tower/messaging/contract/results/inbox_item_result.rb +11 -0
- data/app/services/command_tower/messaging/contract/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/delivery_retry_policy.rb +30 -0
- data/app/services/command_tower/messaging/endpoints/begin_verification.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/channel_gate.rb +39 -0
- data/app/services/command_tower/messaging/endpoints/conflict_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/create.rb +181 -0
- data/app/services/command_tower/messaging/endpoints/error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/fingerprinter.rb +50 -0
- data/app/services/command_tower/messaging/endpoints/invalid_transition_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/lifecycle.rb +37 -0
- data/app/services/command_tower/messaging/endpoints/mark_invalid.rb +32 -0
- data/app/services/command_tower/messaging/endpoints/mark_verification_failed.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/mark_verified.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/persistence.rb +91 -0
- data/app/services/command_tower/messaging/endpoints/pushover/verify.rb +101 -0
- data/app/services/command_tower/messaging/endpoints/query.rb +28 -0
- data/app/services/command_tower/messaging/endpoints/replace.rb +181 -0
- data/app/services/command_tower/messaging/endpoints/reset_verification.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/revoke.rb +40 -0
- data/app/services/command_tower/messaging/endpoints/safe_view.rb +50 -0
- data/app/services/command_tower/messaging/endpoints/secret_box.rb +86 -0
- data/app/services/command_tower/messaging/endpoints/secret_reader.rb +72 -0
- data/app/services/command_tower/messaging/endpoints/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/validators/push.rb +24 -0
- data/app/services/command_tower/messaging/endpoints/validators/pushover.rb +68 -0
- data/app/services/command_tower/messaging/endpoints/validators.rb +36 -0
- data/app/services/command_tower/messaging/endpoints/verification.rb +39 -0
- data/app/services/command_tower/messaging/endpoints/verification_failed_error.rb +18 -0
- data/app/services/command_tower/messaging/endpoints/verification_transition.rb +20 -0
- data/app/services/command_tower/messaging/endpoints.rb +55 -0
- data/app/services/command_tower/messaging/execution/adapter_request.rb +45 -0
- data/app/services/command_tower/messaging/execution/adapter_result.rb +81 -0
- data/app/services/command_tower/messaging/execution/adapters/email/adapter.rb +78 -0
- data/app/services/command_tower/messaging/execution/adapters/email/configuration.rb +55 -0
- data/app/services/command_tower/messaging/execution/adapters/fake_adapter.rb +34 -0
- data/app/services/command_tower/messaging/execution/adapters/pushover/adapter.rb +107 -0
- data/app/services/command_tower/messaging/execution/adapters/pushover/configuration.rb +30 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/adapter.rb +90 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/configuration.rb +58 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/log_adapter.rb +34 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/twilio_http_client.rb +70 -0
- data/app/services/command_tower/messaging/execution/adapters/unconfigured_adapter.rb +20 -0
- data/app/services/command_tower/messaging/execution/attempt_ledger.rb +146 -0
- data/app/services/command_tower/messaging/execution/claim.rb +35 -0
- data/app/services/command_tower/messaging/execution/invalid_adapter_contract_error.rb +9 -0
- data/app/services/command_tower/messaging/execution/operation_logger.rb +237 -0
- data/app/services/command_tower/messaging/execution/readiness_terminal_mapping.rb +37 -0
- data/app/services/command_tower/messaging/execution/recovery.rb +78 -0
- data/app/services/command_tower/messaging/execution/resolve_recipient_address.rb +58 -0
- data/app/services/command_tower/messaging/execution/revalidate_readiness.rb +73 -0
- data/app/services/command_tower/messaging/execution/scheduler.rb +38 -0
- data/app/services/command_tower/messaging/execution/select_adapter.rb +63 -0
- data/app/services/command_tower/messaging/handoff/advance_communication.rb +39 -0
- data/app/services/command_tower/messaging/handoff/operation_logger.rb +78 -0
- data/app/services/command_tower/messaging/handoff/recovery.rb +26 -0
- data/app/services/command_tower/messaging/inbox/bulk_result.rb +19 -0
- data/app/services/command_tower/messaging/inbox/error.rb +9 -0
- data/app/services/command_tower/messaging/inbox/invalid_bulk_items_error.rb +19 -0
- data/app/services/command_tower/messaging/inbox/invariant_error.rb +9 -0
- data/app/services/command_tower/messaging/inbox/item_result.rb +64 -0
- data/app/services/command_tower/messaging/inbox/list_result.rb +18 -0
- data/app/services/command_tower/messaging/inbox/mutator.rb +252 -0
- data/app/services/command_tower/messaging/inbox/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/inbox/operation_logger.rb +37 -0
- data/app/services/command_tower/messaging/inbox/reader.rb +117 -0
- data/app/services/command_tower/messaging/inbox/scope.rb +24 -0
- data/app/services/command_tower/messaging/inbox/unread_count_result.rb +16 -0
- data/app/services/command_tower/messaging/inbox/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/inbox.rb +61 -0
- data/app/services/command_tower/messaging/notification_types/catalog_category.rb +19 -0
- data/app/services/command_tower/messaging/notification_types/declaration.rb +96 -0
- data/app/services/command_tower/messaging/notification_types/declaration_validator.rb +152 -0
- data/app/services/command_tower/messaging/notification_types/duplicate_type_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/invalid_declaration_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/registry.rb +103 -0
- data/app/services/command_tower/messaging/notification_types/sealed_registry_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types.rb +37 -0
- data/app/services/command_tower/messaging/planner/destination_plan.rb +41 -0
- data/app/services/command_tower/messaging/planner/error.rb +9 -0
- data/app/services/command_tower/messaging/planner/excluded_destination.rb +28 -0
- data/app/services/command_tower/messaging/planner/illegal_override_error.rb +9 -0
- data/app/services/command_tower/messaging/planner/impossible_mandatory_plan_error.rb +9 -0
- data/app/services/command_tower/messaging/planner/invalid_evaluation_error.rb +9 -0
- data/app/services/command_tower/messaging/planner/message_overrides.rb +33 -0
- data/app/services/command_tower/messaging/planner/operation_logger.rb +28 -0
- data/app/services/command_tower/messaging/planner/planner.rb +261 -0
- data/app/services/command_tower/messaging/planner/unknown_type_error.rb +9 -0
- data/app/services/command_tower/messaging/planner.rb +25 -0
- data/app/services/command_tower/messaging/preferences/catalog.rb +96 -0
- data/app/services/command_tower/messaging/preferences/catalog_category_result.rb +19 -0
- data/app/services/command_tower/messaging/preferences/catalog_notification_result.rb +55 -0
- data/app/services/command_tower/messaging/preferences/catalog_result.rb +13 -0
- data/app/services/command_tower/messaging/preferences/error.rb +9 -0
- data/app/services/command_tower/messaging/preferences/evaluation_result.rb +61 -0
- data/app/services/command_tower/messaging/preferences/evaluator.rb +194 -0
- data/app/services/command_tower/messaging/preferences/invalid_preference_state_error.rb +9 -0
- data/app/services/command_tower/messaging/preferences/invalid_preference_write_error.rb +10 -0
- data/app/services/command_tower/messaging/preferences/not_settings_visible_error.rb +10 -0
- data/app/services/command_tower/messaging/preferences/preference_state.rb +61 -0
- data/app/services/command_tower/messaging/preferences/reason_classes.rb +17 -0
- data/app/services/command_tower/messaging/preferences/resolve.rb +50 -0
- data/app/services/command_tower/messaging/preferences/store.rb +126 -0
- data/app/services/command_tower/messaging/preferences/store_error.rb +10 -0
- data/app/services/command_tower/messaging/preferences/suppressed_destination.rb +24 -0
- data/app/services/command_tower/messaging/preferences/unknown_type_error.rb +9 -0
- data/app/services/command_tower/messaging/preferences/update.rb +248 -0
- data/app/services/command_tower/messaging/preferences.rb +68 -0
- data/app/services/command_tower/messaging/pushover/adapters/disabled_adapter.rb +33 -0
- data/app/services/command_tower/messaging/pushover/adapters/fake_adapter.rb +90 -0
- data/app/services/command_tower/messaging/pushover/adapters/http_adapter.rb +145 -0
- data/app/services/command_tower/messaging/pushover/adapters/log_adapter.rb +52 -0
- data/app/services/command_tower/messaging/pushover/result.rb +34 -0
- data/app/services/command_tower/messaging/pushover/transport.rb +64 -0
- data/app/services/command_tower/messaging/recipient_readiness/channel_result.rb +55 -0
- data/app/services/command_tower/messaging/recipient_readiness/error.rb +9 -0
- data/app/services/command_tower/messaging/recipient_readiness/evaluate.rb +319 -0
- data/app/services/command_tower/messaging/recipient_readiness/identity_facts.rb +54 -0
- data/app/services/command_tower/messaging/recipient_readiness/reason_codes.rb +33 -0
- data/app/services/command_tower/messaging/recipient_readiness/recipient_not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/recipient_readiness/recipient_result.rb +38 -0
- data/app/services/command_tower/messaging/recipient_readiness/unknown_channel_error.rb +9 -0
- data/app/services/command_tower/messaging/recipient_readiness.rb +26 -0
- data/app/services/command_tower/messaging/rendering/channel_renderer.rb +127 -0
- data/app/services/command_tower/messaging/rendering/render_error.rb +24 -0
- data/app/services/command_tower/messaging/rendering/rendered_payload.rb +44 -0
- data/app/services/command_tower/messaging/rendering/rendered_pushover_payload.rb +43 -0
- data/app/services/command_tower/messaging/rendering/rendered_sms_payload.rb +40 -0
- data/app/services/command_tower/messaging.rb +35 -0
- data/app/services/command_tower/secrets/cleanse.rb +19 -5
- data/app/services/command_tower/secrets/generate.rb +62 -27
- data/app/services/command_tower/secrets/verify.rb +46 -12
- data/app/services/command_tower/secrets.rb +2 -0
- data/app/services/command_tower/services/account/clear_phone.rb +25 -0
- data/app/services/command_tower/services/account/phone_verification/send.rb +114 -0
- data/app/services/command_tower/services/account/phone_verification/verify.rb +88 -0
- data/app/services/command_tower/services/account/pushover/create.rb +28 -0
- data/app/services/command_tower/services/account/pushover/ct_support.rb +70 -0
- data/app/services/command_tower/services/account/pushover/destroy.rb +30 -0
- data/app/services/command_tower/services/account/pushover/replace.rb +28 -0
- data/app/services/command_tower/services/account/pushover/show.rb +19 -0
- data/app/services/command_tower/services/account/pushover/verify.rb +30 -0
- data/app/services/command_tower/services/account/update_phone.rb +50 -0
- data/app/services/command_tower/services/application_service.rb +21 -0
- data/app/services/command_tower/services/auth/authenticate_session.rb +105 -0
- data/app/services/command_tower/services/auth/authorize_request.rb +34 -0
- data/app/services/command_tower/services/auth/client_ip_resolver.rb +13 -0
- data/app/services/command_tower/services/auth/email_availability.rb +42 -0
- data/app/services/command_tower/services/auth/email_verification/send.rb +65 -0
- data/app/services/command_tower/services/auth/email_verification/verify.rb +51 -0
- data/app/services/command_tower/services/auth/identity_policy.rb +60 -0
- data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_send.rb +48 -0
- data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_token_issue.rb +35 -0
- data/app/services/command_tower/services/auth/password_recovery/rate_limits/enforcement.rb +53 -0
- data/app/services/command_tower/services/auth/password_recovery_session/create.rb +26 -0
- data/app/services/command_tower/services/auth/password_recovery_session/decode.rb +29 -0
- data/app/services/command_tower/services/auth/password_recovery_session/encode.rb +31 -0
- data/app/services/command_tower/services/auth/password_recovery_session/validate.rb +58 -0
- data/app/services/command_tower/services/auth/password_reset/reset.rb +65 -0
- data/app/services/command_tower/services/auth/password_reset/send.rb +77 -0
- data/app/services/command_tower/services/auth/password_reset/token_verification.rb +68 -0
- data/app/services/command_tower/services/auth/password_reset/validate.rb +29 -0
- data/app/services/command_tower/services/auth/plain_text/login.rb +44 -0
- data/app/services/command_tower/services/auth/register.rb +77 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/check_availability.rb +52 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/check_register.rb +24 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/check_token_issue.rb +33 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/enforcement.rb +50 -0
- data/app/services/command_tower/services/auth/signup_session/create.rb +26 -0
- data/app/services/command_tower/services/auth/signup_session/decode.rb +29 -0
- data/app/services/command_tower/services/auth/signup_session/encode.rb +31 -0
- data/app/services/command_tower/services/auth/signup_session/validate.rb +58 -0
- data/app/services/command_tower/services/auth/username_availability.rb +28 -0
- data/app/services/command_tower/services/me/capabilities.rb +43 -0
- data/app/services/command_tower/services/me/change_password.rb +84 -0
- data/app/services/command_tower/services/me/pushover_product_gate.rb +18 -0
- data/app/services/command_tower/services/me/sms_product_gate.rb +19 -0
- data/app/services/command_tower/services/me/update_name.rb +58 -0
- data/app/services/command_tower/services/messaging/communications/produce.rb +68 -0
- data/app/services/command_tower/services/messaging/communications/produce_many.rb +154 -0
- data/app/services/command_tower/services/messaging/inbox.rb +129 -0
- data/app/services/command_tower/services/messaging/preferences/platform_enabled_channels.rb +20 -0
- data/app/services/command_tower/services/messaging/preferences/show.rb +36 -0
- data/app/services/command_tower/services/messaging/preferences/update.rb +43 -0
- data/app/services/command_tower/services/messaging/recipients.rb +20 -0
- data/app/services/command_tower/services/rate_limits/check.rb +31 -0
- data/app/services/command_tower/services/service_result.rb +73 -0
- data/app/services/command_tower/user_attributes/mutate.rb +154 -0
- data/app/services/command_tower/username/available.rb +24 -11
- data/app/views/command_tower/email_verification_mailer/verify_email.html.erb +70 -22
- data/app/views/command_tower/messaging/rendering/email.html.erb +28 -0
- data/app/views/command_tower/messaging/rendering/email.text.erb +7 -0
- data/app/views/command_tower/messaging/rendering/pushover.text.erb +1 -0
- data/app/views/command_tower/messaging/rendering/sms.text.erb +1 -0
- data/app/views/command_tower/password_reset_mailer/reset_password.html.erb +107 -0
- data/app/views/layouts/mailer.html.erb +13 -0
- data/app/views/layouts/mailer.text.erb +1 -0
- data/app/workflows/command_tower/workflows/admin/messaging/create_announcement_workflow.rb +62 -0
- data/app/workflows/command_tower/workflows/application_workflow.rb +68 -0
- data/app/workflows/command_tower/workflows/auth/authenticate_request_workflow.rb +40 -0
- data/app/workflows/command_tower/workflows/auth/authentication_response_effects.rb +21 -0
- data/app/workflows/command_tower/workflows/auth/authorize_request_workflow.rb +28 -0
- data/app/workflows/command_tower/workflows/auth/email_availability_workflow.rb +39 -0
- data/app/workflows/command_tower/workflows/auth/email_verification/send_workflow.rb +32 -0
- data/app/workflows/command_tower/workflows/auth/email_verification/verify_workflow.rb +35 -0
- data/app/workflows/command_tower/workflows/auth/identity_error_status.rb +36 -0
- data/app/workflows/command_tower/workflows/auth/identity_policy_workflow.rb +26 -0
- data/app/workflows/command_tower/workflows/auth/logout_workflow.rb +19 -0
- data/app/workflows/command_tower/workflows/auth/password_recovery_session/authenticate_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/auth/password_recovery_session/create_workflow.rb +39 -0
- data/app/workflows/command_tower/workflows/auth/password_reset/reset_workflow.rb +40 -0
- data/app/workflows/command_tower/workflows/auth/password_reset/send_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/auth/password_reset/validate_workflow.rb +40 -0
- data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +46 -0
- data/app/workflows/command_tower/workflows/auth/register_workflow.rb +83 -0
- data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +44 -0
- data/app/workflows/command_tower/workflows/auth/session_error_status.rb +30 -0
- data/app/workflows/command_tower/workflows/auth/signup_error_status.rb +31 -0
- data/app/workflows/command_tower/workflows/auth/signup_session/authenticate_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/auth/signup_session/create_workflow.rb +37 -0
- data/app/workflows/command_tower/workflows/auth/username_availability_workflow.rb +39 -0
- data/app/workflows/command_tower/workflows/me/change_password_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/clear_phone_workflow.rb +52 -0
- data/app/workflows/command_tower/workflows/me/error_mapping.rb +46 -0
- data/app/workflows/command_tower/workflows/me/error_status.rb +28 -0
- data/app/workflows/command_tower/workflows/me/phone_verification/send_workflow.rb +65 -0
- data/app/workflows/command_tower/workflows/me/phone_verification/verify_workflow.rb +57 -0
- data/app/workflows/command_tower/workflows/me/pushover/create_workflow.rb +38 -0
- data/app/workflows/command_tower/workflows/me/pushover/destroy_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/pushover/replace_workflow.rb +38 -0
- data/app/workflows/command_tower/workflows/me/pushover/show_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/pushover/verify_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/pushover/workflow_support.rb +31 -0
- data/app/workflows/command_tower/workflows/me/show_workflow.rb +33 -0
- data/app/workflows/command_tower/workflows/me/update_name_workflow.rb +48 -0
- data/app/workflows/command_tower/workflows/me/update_phone_workflow.rb +55 -0
- data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +60 -0
- data/app/workflows/command_tower/workflows/messaging/error_mapping.rb +26 -0
- data/app/workflows/command_tower/workflows/messaging/execution/deliver_workflow.rb +234 -0
- data/app/workflows/command_tower/workflows/messaging/handoff/process_workflow.rb +90 -0
- data/app/workflows/command_tower/workflows/messaging/inbox.rb +97 -0
- data/app/workflows/command_tower/workflows/messaging/preferences/show_workflow.rb +31 -0
- data/app/workflows/command_tower/workflows/messaging/preferences/update_workflow.rb +37 -0
- data/app/workflows/command_tower/workflows/profile/show_workflow.rb +27 -0
- data/app/workflows/command_tower/workflows/workflow_result.rb +34 -0
- data/config/routes.rb +85 -37
- data/db/migrate/{20241117043720_create_command_tower_users.rb → 20260805000001_create_command_tower_users.rb} +7 -9
- data/db/migrate/{20241204065708_create_command_tower_user_secrets.rb → 20260805000002_create_command_tower_user_secrets.rb} +3 -0
- data/db/migrate/20260805000003_create_command_tower_messaging_core.rb +90 -0
- data/db/migrate/20260805000004_create_messaging_notification_preferences.rb +17 -0
- data/db/migrate/20260805000005_create_messaging_endpoints_and_pushover_credentials.rb +72 -0
- data/docs/api_reference.md +438 -0
- data/docs/architecture.md +30 -0
- data/docs/authentication.md +37 -0
- data/docs/authentication_authorization_guide.md +150 -0
- data/docs/authorization.md +34 -0
- data/docs/change_password_workflow.md +67 -0
- data/docs/controllers.md +35 -0
- data/docs/cookie_authentication_guide.md +167 -0
- data/docs/email_verification_workflow.md +58 -0
- data/docs/extending.md +105 -0
- data/docs/host_integration_guide.md +158 -0
- data/docs/initializing.md +169 -0
- data/docs/messaging_integration_guide.md +87 -0
- data/docs/models.md +51 -0
- data/docs/pagination.md +47 -0
- data/docs/password_reset_workflow.md +82 -0
- data/docs/sensitive_routes.md +47 -0
- data/docs/testing.md +74 -0
- data/docs/upgrades/0.10.0.md +58 -0
- data/docs/upgrades/README.md +9 -0
- data/lib/command_tower/authorization/default.yml +5 -33
- data/lib/command_tower/configuration/application/config.rb +2 -2
- data/lib/command_tower/configuration/base.rb +0 -1
- data/lib/command_tower/configuration/config.rb +31 -1
- data/lib/command_tower/configuration/credentials/config.rb +33 -0
- data/lib/command_tower/configuration/credentials/smtp.rb +33 -0
- data/lib/command_tower/configuration/credentials/twilio.rb +33 -0
- data/lib/command_tower/configuration/email/config.rb +1 -18
- data/lib/command_tower/configuration/identity/config.rb +19 -0
- data/lib/command_tower/configuration/identity/phone_verification.rb +54 -0
- data/lib/command_tower/configuration/jwt/config.rb +7 -0
- data/lib/command_tower/configuration/jwt/cookie/config.rb +64 -0
- data/lib/command_tower/configuration/jwt/cookie/csrf/config.rb +72 -0
- data/lib/command_tower/configuration/login/strategy/plain_text/config.rb +6 -0
- data/lib/command_tower/configuration/login/strategy/plain_text/email_verify.rb +5 -0
- data/lib/command_tower/configuration/login/strategy/plain_text/password_reset.rb +49 -0
- data/lib/command_tower/configuration/messaging/config.rb +60 -0
- data/lib/command_tower/configuration/messaging/pushover.rb +42 -0
- data/lib/command_tower/configuration/messaging/sms.rb +32 -0
- data/lib/command_tower/configuration/password_recovery_session/config.rb +61 -0
- data/lib/command_tower/configuration/password_recovery_session/rate_limits.rb +33 -0
- data/lib/command_tower/configuration/signup_session/config.rb +67 -0
- data/lib/command_tower/configuration/signup_session/email_availability.rb +18 -0
- data/lib/command_tower/configuration/signup_session/rate_limits.rb +48 -0
- data/lib/command_tower/configuration/user/config.rb +6 -5
- data/lib/command_tower/credential_resolution/env_backend.rb +25 -0
- data/lib/command_tower/credential_resolution/smtp_action_mailer_bridge.rb +24 -0
- data/lib/command_tower/credential_resolution/smtp_credentials.rb +26 -0
- data/lib/command_tower/credential_resolution/twilio_credentials.rb +26 -0
- data/lib/command_tower/credential_resolution.rb +78 -0
- data/lib/command_tower/current.rb +7 -0
- data/lib/command_tower/engine.rb +4 -1
- data/lib/command_tower/install/baseline.rb +31 -0
- data/lib/command_tower/install/doctor.rb +227 -0
- data/lib/command_tower/jwt/authorization_helper.rb +136 -0
- data/lib/command_tower/jwt/csrf_helper.rb +138 -0
- data/lib/command_tower/password_recovery/authorization_helper.rb +25 -0
- data/lib/command_tower/redis_connection.rb +21 -0
- data/lib/command_tower/schema/error/email_validation_required.rb +15 -0
- data/lib/command_tower/schema/shared/user.rb +32 -0
- data/lib/command_tower/schema.rb +2 -25
- data/lib/command_tower/signup/authorization_helper.rb +25 -0
- data/lib/command_tower/spec_helper.rb +58 -1
- data/lib/command_tower/testing.rb +62 -0
- data/lib/command_tower/version.rb +1 -1
- data/lib/command_tower.rb +20 -0
- data/lib/generators/command_tower/configure/USAGE +14 -2
- data/lib/generators/command_tower/configure/configure_generator.rb +29 -2
- data/lib/tasks/auto_annotate_models.rake +23 -55
- data/lib/tasks/install.rake +81 -0
- data/lib/tasks/users.rake +119 -0
- data/spec/factories/entity.rb +37 -0
- data/spec/factories/messaging.rb +156 -0
- data/spec/factories/role.rb +29 -0
- data/spec/factories/user.rb +53 -0
- data/spec/factories/user_secret.rb +12 -0
- metadata +576 -55
- data/app/controllers/command_tower/admin_controller.rb +0 -110
- data/app/controllers/command_tower/auth/plain_text_controller.rb +0 -133
- data/app/controllers/command_tower/inbox/message_blast_controller.rb +0 -89
- data/app/controllers/command_tower/inbox/message_controller.rb +0 -81
- data/app/controllers/command_tower/user_controller.rb +0 -49
- data/app/controllers/command_tower/username_controller.rb +0 -26
- data/app/helpers/command_tower/pagination_helper.rb +0 -40
- data/app/helpers/command_tower/schema_helper.rb +0 -29
- data/app/models/message.rb +0 -30
- data/app/models/message_blast.rb +0 -27
- data/app/services/command_tower/admin_service/users.rb +0 -27
- data/app/services/command_tower/inbox_service/blast/delete.rb +0 -23
- data/app/services/command_tower/inbox_service/blast/metadata.rb +0 -26
- data/app/services/command_tower/inbox_service/blast/new_user_blaster.rb +0 -24
- data/app/services/command_tower/inbox_service/blast/retrieve.rb +0 -30
- data/app/services/command_tower/inbox_service/blast/upsert.rb +0 -67
- data/app/services/command_tower/inbox_service/message/metadata.rb +0 -41
- data/app/services/command_tower/inbox_service/message/modify.rb +0 -44
- data/app/services/command_tower/inbox_service/message/retrieve.rb +0 -36
- data/app/services/command_tower/inbox_service/message/send.rb +0 -33
- data/app/services/command_tower/jwt/time_delay_token.rb +0 -17
- data/app/services/command_tower/login_strategy/plain_text/create.rb +0 -43
- data/app/services/command_tower/login_strategy/plain_text/email_verification/generate.rb +0 -29
- data/app/services/command_tower/login_strategy/plain_text/email_verification/send.rb +0 -23
- data/app/services/command_tower/login_strategy/plain_text/email_verification/verify.rb +0 -24
- data/app/services/command_tower/login_strategy/plain_text/login.rb +0 -50
- data/app/services/command_tower/pagination_service_helper.rb +0 -88
- data/app/services/command_tower/user_attributes/modify.rb +0 -68
- data/app/services/command_tower/user_attributes/roles.rb +0 -27
- data/db/migrate/20250223023306_create_command_tower_messages.rb +0 -12
- data/db/migrate/20250223023313_create_command_tower_message_blasts.rb +0 -14
- data/lib/command_tower/schema/admin/users.rb +0 -16
- data/lib/command_tower/schema/inbox/blast_request.rb +0 -15
- data/lib/command_tower/schema/inbox/blast_response.rb +0 -16
- data/lib/command_tower/schema/inbox/message_blast_entity.rb +0 -16
- data/lib/command_tower/schema/inbox/message_blast_metadata.rb +0 -16
- data/lib/command_tower/schema/inbox/message_entity.rb +0 -14
- data/lib/command_tower/schema/inbox/metadata.rb +0 -18
- data/lib/command_tower/schema/inbox/modified.rb +0 -13
- data/lib/command_tower/schema/page.rb +0 -11
- data/lib/command_tower/schema/pagination.rb +0 -16
- data/lib/command_tower/schema/plain_text/create_user_request.rb +0 -18
- data/lib/command_tower/schema/plain_text/create_user_response.rb +0 -17
- data/lib/command_tower/schema/plain_text/email_verify_request.rb +0 -11
- data/lib/command_tower/schema/plain_text/email_verify_response.rb +0 -11
- data/lib/command_tower/schema/plain_text/email_verify_send_request.rb +0 -9
- data/lib/command_tower/schema/plain_text/email_verify_send_response.rb +0 -11
- data/lib/command_tower/schema/plain_text/login_request.rb +0 -15
- data/lib/command_tower/schema/plain_text/login_response.rb +0 -16
- data/lib/command_tower/schema/user.rb +0 -28
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Initializing CommandTower
|
|
2
|
+
|
|
3
|
+
CommandTower is a Rails engine. The install flow below gets the engine **mounted, migrated, and doctor-checked**. That alone is not a fully usable Me/Auth host — complete RBAC, feature gates, and a smoke check via the [Host integration guide](host_integration_guide.md).
|
|
4
|
+
|
|
5
|
+
Back to [README](../README.md).
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Gemfile: gem "command_tower" (or path/git source)
|
|
11
|
+
bundle install
|
|
12
|
+
|
|
13
|
+
bin/rails command_tower:install
|
|
14
|
+
bin/rails db:migrate
|
|
15
|
+
bin/rails command_tower:doctor
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`command_tower:install` does **not** run `db:migrate`. Installing and migrating stay separate on purpose.
|
|
19
|
+
|
|
20
|
+
### After install
|
|
21
|
+
|
|
22
|
+
Continue with [Host integration](host_integration_guide.md):
|
|
23
|
+
|
|
24
|
+
1. Host `rbac_groups.yml` (required — otherwise authenticated Me/Auth calls **403**)
|
|
25
|
+
2. Assign host roles (for example `member`) on users
|
|
26
|
+
3. Enable feature gates you need (login, reset, availability, …)
|
|
27
|
+
4. Smoke-check `GET /me` with a Bearer token
|
|
28
|
+
5. Wire messaging catalog/adapters when you emit or use phone/Pushover
|
|
29
|
+
|
|
30
|
+
### What `command_tower:install` does
|
|
31
|
+
|
|
32
|
+
1. Runs Rails-native `command_tower:install:migrations` (copies engine migrations into the host `db/migrate/` with `*.command_tower.rb` scope).
|
|
33
|
+
2. Runs `rails g command_tower:configure` unless skipped (creates initializer; mounts the engine).
|
|
34
|
+
3. Prints next steps.
|
|
35
|
+
|
|
36
|
+
### Flags / environment
|
|
37
|
+
|
|
38
|
+
| Variable | Effect |
|
|
39
|
+
|----------|--------|
|
|
40
|
+
| `SKIP_CONFIGURE=1` | Migrations only (existing custom initializer / mount) |
|
|
41
|
+
| `SKIP_MOUNT=1` | Generate initializer but do not mount routes |
|
|
42
|
+
| `FORCE=1` | Overwrite an existing `config/initializers/command_tower.rb` |
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
SKIP_CONFIGURE=1 bin/rails command_tower:install
|
|
48
|
+
SKIP_MOUNT=1 bin/rails command_tower:install
|
|
49
|
+
FORCE=1 bin/rails command_tower:install
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configure generator (optional)
|
|
53
|
+
|
|
54
|
+
Prefer `command_tower:install` for greenfield hosts. The generator remains available:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bin/rails generate command_tower:configure
|
|
58
|
+
bin/rails generate command_tower:configure --skip-routes
|
|
59
|
+
bin/rails generate command_tower:configure --force
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The generator:
|
|
63
|
+
|
|
64
|
+
1. Adds `config/initializers/command_tower.rb` when missing (or overwrites with `--force`).
|
|
65
|
+
2. Mounts `CommandTower::Engine` unless `--skip-routes` or already mounted.
|
|
66
|
+
|
|
67
|
+
Dummy-host example initializer (engine development): [`rails_app/config/initializers/command_tower.rb`](../rails_app/config/initializers/command_tower.rb).
|
|
68
|
+
|
|
69
|
+
## Database migrations
|
|
70
|
+
|
|
71
|
+
CommandTower is the **sole authoring authority** for CommandTower-owned schema (`db/migrate` inside the gem). Hosts must **not** manually write, edit, or copy-paste CommandTower schema migrations (**AD-SCH-01**).
|
|
72
|
+
|
|
73
|
+
### Fresh host install
|
|
74
|
+
|
|
75
|
+
Handled by `command_tower:install` (step 1). Equivalent migration-only command:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bin/rails command_tower:install:migrations
|
|
79
|
+
bin/rails db:migrate
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`command_tower:install:migrations` is the standard Rails engine task (wrapper around `railties:install:migrations`). Re-running installation is **idempotent**: already-installed CommandTower migrations are skipped.
|
|
83
|
+
|
|
84
|
+
Rails may **retimestamp** newly copied host files. That is normal. Do not hand-edit installed `*.command_tower.rb` files.
|
|
85
|
+
|
|
86
|
+
### Upgrade workflow
|
|
87
|
+
|
|
88
|
+
1. Bump/release the CommandTower gem dependency in the host.
|
|
89
|
+
2. Run `bin/rails command_tower:install:migrations` (or `SKIP_CONFIGURE=1 bin/rails command_tower:install`).
|
|
90
|
+
3. Run `bin/rails db:migrate`.
|
|
91
|
+
4. Optionally run `bin/rails command_tower:doctor`.
|
|
92
|
+
|
|
93
|
+
Do not re-run configure on hosts that already customize the initializer unless you intend to regenerate it (`FORCE=1`).
|
|
94
|
+
|
|
95
|
+
### Adding future migrations
|
|
96
|
+
|
|
97
|
+
1. Author the migration **only** in CommandTower `db/migrate/`.
|
|
98
|
+
2. Release / bump the gem in the host.
|
|
99
|
+
3. Install + migrate as above.
|
|
100
|
+
|
|
101
|
+
Installed host copies are an **execution context**, not a second authoring home.
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
Required for production-ready hosts:
|
|
106
|
+
|
|
107
|
+
- `config.jwt.hmac_secret` — typically `SECRET_KEY_BASE` / `Rails.application.secret_key_base`
|
|
108
|
+
- `config.signup_session.jwt_secret` — or `SIGNUP_SESSION_JWT_SECRET`
|
|
109
|
+
- `config.password_recovery_session.jwt_secret` — or `PASSWORD_RECOVERY_SESSION_JWT_SECRET`
|
|
110
|
+
|
|
111
|
+
Optional / feature-gated:
|
|
112
|
+
|
|
113
|
+
- Messaging SMS / Pushover adapters and credential resolution (`config.credentials.*` or ENV)
|
|
114
|
+
- Cookie + CSRF JWT modes
|
|
115
|
+
- Admin enablement, application URL, welcome content hooks
|
|
116
|
+
|
|
117
|
+
The generated initializer documents available options via class_composer. Defaults live in CommandTower; do not duplicate them unnecessarily in the host.
|
|
118
|
+
|
|
119
|
+
## Doctor
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
bin/rails command_tower:doctor
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Checks Rails version compatibility, engine baseline migrations, host-installed migration copies, JWT / session secrets, and messaging adapter names. Failures abort with remediation text; warnings print but allow success.
|
|
126
|
+
|
|
127
|
+
Doctor does **not** probe Redis/SMTP connectivity.
|
|
128
|
+
|
|
129
|
+
## Host vs CommandTower responsibilities
|
|
130
|
+
|
|
131
|
+
| Concern | Owner |
|
|
132
|
+
|---------|--------|
|
|
133
|
+
| Author CT schema migrations | CommandTower |
|
|
134
|
+
| Install / run migrations | Host (via engine tasks + `db:migrate`) |
|
|
135
|
+
| Product configuration / secrets | Host initializer + ENV |
|
|
136
|
+
| Dual-author CT schema in the host | **Forbidden** |
|
|
137
|
+
|
|
138
|
+
## Troubleshooting
|
|
139
|
+
|
|
140
|
+
| Symptom | Fix |
|
|
141
|
+
|---------|-----|
|
|
142
|
+
| `users already exists` on migrate | Database not empty / stale `schema.rb` load — migrate an empty DB, or dump schema after a clean migrate |
|
|
143
|
+
| Install copies nothing new | Already installed — expected idempotency |
|
|
144
|
+
| Missing tables after install | You installed but did not `db:migrate` |
|
|
145
|
+
| Doctor fails on JWT default | Set `config.jwt.hmac_secret` from a real secret |
|
|
146
|
+
| Accidental public engine mount | Use `SKIP_MOUNT=1` / `--skip-routes`, or mount at the path your product needs |
|
|
147
|
+
|
|
148
|
+
## Existing customized hosts
|
|
149
|
+
|
|
150
|
+
If the host already has a bespoke initializer and mount:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
SKIP_CONFIGURE=1 bin/rails command_tower:install
|
|
154
|
+
# same as: bin/rails command_tower:install:migrations
|
|
155
|
+
bin/rails db:migrate
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Do not run `rails g command_tower:configure` unless you intentionally want stock scaffolding.
|
|
159
|
+
|
|
160
|
+
## Test suite factories
|
|
161
|
+
|
|
162
|
+
Shared FactoryBot definitions ship with the gem. In the host test boot path:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
require "command_tower/testing"
|
|
166
|
+
CommandTower::Testing.install!
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Then extend with `FactoryBot.modify` as needed. See [Testing with CommandTower](testing.md).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Messaging with CommandTower
|
|
2
|
+
|
|
3
|
+
CommandTower owns modern messaging **platform** surfaces. Hosts supply product content, channel policy, and adapter credentials.
|
|
4
|
+
|
|
5
|
+
Complete install and host RBAC first: [Host integration](host_integration_guide.md) (Steps 1–8 before emitting or wiring phone/Pushover).
|
|
6
|
+
|
|
7
|
+
## Current surfaces
|
|
8
|
+
|
|
9
|
+
| Concern | Path / entry |
|
|
10
|
+
|---------|----------------|
|
|
11
|
+
| User inbox (consume) | Engine `/me/inbox*` — see [API reference](api_reference.md#me-inbox) |
|
|
12
|
+
| Preferences | Engine `/me/preferences*` — see [API reference](api_reference.md#preferences) |
|
|
13
|
+
| Phone / Pushover endpoints | Engine `/me/phone*`, `/me/pushover*` (503 when capability unavailable) |
|
|
14
|
+
| Single-recipient emit | `CommandTower::Services::Messaging::Communications::Produce` |
|
|
15
|
+
| Multi-recipient emit | `CommandTower::Services::Messaging::Communications::ProduceMany` |
|
|
16
|
+
| Admin cohort announce | Engine `POST /admin/messaging/announcements` |
|
|
17
|
+
| Host ops announce | Host-owned rake/job (product-specific; not part of the gem) |
|
|
18
|
+
|
|
19
|
+
Authentication uses JWT bearer or cookie session as documented in [Authentication](authentication.md). Me Inbox authorization requires host RBAC entities (dummy host uses a `member` role mapping). Admin announcements use entity `admin_messaging_announcements` (engine `default.yml` admin group).
|
|
20
|
+
|
|
21
|
+
## Produce (single recipient)
|
|
22
|
+
|
|
23
|
+
Call from a **product workflow**, not from a controller service bypass:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
CommandTower::Services::Messaging::Communications::Produce.call(
|
|
27
|
+
user: user,
|
|
28
|
+
notification_type_key: "welcome",
|
|
29
|
+
host_event_identity: "welcome/#{user.id}/#{SecureRandom.uuid}",
|
|
30
|
+
title: "Welcome",
|
|
31
|
+
body: "Thanks for joining.",
|
|
32
|
+
platform_enabled_channels: [:inbox, :email],
|
|
33
|
+
metadata: { source: "onboarding" } # optional
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Kwarg | Required |
|
|
38
|
+
|-------|----------|
|
|
39
|
+
| `user` | yes (`User`) |
|
|
40
|
+
| `notification_type_key` | yes (`String`) |
|
|
41
|
+
| `host_event_identity` | yes (`String`) |
|
|
42
|
+
| `title` | yes |
|
|
43
|
+
| `body` | yes |
|
|
44
|
+
| `platform_enabled_channels` | yes (`Array`) |
|
|
45
|
+
| `metadata` | no (`Hash`) |
|
|
46
|
+
|
|
47
|
+
## ProduceMany (multi recipient)
|
|
48
|
+
|
|
49
|
+
| Kwarg | Required / notes |
|
|
50
|
+
|-------|------------------|
|
|
51
|
+
| `user_ids` | yes (`Array` of integer ids) |
|
|
52
|
+
| `notification_type_key` | yes |
|
|
53
|
+
| `campaign_identity` | yes — fan-out builds `host_event_identity` as `"#{campaign_identity}/#{user.id}"` |
|
|
54
|
+
| `title`, `body` | yes |
|
|
55
|
+
| `platform_enabled_channels` | yes |
|
|
56
|
+
| `metadata` | optional |
|
|
57
|
+
| `execution_mode` | optional, default `:async`; `:sync` capped at **25** recipients |
|
|
58
|
+
|
|
59
|
+
Admin announcements HTTP is a product path over ProduceMany (async/sync, audience selection). Contract: [API reference — Admin messaging](api_reference.md#admin-messaging).
|
|
60
|
+
|
|
61
|
+
## Me Inbox HTTP (summary)
|
|
62
|
+
|
|
63
|
+
| Concern | Contract |
|
|
64
|
+
|---------|----------|
|
|
65
|
+
| List | `GET /me/inbox?limit=&offset=&scope=` — meta `{ limit, offset, totalCount }` |
|
|
66
|
+
| Detail / open / archive / delete | `/me/inbox/:id` (+ `open`, `archive`) |
|
|
67
|
+
| Bulk | `POST /me/inbox/bulk/{read,unread,archive,restore,delete}` with body `ids` |
|
|
68
|
+
| Unread | `GET /me/inbox/unread-count` |
|
|
69
|
+
|
|
70
|
+
Pagination detail: [pagination.md](pagination.md). Full catalog: [api_reference.md](api_reference.md#me-inbox).
|
|
71
|
+
|
|
72
|
+
## Host responsibilities
|
|
73
|
+
|
|
74
|
+
- Notification catalog content (registered into CommandTower notification types)
|
|
75
|
+
- `platform_enabled_channels` / channel policy injection
|
|
76
|
+
- Messaging adapter credentials (email / SMS / Pushover) via initializer or ENV
|
|
77
|
+
- Host `rbac_groups.yml` entities for Me inbox/preferences/phone/pushover (and admin if used)
|
|
78
|
+
- Product-specific operational tooling (announce rake tasks, welcome copy)
|
|
79
|
+
|
|
80
|
+
## Related
|
|
81
|
+
|
|
82
|
+
- [Controllers / routes](controllers.md)
|
|
83
|
+
- [API reference](api_reference.md)
|
|
84
|
+
- [Extending](extending.md) — Produce from host workflows; Do-Not-Extend
|
|
85
|
+
- [Initializing](initializing.md) — configuration and doctor
|
|
86
|
+
- [Pagination](pagination.md)
|
|
87
|
+
- [README](../README.md)
|
data/docs/models.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# CommandTower Models
|
|
2
|
+
|
|
3
|
+
Core models live in the root / CommandTower namespaces and support authentication, authorization, and messaging ledger associations.
|
|
4
|
+
|
|
5
|
+
Back to [README](../README.md).
|
|
6
|
+
|
|
7
|
+
## User
|
|
8
|
+
|
|
9
|
+
`User` is the primary identity model for host applications.
|
|
10
|
+
|
|
11
|
+
It underpins [Authentication](authentication.md) and [Authorization](authorization.md), and participates in messaging via associations such as `messaging_communications`.
|
|
12
|
+
|
|
13
|
+
Sensitive session invalidation uses the user’s verifier token — see [Sensitive changes](sensitive_routes.md).
|
|
14
|
+
|
|
15
|
+
### Reopening `User` in the host
|
|
16
|
+
|
|
17
|
+
Prefer services/workflows for new behavior. When you must reopen the class:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
require CommandTower::Engine.root.join("app", "models", "user.rb")
|
|
21
|
+
|
|
22
|
+
class User
|
|
23
|
+
def self.my_class_method; end
|
|
24
|
+
def my_instance_method; end
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## UserSecret
|
|
29
|
+
|
|
30
|
+
`UserSecret` backs validation and recovery-related secrets (for example plain-text email validation flows). It complements [Sensitive changes](sensitive_routes.md).
|
|
31
|
+
|
|
32
|
+
### Reopening `UserSecret` in the host
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
require CommandTower::Engine.root.join("app", "models", "user_secret.rb")
|
|
36
|
+
|
|
37
|
+
class UserSecret
|
|
38
|
+
def self.my_class_method; end
|
|
39
|
+
def my_instance_method; end
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Messaging models
|
|
44
|
+
|
|
45
|
+
Engine-owned messaging persistence (communications, endpoints, preferences, and related records) ships with CommandTower. Prefer platform services/workflows for mutations; do not dual-author schema in the host ([Initializing](initializing.md)).
|
|
46
|
+
|
|
47
|
+
## Related
|
|
48
|
+
|
|
49
|
+
- [Architecture](architecture.md)
|
|
50
|
+
- [Sensitive changes](sensitive_routes.md)
|
|
51
|
+
- [Testing](testing.md) — shared factories for CT-owned models
|
data/docs/pagination.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Pagination
|
|
2
|
+
|
|
3
|
+
CommandTower exposes one live HTTP list contract that returns pagination metadata: **Me Inbox**.
|
|
4
|
+
|
|
5
|
+
Back to [README](../README.md).
|
|
6
|
+
|
|
7
|
+
## Me Inbox list contract
|
|
8
|
+
|
|
9
|
+
`GET /me/inbox` accepts query parameters:
|
|
10
|
+
|
|
11
|
+
| Param | Default | Notes |
|
|
12
|
+
|-------|---------|--------|
|
|
13
|
+
| `limit` | `50` | Max `100` |
|
|
14
|
+
| `offset` | `0` | |
|
|
15
|
+
| `scope` | `"inbox"` | `"inbox"` or `"archived"` |
|
|
16
|
+
|
|
17
|
+
Success envelope `meta`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"limit": 50,
|
|
22
|
+
"offset": 0,
|
|
23
|
+
"totalCount": 123
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`data` is an array of inbox items. Full route table: [API reference — Me Inbox](api_reference.md#me-inbox).
|
|
28
|
+
|
|
29
|
+
Deserializer: `CommandTower::Deserializers::Messaging::Inbox::ListDeserializer`.
|
|
30
|
+
|
|
31
|
+
## Global pagination config
|
|
32
|
+
|
|
33
|
+
`CommandTower.config.pagination.limit` defaults to **10**. That value is a generic configuration default for pagination helpers — it is **not** the Me Inbox HTTP default (Inbox uses 50).
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
CommandTower.configure do |c|
|
|
37
|
+
c.pagination.limit = 50
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
There is no engine admin list endpoint and no `pagination=true` / `page` / `cursor` query API on current engine HTTP surfaces.
|
|
42
|
+
|
|
43
|
+
## Related
|
|
44
|
+
|
|
45
|
+
- [API reference](api_reference.md)
|
|
46
|
+
- [Messaging](messaging_integration_guide.md)
|
|
47
|
+
- [Controllers](controllers.md)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Password Reset (Forgot Password) Workflow
|
|
2
|
+
|
|
3
|
+
Public password recovery for users who cannot authenticate. Flow uses a **password-recovery session** plus gated **password-reset** endpoints. Detailed schemas live here and are summarized in the [API reference](api_reference.md).
|
|
4
|
+
|
|
5
|
+
## Purpose and orchestration
|
|
6
|
+
|
|
7
|
+
| Step | Route | Workflow |
|
|
8
|
+
|------|-------|----------|
|
|
9
|
+
| Create recovery session | `POST /auth/password-recovery-session` | `Workflows::Auth::PasswordRecoverySession::CreateWorkflow` |
|
|
10
|
+
| Send reset email | `POST /auth/password-reset/send` | `Workflows::Auth::PasswordReset::SendWorkflow` |
|
|
11
|
+
| Validate token | `POST /auth/password-reset/validate` | `Workflows::Auth::PasswordReset::ValidateWorkflow` |
|
|
12
|
+
| Reset password | `POST /auth/password-reset/reset` | `Workflows::Auth::PasswordReset::ResetWorkflow` |
|
|
13
|
+
|
|
14
|
+
All listed workflows inherit `ApplicationWorkflow` with `retry_strategy :none`. Controllers under `CommandTower::Auth::PasswordReset::*` and password-recovery-session authenticate the recovery session where required, deserialize, run **one** workflow, and render `WorkflowResult`.
|
|
15
|
+
|
|
16
|
+
**Feature gate:** `CommandTower.config.login.plain_text.password_reset?` (reset endpoints). Recovery session creation is part of the public recovery path.
|
|
17
|
+
|
|
18
|
+
**Boundary:** Controllers own transport + recovery-session auth. Workflows orchestrate rate limits and password-reset services. Services own email/token capability details. Do not call messaging execution internals from hosts — see [extending.md](extending.md).
|
|
19
|
+
|
|
20
|
+
## High-level characteristics
|
|
21
|
+
|
|
22
|
+
- **No user JWT**; `password-reset/send` requires a valid **password-recovery session**; `validate` / `reset` are public and use the emailed reset `token` in the body
|
|
23
|
+
- Email-based identification; responses avoid user enumeration where designed to
|
|
24
|
+
- Secure, time-limited reset tokens
|
|
25
|
+
- Rate limiting via password-recovery rate-limit services
|
|
26
|
+
|
|
27
|
+
## Endpoints (engine-relative)
|
|
28
|
+
|
|
29
|
+
### 1. Create password-recovery session
|
|
30
|
+
|
|
31
|
+
`POST /auth/password-recovery-session`
|
|
32
|
+
|
|
33
|
+
Establishes the recovery session used by subsequent reset calls. See request specs under `spec/requests/` for exact payload/envelope.
|
|
34
|
+
|
|
35
|
+
### 2. Send reset email
|
|
36
|
+
|
|
37
|
+
`POST /auth/password-reset/send`
|
|
38
|
+
|
|
39
|
+
**Auth:** Password-recovery session
|
|
40
|
+
|
|
41
|
+
**Body (typical):** `{ "email": "string" }`
|
|
42
|
+
|
|
43
|
+
**Workflow:** `SendWorkflow` → rate-limit check → `Services::Auth::PasswordReset::Send`
|
|
44
|
+
|
|
45
|
+
### 3. Validate token
|
|
46
|
+
|
|
47
|
+
`POST /auth/password-reset/validate`
|
|
48
|
+
|
|
49
|
+
**Auth:** Public (reset token in body)
|
|
50
|
+
|
|
51
|
+
**Body (typical):** `{ "token": "string" }` (optional `email` when config requires it)
|
|
52
|
+
|
|
53
|
+
**Workflow:** `ValidateWorkflow`
|
|
54
|
+
|
|
55
|
+
### 4. Reset password
|
|
56
|
+
|
|
57
|
+
`POST /auth/password-reset/reset`
|
|
58
|
+
|
|
59
|
+
**Auth:** Public (reset token in body)
|
|
60
|
+
|
|
61
|
+
**Body (typical):** token + new password fields (`passwordConfirmation` / `password_confirmation`)
|
|
62
|
+
|
|
63
|
+
**Workflow:** `ResetWorkflow`
|
|
64
|
+
|
|
65
|
+
Success behavior and verifier rotation differ from authenticated [change password](change_password_workflow.md) — treat recovery success per product policy (often force Sign In).
|
|
66
|
+
|
|
67
|
+
## Errors
|
|
68
|
+
|
|
69
|
+
Failures return the standard application envelope via `render_application_result`. Status mapping uses Auth identity/password-reset error helpers. Validation failures from deserializers typically surface as `422 Unprocessable Entity`.
|
|
70
|
+
|
|
71
|
+
## Security notes
|
|
72
|
+
|
|
73
|
+
- Never log passwords, raw tokens, or recovery-session secrets
|
|
74
|
+
- Prefer HTTPS in production
|
|
75
|
+
- Respect rate limits; do not probe for account existence in client UX
|
|
76
|
+
|
|
77
|
+
## Related
|
|
78
|
+
|
|
79
|
+
- [Change password workflow](change_password_workflow.md)
|
|
80
|
+
- [API reference](api_reference.md)
|
|
81
|
+
- [Initializing](initializing.md) — secrets / feature config
|
|
82
|
+
- [Extending](extending.md)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Sensitive changes and session invalidation
|
|
2
|
+
|
|
3
|
+
CommandTower JWTs are bound to the authenticated user’s **verifier token**. When the verifier rotates, previously issued access tokens that carried the old verifier no longer authenticate (`401`).
|
|
4
|
+
|
|
5
|
+
This is the platform mechanism behind “logout everywhere” and post-password-change session invalidation.
|
|
6
|
+
|
|
7
|
+
## Verifier token on `User`
|
|
8
|
+
|
|
9
|
+
Each `User` may store:
|
|
10
|
+
|
|
11
|
+
- `verifier_token` — opaque string embedded in JWTs at issue time
|
|
12
|
+
- `verifier_token_last_reset` — timestamp of the last rotation
|
|
13
|
+
|
|
14
|
+
API:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
user.reset_verifier_token! # generates a new token, updates timestamp, returns the value
|
|
18
|
+
user.retreive_verifier_token! # returns existing token or creates one (historical spelling)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Authentication compares the verifier claim in the JWT to the user’s current `verifier_token`. A mismatch fails authentication.
|
|
22
|
+
|
|
23
|
+
## When the platform rotates the verifier
|
|
24
|
+
|
|
25
|
+
| Event | Behavior |
|
|
26
|
+
|-------|----------|
|
|
27
|
+
| Authenticated password change (`PATCH /me/password`) | Rotates verifier so prior tokens fail |
|
|
28
|
+
| Host / ops call `user.reset_verifier_token!` | Explicit revoke-all-sessions |
|
|
29
|
+
|
|
30
|
+
There is no engine HTTP admin “mutate user with `verifier_token: true`” route. `UserAttributes::Mutate` may still exist for internal/host use; do not assume a public SchemaHelper admin path.
|
|
31
|
+
|
|
32
|
+
Browser `POST /auth/logout` clears cookie/session transport for that client; it does **not** by itself rotate `verifier_token` for all devices. Use verifier rotation when all outstanding JWTs must die.
|
|
33
|
+
|
|
34
|
+
## Host guidance
|
|
35
|
+
|
|
36
|
+
- Treat password changes and explicit “revoke all sessions” as **sensitive** operations that should rotate the verifier.
|
|
37
|
+
- Do not expose `verifier_token` in API payloads (platform serializers omit it).
|
|
38
|
+
- Prefer platform workflows/services for password change rather than hand-editing password digests without rotation.
|
|
39
|
+
- `UserSecret` backs related validation/recovery secrets; see [Models](models.md).
|
|
40
|
+
|
|
41
|
+
## Related
|
|
42
|
+
|
|
43
|
+
- [Change password workflow](change_password_workflow.md)
|
|
44
|
+
- [Authentication](authentication.md) / [Authentication & authorization guide](authentication_authorization_guide.md)
|
|
45
|
+
- [Cookie authentication](cookie_authentication_guide.md)
|
|
46
|
+
- [Models](models.md)
|
|
47
|
+
- [README](../README.md)
|
data/docs/testing.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Testing with CommandTower
|
|
2
|
+
|
|
3
|
+
CommandTower ships FactoryBot definitions for engine-owned models. Hosts load
|
|
4
|
+
them explicitly in the test suite — there is no production Railtie and
|
|
5
|
+
FactoryBot is not a runtime gem dependency.
|
|
6
|
+
|
|
7
|
+
Back to [README](../README.md). See also [Initializing](initializing.md).
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# spec/rails_helper.rb (after Rails is loaded)
|
|
13
|
+
require "command_tower/testing"
|
|
14
|
+
|
|
15
|
+
CommandTower::Testing.install! # preferred
|
|
16
|
+
# CommandTower::Testing.load_factories! # factories-only / advanced
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Call `install!` **before** any host `FactoryBot.modify` blocks so shared
|
|
20
|
+
factories exist when you extend them.
|
|
21
|
+
|
|
22
|
+
## Extending factories
|
|
23
|
+
|
|
24
|
+
Hosts must not redefine CommandTower base factories. Use `FactoryBot.modify`:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
FactoryBot.modify do
|
|
28
|
+
factory :user do
|
|
29
|
+
roles { ["member"] }
|
|
30
|
+
|
|
31
|
+
trait :operator do
|
|
32
|
+
roles { %w[member operator] }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Packaging and evolution
|
|
39
|
+
|
|
40
|
+
Factory files live under `spec/factories/**` inside the gem and are included in
|
|
41
|
+
the gemspec. Adding or changing a shared factory is a normal CommandTower gem
|
|
42
|
+
bump — hosts pick it up on upgrade.
|
|
43
|
+
|
|
44
|
+
`CommandTower::Testing.load_factories!` is idempotent (safe to call more than
|
|
45
|
+
once). It loads each factory file under `CommandTower::Engine.root/spec/factories`
|
|
46
|
+
explicitly; it does not append to global `FactoryBot.definition_file_paths`.
|
|
47
|
+
|
|
48
|
+
## Responsibilities
|
|
49
|
+
|
|
50
|
+
| Owner | Responsibility |
|
|
51
|
+
|-------|----------------|
|
|
52
|
+
| CommandTower | Base factories for CT-owned models; Testing API; packaging |
|
|
53
|
+
| Host | `install!` in the test boot path; product traits via `FactoryBot.modify` |
|
|
54
|
+
|
|
55
|
+
## Factory invariant
|
|
56
|
+
|
|
57
|
+
Base factories construct **persistent model state only**. They must not invoke
|
|
58
|
+
Services, Workflows, ApplicationWorkflows, or external adapters. Endpoint
|
|
59
|
+
ciphertext uses `SecretBox` / `Fingerprinter` (crypto helpers).
|
|
60
|
+
|
|
61
|
+
For endpoint factories in environments where
|
|
62
|
+
`COMMAND_TOWER_MESSAGING_ENDPOINT_SECRET` is unset, you may call:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
CommandTower::Testing.ensure_endpoint_secret!
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
(SecretBox already falls back to a non-production test secret outside production.)
|
|
69
|
+
|
|
70
|
+
## Contributors vs hosts
|
|
71
|
+
|
|
72
|
+
This guide is the **host Testing API**. Contributors working inside the CommandTower
|
|
73
|
+
repository also follow workspace test standards (`.cursor/rules/testing.mdc`) and
|
|
74
|
+
architecture guards under `spec/architecture/`.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Upgrade: CommandTower 0.10.0
|
|
2
|
+
|
|
3
|
+
**From:** `0.5.0` (and earlier SchemaHelper-era hosts on `main`)
|
|
4
|
+
**To:** `0.10.0`
|
|
5
|
+
|
|
6
|
+
This document summarizes **all changes from `main` through the `0.10.0` release** (committed work on `schematizing` plus the modern platform working tree). It is the durable upgrade / change SST for hosts. For a sequenced blank-host path, use [Host integration](../host_integration_guide.md).
|
|
7
|
+
|
|
8
|
+
## Breaking / host-impact
|
|
9
|
+
|
|
10
|
+
| Change | Host impact |
|
|
11
|
+
|--------|-------------|
|
|
12
|
+
| SchemaHelper Admin / User / legacy Inbox HTTP removed | No `GET /user`, `/user/modify`, `/admin/modify*`, legacy blast/message SchemaHelper routes |
|
|
13
|
+
| Modern response envelope | Engine HTTP returns `{ data, meta, errors }` via `render_application_result` |
|
|
14
|
+
| Current-user path | Use `GET /me` (and `GET /profile`); not `GET /user` |
|
|
15
|
+
| Login path | `POST /auth/plain-text/login` when `login.plain_text.enable?` (route absent → **404** if gate off) |
|
|
16
|
+
| Password change | `PATCH /me/password` (rotates verifier; does not re-issue JWT) |
|
|
17
|
+
| Host RBAC required | Without host `rbac_groups.yml` entity mappings, Me/Auth calls **403** (fail-closed) |
|
|
18
|
+
| Feature gates | Login, email verify, password reset, availability routes are config-gated |
|
|
19
|
+
| Schema ownership (**AD-SCH-01**) | CT authors migrations; hosts install via `command_tower:install` / `install:migrations` then `db:migrate` |
|
|
20
|
+
| Admin HTTP | Only `POST /admin/messaging/announcements` (no SchemaHelper user admin) |
|
|
21
|
+
|
|
22
|
+
## Platform capabilities in 0.10.0
|
|
23
|
+
|
|
24
|
+
- **Auth:** register, plain-text login, logout, session, identity policy, signup session, email/username availability, email verification, password-recovery session + password reset
|
|
25
|
+
- **Me:** account show, profile, name, password, inbox (list/detail/bulk), preferences, phone, Pushover
|
|
26
|
+
- **Messaging emit:** `Communications::Produce` / `ProduceMany` from host workflows
|
|
27
|
+
- **Install:** `command_tower:install`, configure generator, `command_tower:doctor`
|
|
28
|
+
- **Testing:** `CommandTower::Testing.install!` shared factories
|
|
29
|
+
- **Architecture:** Controller → Workflow → Service; Engine uses `authenticate_request!` / `authorize_request!`
|
|
30
|
+
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
- Canonical HTTP catalog: [API reference](../api_reference.md)
|
|
34
|
+
- Host start-here: [Host integration](../host_integration_guide.md)
|
|
35
|
+
- Install SST: [Initializing](../initializing.md)
|
|
36
|
+
- Auth/cookie/RBAC guides refreshed to modern routes
|
|
37
|
+
- Messaging and pagination aligned to Inbox + Produce contracts
|
|
38
|
+
|
|
39
|
+
## Upgrade checklist (existing hosts)
|
|
40
|
+
|
|
41
|
+
1. Bump gem to `0.10.0` and run `bin/rails command_tower:install:migrations` (or `SKIP_CONFIGURE=1 bin/rails command_tower:install`) then `bin/rails db:migrate`.
|
|
42
|
+
2. Set JWT + signup/recovery session secrets; run `bin/rails command_tower:doctor`.
|
|
43
|
+
3. Copy/adapt [`rails_app/config/rbac_groups.yml`](../../rails_app/config/rbac_groups.yml) into the host; do not redefine engine `owner` / `admin` groups.
|
|
44
|
+
4. Ensure users who need Me surfaces have the host `member` role (or equivalent).
|
|
45
|
+
5. Enable feature gates you rely on (`login.plain_text.enable`, password reset, email verify, availability).
|
|
46
|
+
6. Point clients at modern paths (`/me`, `/auth/plain-text/login`, envelope parsing).
|
|
47
|
+
7. Smoke: Bearer login → `GET /me` returns **200** (not **403**).
|
|
48
|
+
8. If emitting messages: catalog, channels, adapters; call Produce from host workflows.
|
|
49
|
+
|
|
50
|
+
New hosts: follow [Host integration](../host_integration_guide.md) end-to-end.
|
|
51
|
+
|
|
52
|
+
## Related
|
|
53
|
+
|
|
54
|
+
- [Initializing](../initializing.md)
|
|
55
|
+
- [Extending](../extending.md)
|
|
56
|
+
- [Messaging](../messaging_integration_guide.md)
|
|
57
|
+
- [Authentication & authorization guide](../authentication_authorization_guide.md)
|
|
58
|
+
- [README](../../README.md)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Upgrades
|
|
2
|
+
|
|
3
|
+
Host-facing upgrade / change summaries for CommandTower releases.
|
|
4
|
+
|
|
5
|
+
| Version | Summary |
|
|
6
|
+
|---------|---------|
|
|
7
|
+
| [0.10.0](0.10.0.md) | Modern Auth/Me/Messaging platform; SchemaHelper removal; host RBAC required |
|
|
8
|
+
|
|
9
|
+
New hosts should start with [Host integration](../host_integration_guide.md).
|
|
@@ -5,38 +5,10 @@ groups:
|
|
|
5
5
|
entities: true
|
|
6
6
|
admin:
|
|
7
7
|
description: |
|
|
8
|
-
|
|
9
|
-
the ability to view and update other users states.
|
|
8
|
+
Platform admin role for modern Engine admin surfaces (e.g. messaging announcements).
|
|
10
9
|
entities:
|
|
11
|
-
-
|
|
12
|
-
- message-blast
|
|
13
|
-
admin-without-impersonation:
|
|
14
|
-
description: |
|
|
15
|
-
This group defines permissions for Admin Read and Write operations. Users with this role will have
|
|
16
|
-
the ability to view and update other users states. However, impersonation is not permitted with this role
|
|
17
|
-
entities:
|
|
18
|
-
- admin-without-impersonate
|
|
19
|
-
- message-blast
|
|
20
|
-
admin-read-only:
|
|
21
|
-
description: |
|
|
22
|
-
This group defines permissions for Admin Read interface only.
|
|
23
|
-
entities:
|
|
24
|
-
- read-admin
|
|
25
|
-
- message-blast-read-only
|
|
10
|
+
- admin_messaging_announcements
|
|
26
11
|
entities:
|
|
27
|
-
- name:
|
|
28
|
-
controller: CommandTower::
|
|
29
|
-
|
|
30
|
-
controller: CommandTower::Inbox::MessageBlastController
|
|
31
|
-
only: metadata
|
|
32
|
-
- name: read-admin
|
|
33
|
-
controller: CommandTower::AdminController
|
|
34
|
-
only: show
|
|
35
|
-
- name: admin
|
|
36
|
-
controller: CommandTower::AdminController
|
|
37
|
-
- name: admin-without-impersonate
|
|
38
|
-
controller: CommandTower::AdminController
|
|
39
|
-
except: impersonate
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
12
|
+
- name: admin_messaging_announcements
|
|
13
|
+
controller: CommandTower::Admin::Messaging::AnnouncementsController
|
|
14
|
+
only: create
|