command_tower 0.3.1 → 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 +104 -28
- 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 +41 -5
- 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/pagination/config.rb +16 -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 +20 -0
- data/lib/generators/command_tower/configure/configure_generator.rb +39 -0
- 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 +579 -53
- data/app/controllers/command_tower/admin_controller.rb +0 -104
- data/app/controllers/command_tower/auth/plain_text_controller.rb +0 -132
- data/app/controllers/command_tower/inbox/message_blast_controller.rb +0 -89
- data/app/controllers/command_tower/inbox/message_controller.rb +0 -79
- 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/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/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 -35
- 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/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 -15
- 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 -14
- 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 -13
- data/lib/command_tower/schema/user.rb +0 -28
- data/lib/generators/api_engine_base/configure/USAGE +0 -8
- data/lib/generators/api_engine_base/configure/configure_generator.rb +0 -12
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Authenticated Password Change Workflow
|
|
2
|
+
|
|
3
|
+
Authenticated password change lets a signed-in user replace their password by proving knowledge of the current password. On success, CommandTower rotates `verifier_token` in the same transaction as the password digest update, invalidating **all** existing JWT sessions (including the caller’s). The API does **not** re-issue a JWT; hosts must clear local auth and send the user through Sign In again.
|
|
4
|
+
|
|
5
|
+
This is distinct from [password recovery](password_reset_workflow.md), which is public and recovery-session/token-based.
|
|
6
|
+
|
|
7
|
+
## Purpose and orchestration
|
|
8
|
+
|
|
9
|
+
| Item | Value |
|
|
10
|
+
|------|--------|
|
|
11
|
+
| Route | `PATCH /me/password` (engine-relative) |
|
|
12
|
+
| Auth | Authentication + authorization boundaries |
|
|
13
|
+
| Controller | `CommandTower::Me::PasswordController#update` |
|
|
14
|
+
| Workflow | `CommandTower::Workflows::Me::ChangePasswordWorkflow` (`ApplicationWorkflow`, `retry_strategy :none`) |
|
|
15
|
+
| Service | `CommandTower::Services::Me::ChangePassword` |
|
|
16
|
+
| Deserializer | `CommandTower::Deserializers::Me::ChangePasswordDeserializer` |
|
|
17
|
+
| Serializer | `CommandTower::Serializers::Me::ChangePasswordResponseSerializer` |
|
|
18
|
+
| Feature gate | Route always drawn (not constrained in `routes.rb`) |
|
|
19
|
+
|
|
20
|
+
**Boundary:** The controller deserializes input and runs **one** workflow. The workflow orchestrates the change-password service and maps failures to HTTP status. It does not implement password rules itself.
|
|
21
|
+
|
|
22
|
+
## Request
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"current_password": "string",
|
|
27
|
+
"password": "string",
|
|
28
|
+
"password_confirmation": "string"
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Success (200)
|
|
33
|
+
|
|
34
|
+
Serialized success payload (message only). Response effects include `clear_token: true` so cookie/header sessions are cleared. No JWT, verifier, or password material is returned.
|
|
35
|
+
|
|
36
|
+
## Service flow (under the workflow)
|
|
37
|
+
|
|
38
|
+
1. Authenticate `current_password`
|
|
39
|
+
2. Confirm `password` matches `password_confirmation`
|
|
40
|
+
3. Enforce configured password length bounds
|
|
41
|
+
4. In one DB transaction: persist new password digest → `reset_verifier_token!`
|
|
42
|
+
5. On failure: typed application errors → workflow HTTP mapping
|
|
43
|
+
|
|
44
|
+
## Failures
|
|
45
|
+
|
|
46
|
+
| Condition | Typical status |
|
|
47
|
+
|-----------|----------------|
|
|
48
|
+
| Missing / invalid JWT | 401 |
|
|
49
|
+
| Validation / bad current password / confirmation | 4xx via Me error mapping |
|
|
50
|
+
| Unexpected infra failure | 5xx |
|
|
51
|
+
|
|
52
|
+
## Contrast with recovery reset
|
|
53
|
+
|
|
54
|
+
| | Authenticated change | Forgot / reset |
|
|
55
|
+
|--|----------------------|----------------|
|
|
56
|
+
| Proof | Current password | Recovery session + reset token |
|
|
57
|
+
| Route | `PATCH /me/password` | `POST /auth/password-recovery-session` + `/auth/password-reset/*` |
|
|
58
|
+
| Auth | JWT | Public (+ recovery session) |
|
|
59
|
+
| Verifier | Rotated | See recovery guide |
|
|
60
|
+
| Post-success | All JWTs invalid; host → Sign In | Host-defined |
|
|
61
|
+
|
|
62
|
+
## Related
|
|
63
|
+
|
|
64
|
+
- [Sensitive changes](sensitive_routes.md)
|
|
65
|
+
- [API reference](api_reference.md)
|
|
66
|
+
- [Password reset workflow](password_reset_workflow.md)
|
|
67
|
+
- [Extending](extending.md)
|
data/docs/controllers.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Controllers and routes
|
|
2
|
+
|
|
3
|
+
CommandTower exposes platform HTTP through `CommandTower::Engine` routes. Hosts typically mount the engine (for example at `/api`) via the configure generator or a custom mount.
|
|
4
|
+
|
|
5
|
+
This page is an **index** of route areas. Detailed request/response contracts live in the [API reference](api_reference.md).
|
|
6
|
+
|
|
7
|
+
## Route areas
|
|
8
|
+
|
|
9
|
+
| Area | Prefix (engine-relative) | Purpose |
|
|
10
|
+
|------|--------------------------|---------|
|
|
11
|
+
| Auth session | `/auth/*` | Login, logout, session, register, signup-session, identity policy, availability |
|
|
12
|
+
| Email verification | `/auth/email-verification/*` | Send / verify email codes |
|
|
13
|
+
| Password recovery | `/auth/password-recovery-session`, `/auth/password-reset/*` | Forgot / reset password |
|
|
14
|
+
| Me / profile | `/me`, `/profile`, `/me/name`, `/me/password` | Account reads and updates |
|
|
15
|
+
| Inbox | `/me/inbox*` | User inbox consume (list, open, archive, bulk ops) |
|
|
16
|
+
| Preferences | `/me/preferences*` | Notification preferences |
|
|
17
|
+
| Phone | `/me/phone*` | Phone endpoint + verification |
|
|
18
|
+
| Pushover | `/me/pushover*` | Pushover endpoint lifecycle + verification |
|
|
19
|
+
| Admin messaging | `/admin/messaging/announcements` | Cohort announcements |
|
|
20
|
+
|
|
21
|
+
Exact paths depend on where the host mounts the engine.
|
|
22
|
+
|
|
23
|
+
## Controllers
|
|
24
|
+
|
|
25
|
+
Engine controllers are transport adapters: authenticate/authorize as required, deserialize, run **exactly one** workflow, render the application envelope. See [Architecture](architecture.md).
|
|
26
|
+
|
|
27
|
+
## Related
|
|
28
|
+
|
|
29
|
+
- [API reference](api_reference.md) — endpoint catalog
|
|
30
|
+
- [Messaging](messaging_integration_guide.md) — messaging surfaces summary
|
|
31
|
+
- [Extending](extending.md) — host extension boundaries
|
|
32
|
+
- [Initializing](initializing.md) — mount and configure
|
|
33
|
+
- [README](../README.md)
|
|
34
|
+
|
|
35
|
+
HTTP request specs for the engine live under `spec/requests/`.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Cookie Authentication Guide
|
|
2
|
+
|
|
3
|
+
HttpOnly cookie authentication for browser / SPA hosts. Mobile and API clients typically use Bearer tokens only.
|
|
4
|
+
|
|
5
|
+
Back to [README](../README.md). Endpoint contracts: [API reference](api_reference.md). Deep authn/RBAC: [authentication_authorization_guide.md](authentication_authorization_guide.md).
|
|
6
|
+
|
|
7
|
+
Journey coverage: `spec/integration_test/auth/cookie_*.rb`.
|
|
8
|
+
|
|
9
|
+
## Enable cookies
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
CommandTower.configure do |config|
|
|
13
|
+
config.jwt.cookie.enabled = true
|
|
14
|
+
# Optional CSRF for cookie-authenticated unsafe methods:
|
|
15
|
+
# config.jwt.cookie.csrf.enabled = true
|
|
16
|
+
end
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Defaults
|
|
20
|
+
|
|
21
|
+
| Setting | Default |
|
|
22
|
+
|---------|---------|
|
|
23
|
+
| `jwt.ttl` | `7.days` |
|
|
24
|
+
| `jwt.cookie.enabled` | `false` |
|
|
25
|
+
| `jwt.cookie.name` | `"ct_jwt"` |
|
|
26
|
+
| `jwt.cookie.same_site` | `:lax` |
|
|
27
|
+
| `jwt.cookie.secure` | `false` (set `true` in production / HTTPS) |
|
|
28
|
+
| `jwt.cookie.httponly` | `true` |
|
|
29
|
+
| `jwt.cookie.path` | `"/"` |
|
|
30
|
+
| `jwt.cookie.domain` | `nil` (host-only) |
|
|
31
|
+
| `jwt.cookie.ttl` | `7.days` |
|
|
32
|
+
| `jwt.cookie.csrf.enabled` | `false` |
|
|
33
|
+
| `jwt.cookie.csrf.cookie_name` | `"ct_csrf"` |
|
|
34
|
+
| `jwt.cookie.csrf.header_name` | `"X-CSRF-Token"` |
|
|
35
|
+
| `jwt.cookie.csrf.rotate_on_login` | `true` |
|
|
36
|
+
| `jwt.cookie.csrf.rotate_on_reset` | `true` |
|
|
37
|
+
| CSRF `same_site` / `secure` / `path` / `domain` | `nil` (inherit JWT cookie) |
|
|
38
|
+
| CSRF `ttl` | `7.days` |
|
|
39
|
+
|
|
40
|
+
## Token extraction order
|
|
41
|
+
|
|
42
|
+
1. `Authorization: Bearer <token>` header (wins when present)
|
|
43
|
+
2. HttpOnly JWT cookie when cookie mode is enabled
|
|
44
|
+
|
|
45
|
+
## Login / logout examples
|
|
46
|
+
|
|
47
|
+
Paths are engine-relative. Prefix with your mount (generator often mounts at `/`; hosts may use `/api`).
|
|
48
|
+
|
|
49
|
+
Plain-text login is gated by `config.login.plain_text.enable?`.
|
|
50
|
+
|
|
51
|
+
### Login
|
|
52
|
+
|
|
53
|
+
```http
|
|
54
|
+
POST /auth/plain-text/login
|
|
55
|
+
Content-Type: application/json
|
|
56
|
+
|
|
57
|
+
{ "identifier": "user@example.com", "password": "secret" }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Success **201** envelope:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"data": {
|
|
65
|
+
"user": {
|
|
66
|
+
"id": 1,
|
|
67
|
+
"email": "user@example.com",
|
|
68
|
+
"username": "user",
|
|
69
|
+
"firstName": "Ada",
|
|
70
|
+
"lastName": "Lovelace",
|
|
71
|
+
"emailValidated": true,
|
|
72
|
+
"roles": ["member"]
|
|
73
|
+
},
|
|
74
|
+
"token": "<jwt>",
|
|
75
|
+
"tokenExpiresAt": "<iso8601>"
|
|
76
|
+
},
|
|
77
|
+
"meta": {},
|
|
78
|
+
"errors": []
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
When cookies are enabled, the response also sets the JWT cookie (and may set/rotate CSRF cookie). Clients that rely on cookies should use `credentials: "include"` (fetch) / `withCredentials: true` (axios).
|
|
83
|
+
|
|
84
|
+
### Current user
|
|
85
|
+
|
|
86
|
+
```http
|
|
87
|
+
GET /me
|
|
88
|
+
Authorization: Bearer <jwt>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or rely on the JWT cookie when cookie mode is enabled and no Bearer header is sent.
|
|
92
|
+
|
|
93
|
+
### Logout (this browser)
|
|
94
|
+
|
|
95
|
+
```http
|
|
96
|
+
POST /auth/logout
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Success **200**:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"data": { "message": "logged_out" },
|
|
104
|
+
"meta": {},
|
|
105
|
+
"errors": []
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Clears the auth cookie for this client. It does **not** rotate `verifier_token`, so other devices’ JWTs remain valid until expiry or verifier rotation.
|
|
110
|
+
|
|
111
|
+
### Logout everywhere
|
|
112
|
+
|
|
113
|
+
Rotate the verifier via authenticated password change:
|
|
114
|
+
|
|
115
|
+
```http
|
|
116
|
+
PATCH /me/password
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or call `user.reset_verifier_token!` from trusted host ops. There is no `POST /user/modify` engine route.
|
|
120
|
+
|
|
121
|
+
See [sensitive_routes.md](sensitive_routes.md).
|
|
122
|
+
|
|
123
|
+
## CSRF (optional)
|
|
124
|
+
|
|
125
|
+
When `jwt.cookie.csrf.enabled` is true, cookie-authenticated **unsafe** methods (POST/PATCH/PUT/DELETE) require:
|
|
126
|
+
|
|
127
|
+
1. Non-HttpOnly CSRF cookie (`ct_csrf` by default)
|
|
128
|
+
2. Matching header (`X-CSRF-Token` by default)
|
|
129
|
+
|
|
130
|
+
Mismatch / missing → auth failure with codes such as `csrf_missing` / `csrf_mismatch` (envelope via modern auth boundaries).
|
|
131
|
+
|
|
132
|
+
Bearer-header requests do not require CSRF.
|
|
133
|
+
|
|
134
|
+
## CORS
|
|
135
|
+
|
|
136
|
+
Browser SPA on another origin needs host CORS that allows credentials and your frontend origin. Example sketch (host-owned):
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
# Host rack-cors or equivalent — illustrative only
|
|
140
|
+
allow do
|
|
141
|
+
origins "https://app.example.com"
|
|
142
|
+
resource "*",
|
|
143
|
+
headers: :any,
|
|
144
|
+
methods: [:get, :post, :put, :patch, :delete, :options],
|
|
145
|
+
credentials: true,
|
|
146
|
+
expose: ["X-Authorization-Expire", "X-Authorization-Reset"]
|
|
147
|
+
end
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`SameSite=None` cookies require `Secure=true` (HTTPS). Prefer same-site deployments with `SameSite=Lax` when possible.
|
|
151
|
+
|
|
152
|
+
## Production checklist
|
|
153
|
+
|
|
154
|
+
- [ ] `jwt.cookie.enabled = true` only when browsers need cookies
|
|
155
|
+
- [ ] `jwt.cookie.secure = true` on HTTPS
|
|
156
|
+
- [ ] Review `same_site` for cross-site SPAs
|
|
157
|
+
- [ ] Enable CSRF if cookies authenticate mutating requests from browsers
|
|
158
|
+
- [ ] CORS credentials + explicit origin (not `*`)
|
|
159
|
+
- [ ] Do not log tokens or CSRF secrets
|
|
160
|
+
|
|
161
|
+
## Related
|
|
162
|
+
|
|
163
|
+
- [API reference](api_reference.md)
|
|
164
|
+
- [Authentication](authentication.md)
|
|
165
|
+
- [Authentication & authorization guide](authentication_authorization_guide.md)
|
|
166
|
+
- [Sensitive changes](sensitive_routes.md)
|
|
167
|
+
- [Initializing](initializing.md)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Email Verification Workflow
|
|
2
|
+
|
|
3
|
+
Code-based email verification ensures the user can receive mail at their registered address. Users authenticate with JWT; a configurable grace period may allow API access before verification is mandatory.
|
|
4
|
+
|
|
5
|
+
## Purpose and orchestration
|
|
6
|
+
|
|
7
|
+
| Step | Route | Workflow |
|
|
8
|
+
|------|-------|----------|
|
|
9
|
+
| Send code | `POST /auth/email-verification/send` | `Workflows::Auth::EmailVerification::SendWorkflow` |
|
|
10
|
+
| Verify code | `POST /auth/email-verification/verify` | `Workflows::Auth::EmailVerification::VerifyWorkflow` |
|
|
11
|
+
|
|
12
|
+
Both workflows inherit `ApplicationWorkflow` with `retry_strategy :none`.
|
|
13
|
+
|
|
14
|
+
| Item | Value |
|
|
15
|
+
|------|--------|
|
|
16
|
+
| Controllers | `CommandTower::Auth::EmailVerification::SendController` / `VerifyController` |
|
|
17
|
+
| Auth | Authentication + authorization boundaries; send bypasses email-validation precondition so unverified users can request codes |
|
|
18
|
+
| Feature gate | `CommandTower.config.login.plain_text.email_verify?` |
|
|
19
|
+
|
|
20
|
+
**Boundary:** Controllers authenticate/authorize, run **one** workflow, render results. Workflows orchestrate verification services. Hosts do not reopen JWT or email-verification framework internals — see [extending.md](extending.md).
|
|
21
|
+
|
|
22
|
+
## Key characteristics
|
|
23
|
+
|
|
24
|
+
- **Method:** Numeric code (length configurable; commonly 6 digits)
|
|
25
|
+
- **Delivery:** Email to the registered address
|
|
26
|
+
- **Expiration:** Configurable (commonly ~10 minutes)
|
|
27
|
+
- **Authentication required:** Valid JWT
|
|
28
|
+
- **Grace period:** Configurable window before verification is enforced on other routes
|
|
29
|
+
|
|
30
|
+
## Endpoints (engine-relative)
|
|
31
|
+
|
|
32
|
+
### Send
|
|
33
|
+
|
|
34
|
+
`POST /auth/email-verification/send`
|
|
35
|
+
|
|
36
|
+
No body typically required (uses `current_user`). Returns a message payload on success.
|
|
37
|
+
|
|
38
|
+
### Verify
|
|
39
|
+
|
|
40
|
+
`POST /auth/email-verification/verify`
|
|
41
|
+
|
|
42
|
+
**Body (typical):** `{ "code": "string" }`
|
|
43
|
+
|
|
44
|
+
On success, marks the user’s email validated.
|
|
45
|
+
|
|
46
|
+
## Errors
|
|
47
|
+
|
|
48
|
+
Standard application envelope. Invalid codes and validation failures map through Auth identity error status helpers. See [API reference](api_reference.md) for envelope shape.
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Email verification options live on CommandTower config (enablement, code length, TTL, grace period). Hosts set secrets and product mail delivery via initializer — [initializing.md](initializing.md).
|
|
53
|
+
|
|
54
|
+
## Related
|
|
55
|
+
|
|
56
|
+
- [API reference](api_reference.md)
|
|
57
|
+
- [Authentication](authentication.md)
|
|
58
|
+
- [Extending](extending.md)
|
data/docs/extending.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Extending CommandTower
|
|
2
|
+
|
|
3
|
+
How hosts customize and integrate with CommandTower **without forking platform internals**.
|
|
4
|
+
|
|
5
|
+
**New host?** Start with [Host integration](host_integration_guide.md).
|
|
6
|
+
|
|
7
|
+
Layer map: [architecture.md](architecture.md). Install/configure/migrate/doctor: [initializing.md](initializing.md). Testing API: [testing.md](testing.md). HTTP catalog: [api_reference.md](api_reference.md).
|
|
8
|
+
|
|
9
|
+
## What hosts should extend
|
|
10
|
+
|
|
11
|
+
| Stable Extension Point | Purpose |
|
|
12
|
+
|------------------------|---------|
|
|
13
|
+
| Routes | Additional host endpoints |
|
|
14
|
+
| Controllers | Product-specific HTTP |
|
|
15
|
+
| Workflows | Product orchestration |
|
|
16
|
+
| Services | Product business logic |
|
|
17
|
+
| `Communications::Produce` / `ProduceMany` | Sending messaging |
|
|
18
|
+
| `FactoryBot.modify` | Extend shared factories |
|
|
19
|
+
| Model reopen | Product associations / behavior |
|
|
20
|
+
| Initializers | Configuration |
|
|
21
|
+
| Notification catalogs / channel policy | Host-owned messaging customization |
|
|
22
|
+
|
|
23
|
+
## Internal platform — do not extend
|
|
24
|
+
|
|
25
|
+
| Internal Platform | Do Not Extend |
|
|
26
|
+
|-------------------|---------------|
|
|
27
|
+
| ApplicationWorkflow | Framework / shared orchestration base |
|
|
28
|
+
| ServiceBase | Shared service framework |
|
|
29
|
+
| Serializers | Platform response shaping |
|
|
30
|
+
| Deserializers | Platform request trust boundary |
|
|
31
|
+
| Messaging execution pipeline | Handoff / execution / accept internals |
|
|
32
|
+
| RequestContext | Framework request context |
|
|
33
|
+
| JWT primitives | Token issue / validate plumbing |
|
|
34
|
+
| Internal framework plumbing | Envelope renderer, workflow base mechanics, etc. |
|
|
35
|
+
|
|
36
|
+
Hosts **use** these via documented public APIs and configuration. Do not subclass, reopen, or fork them as product customization.
|
|
37
|
+
|
|
38
|
+
## Generators and install flags
|
|
39
|
+
|
|
40
|
+
Full install narrative: [initializing.md](initializing.md).
|
|
41
|
+
|
|
42
|
+
| Surface | Notes |
|
|
43
|
+
|---------|--------|
|
|
44
|
+
| `bin/rails command_tower:install` | Migrations + optional configure |
|
|
45
|
+
| `SKIP_CONFIGURE=1` | Migrations only |
|
|
46
|
+
| `SKIP_MOUNT=1` | Initializer without mount |
|
|
47
|
+
| `FORCE=1` | Overwrite initializer |
|
|
48
|
+
| `rails g command_tower:configure` | `--skip-routes`, `--force` |
|
|
49
|
+
|
|
50
|
+
## Mounting and configuration
|
|
51
|
+
|
|
52
|
+
Mount `CommandTower::Engine` at the path your product needs. Configuration and secrets belong in the host initializer. Details: [initializing.md](initializing.md).
|
|
53
|
+
|
|
54
|
+
## Migrations
|
|
55
|
+
|
|
56
|
+
CommandTower authors CT-owned schema; hosts install via engine tasks (`command_tower:install:migrations` / `command_tower:install`) and run `db:migrate`. Do not dual-author CT schema in the host (**AD-SCH-01**). Details: [initializing.md](initializing.md).
|
|
57
|
+
|
|
58
|
+
## Testing API and factories
|
|
59
|
+
|
|
60
|
+
Primary SST: [testing.md](testing.md).
|
|
61
|
+
|
|
62
|
+
| API | Role |
|
|
63
|
+
|-----|------|
|
|
64
|
+
| `CommandTower::Testing.install!` | Preferred host entry (loads factories) |
|
|
65
|
+
| `CommandTower::Testing.load_factories!` | Factories only / advanced |
|
|
66
|
+
| `CommandTower::Testing.ensure_endpoint_secret!` | Test helper for endpoint ciphertext when env unset |
|
|
67
|
+
| `FactoryBot.modify` | Host extension of shared factories |
|
|
68
|
+
|
|
69
|
+
Shipped factory files (under the gem `spec/factories/`): `user`, `user_secret`, messaging factories, `entity`, `role`.
|
|
70
|
+
|
|
71
|
+
## RBAC on host controllers
|
|
72
|
+
|
|
73
|
+
Protect host controllers with `authenticate_user!` / `authorize_user!` (or modern Auth boundaries) and RBAC entities. See [authentication_authorization_guide.md](authentication_authorization_guide.md) and [api_reference.md](api_reference.md).
|
|
74
|
+
|
|
75
|
+
## Model reopen
|
|
76
|
+
|
|
77
|
+
Prefer services/workflows for new behavior. Reopen patterns: [models.md](models.md).
|
|
78
|
+
|
|
79
|
+
## Produce from product workflows
|
|
80
|
+
|
|
81
|
+
Product business events own **host** workflows that call:
|
|
82
|
+
|
|
83
|
+
- `CommandTower::Services::Messaging::Communications::Produce`
|
|
84
|
+
- `CommandTower::Services::Messaging::Communications::ProduceMany`
|
|
85
|
+
|
|
86
|
+
Do not create one Messaging workflow per message type. Do not call the messaging handoff/execution pipeline directly. Summary: [messaging_integration_guide.md](messaging_integration_guide.md).
|
|
87
|
+
|
|
88
|
+
## Blank-host checklist
|
|
89
|
+
|
|
90
|
+
1. Add gem → [initializing.md](initializing.md) quick start (`install`, `db:migrate`, `doctor`)
|
|
91
|
+
2. Configure secrets and feature flags in the host initializer
|
|
92
|
+
3. Mount the engine
|
|
93
|
+
4. Load factories in the host test boot path → [testing.md](testing.md)
|
|
94
|
+
5. Exercise shared surfaces via engine routes → [controllers.md](controllers.md) / [api_reference.md](api_reference.md)
|
|
95
|
+
6. Add product routes/controllers/workflows/services only in the **stable extension** columns above
|
|
96
|
+
7. Keep catalogs, channel policy, and adapter credentials host-owned
|
|
97
|
+
|
|
98
|
+
No DoubleFloor Me (or other product) source is required to use the platform.
|
|
99
|
+
|
|
100
|
+
## Related
|
|
101
|
+
|
|
102
|
+
- [Architecture](architecture.md)
|
|
103
|
+
- [Controllers](controllers.md)
|
|
104
|
+
- [Sensitive changes](sensitive_routes.md)
|
|
105
|
+
- Workflow guides: [password reset](password_reset_workflow.md), [change password](change_password_workflow.md), [email verification](email_verification_workflow.md)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Host Integration Guide
|
|
2
|
+
|
|
3
|
+
**Start here** for integrating CommandTower into a new Rails host.
|
|
4
|
+
|
|
5
|
+
This page owns the **order of operations**. It does not replace specialty docs: install details stay in [Initializing](initializing.md), HTTP contracts in [API reference](api_reference.md), boundaries in [Extending](extending.md).
|
|
6
|
+
|
|
7
|
+
`command_tower:doctor` green means secrets/migrations look sane. It does **not** prove Me/Auth authorization works.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- A Rails host application (engine is mounted into the host)
|
|
12
|
+
- `gem "command_tower"` in the host Gemfile (path, git, or released gem)
|
|
13
|
+
- Host-owned database (and any host Redis/job stack your app already uses)
|
|
14
|
+
|
|
15
|
+
## Step 1 — Install
|
|
16
|
+
|
|
17
|
+
Follow [Initializing](initializing.md):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bundle install
|
|
21
|
+
bin/rails command_tower:install
|
|
22
|
+
bin/rails db:migrate
|
|
23
|
+
bin/rails command_tower:doctor
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That copies CT migrations, generates `config/initializers/command_tower.rb` (unless skipped), and mounts `CommandTower::Engine` (unless skipped).
|
|
27
|
+
|
|
28
|
+
## Step 2 — Secrets and initializer
|
|
29
|
+
|
|
30
|
+
In the host initializer, set at least:
|
|
31
|
+
|
|
32
|
+
- `config.jwt.hmac_secret`
|
|
33
|
+
- `config.signup_session.jwt_secret` (or `SIGNUP_SESSION_JWT_SECRET`)
|
|
34
|
+
- `config.password_recovery_session.jwt_secret` (or `PASSWORD_RECOVERY_SESSION_JWT_SECRET`)
|
|
35
|
+
|
|
36
|
+
Re-run `bin/rails command_tower:doctor`. Details: [Initializing — Configuration](initializing.md#configuration).
|
|
37
|
+
|
|
38
|
+
Dummy-host reference: [`rails_app/config/initializers/command_tower.rb`](../rails_app/config/initializers/command_tower.rb).
|
|
39
|
+
|
|
40
|
+
## Step 3 — Confirm mount path
|
|
41
|
+
|
|
42
|
+
Ensure routes include something like:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
mount CommandTower::Engine => "/" # or "/api"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Engine paths below are **relative to that mount**. Route area index: [Controllers](controllers.md).
|
|
49
|
+
|
|
50
|
+
## Step 4 — Host RBAC (required)
|
|
51
|
+
|
|
52
|
+
AuthorizeRequest **fails closed**. Without host entity mappings, authenticated calls to `/me`, `/auth/session`, inbox, etc. return **403**.
|
|
53
|
+
|
|
54
|
+
The configure generator does **not** create this file.
|
|
55
|
+
|
|
56
|
+
1. Copy and adapt the dummy host file: [`rails_app/config/rbac_groups.yml`](../rails_app/config/rbac_groups.yml) → host `config/rbac_groups.yml`.
|
|
57
|
+
2. Keep a host group such as `member` with entities for session, me, profile, inbox, preferences, phone, pushover, email verification.
|
|
58
|
+
3. Do **not** redefine engine groups that already exist in `default.yml` (`owner`, `admin`).
|
|
59
|
+
4. Point config at the file if needed:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
CommandTower.configure do |c|
|
|
63
|
+
c.authorization.rbac_group_path = Rails.root.join("config/rbac_groups.yml")
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
(Default path is already `config/rbac_groups.yml`.)
|
|
68
|
+
|
|
69
|
+
More: [Authorization](authorization.md), [Authentication & authorization guide](authentication_authorization_guide.md).
|
|
70
|
+
|
|
71
|
+
## Step 5 — Roles on users
|
|
72
|
+
|
|
73
|
+
RBAC YAML defines what a role **may** do. Users still need that role assigned.
|
|
74
|
+
|
|
75
|
+
- Specs and the dummy pattern use role name **`member`** for Me/Auth surfaces.
|
|
76
|
+
- Engine register does **not** automatically attach `member`. Hosts must assign roles via product logic, ops (`command_tower:users:create`), or host hooks after account creation.
|
|
77
|
+
- Admin announcements need a role that includes `admin_messaging_announcements` (engine `admin` group, or an equivalent host mapping).
|
|
78
|
+
|
|
79
|
+
## Step 6 — Enable feature gates you need
|
|
80
|
+
|
|
81
|
+
When a gate is off, the route is **not drawn** → **404**.
|
|
82
|
+
|
|
83
|
+
| Capability | Config |
|
|
84
|
+
|------------|--------|
|
|
85
|
+
| Plain-text login | `config.login.plain_text.enable = true` |
|
|
86
|
+
| Email verification routes | `config.login.plain_text.email_verify = true` |
|
|
87
|
+
| Password reset routes | `config.login.plain_text.password_reset = true` |
|
|
88
|
+
| Email availability | `config.signup_session.email_availability = true` |
|
|
89
|
+
| Username availability | `config.username.realtime_username_check = true` |
|
|
90
|
+
|
|
91
|
+
Full gate list: [API reference — Feature gates](api_reference.md#feature-gates).
|
|
92
|
+
|
|
93
|
+
## Step 7 — Choose auth client path
|
|
94
|
+
|
|
95
|
+
- **API / mobile:** `Authorization: Bearer <jwt>` (default)
|
|
96
|
+
- **Browser / SPA cookies:** enable `config.jwt.cookie` (+ CSRF as needed) — [Cookie authentication](cookie_authentication_guide.md)
|
|
97
|
+
|
|
98
|
+
Engine HTTP success/error bodies use the application envelope `{ data, meta, errors }`. Host provisional helpers (`authenticate_user!`) can differ — [Authentication](authentication.md).
|
|
99
|
+
|
|
100
|
+
## Step 8 — Smoke check
|
|
101
|
+
|
|
102
|
+
With gates and RBAC in place (paths relative to your mount):
|
|
103
|
+
|
|
104
|
+
1. `POST /auth/register` (always drawn) — create a user, then **assign `member`** (Step 5) if not done by host logic.
|
|
105
|
+
2. `POST /auth/plain-text/login` (if enabled) — receive `data.token`.
|
|
106
|
+
3. `GET /me` with Bearer token — expect **200** and envelope `data` (account payload).
|
|
107
|
+
|
|
108
|
+
If you get **401**, authn failed. If you get **403**, fix Steps 4–5 before debugging clients.
|
|
109
|
+
|
|
110
|
+
Contracts: [API reference](api_reference.md).
|
|
111
|
+
|
|
112
|
+
## Step 9 — Messaging (when needed)
|
|
113
|
+
|
|
114
|
+
Host owns:
|
|
115
|
+
|
|
116
|
+
- Notification catalog / types
|
|
117
|
+
- `platform_enabled_channels` / channel policy
|
|
118
|
+
- Adapter credentials (email / SMS / Pushover)
|
|
119
|
+
|
|
120
|
+
Emit from a **host product workflow** via `Communications::Produce` / `ProduceMany` — not by bypassing workflows. See [Messaging](messaging_integration_guide.md) and [Extending](extending.md).
|
|
121
|
+
|
|
122
|
+
Phone/Pushover routes are always drawn; missing product readiness returns **503** capability errors.
|
|
123
|
+
|
|
124
|
+
## Step 10 — Host tests
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
require "command_tower/testing"
|
|
128
|
+
CommandTower::Testing.install!
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Details: [Testing](testing.md). Prefer `spec/requests/` patterns in the gem as HTTP contract proof.
|
|
132
|
+
|
|
133
|
+
## Done when
|
|
134
|
+
|
|
135
|
+
- Doctor passes for secrets/migrations
|
|
136
|
+
- Host `rbac_groups.yml` maps Me/Auth entities
|
|
137
|
+
- Users who should use Me surfaces have the host `member` (or equivalent) role
|
|
138
|
+
- Needed feature gates are enabled
|
|
139
|
+
- `GET /me` returns **200** with a Bearer token for a member user
|
|
140
|
+
|
|
141
|
+
## Common failures
|
|
142
|
+
|
|
143
|
+
| Symptom | Likely cause |
|
|
144
|
+
|---------|----------------|
|
|
145
|
+
| Doctor green, `GET /me` is **403** | Missing host RBAC YAML or user lacks `member` (Steps 4–5) |
|
|
146
|
+
| `POST /auth/plain-text/login` is **404** | `login.plain_text.enable?` off (Step 6) |
|
|
147
|
+
| Password-reset / availability **404** | Matching feature gate off |
|
|
148
|
+
| Phone / Pushover **503** | Capability / adapters not ready (Step 9) |
|
|
149
|
+
| Confused error JSON on host controllers | Provisional `authenticate_user!` vs engine envelope (Step 7) |
|
|
150
|
+
|
|
151
|
+
## Related
|
|
152
|
+
|
|
153
|
+
- [Initializing](initializing.md)
|
|
154
|
+
- [Extending](extending.md)
|
|
155
|
+
- [API reference](api_reference.md)
|
|
156
|
+
- [Authentication & authorization guide](authentication_authorization_guide.md)
|
|
157
|
+
- [Messaging](messaging_integration_guide.md)
|
|
158
|
+
- [README](../README.md)
|