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.
Files changed (603) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -28
  3. data/app/auth/command_tower/auth/auth_context.rb +36 -0
  4. data/app/auth/command_tower/auth/password_recovery_session_context.rb +11 -0
  5. data/app/auth/command_tower/auth/signup_session_context.rb +11 -0
  6. data/app/controllers/command_tower/admin/messaging/announcements_controller.rb +34 -0
  7. data/app/controllers/command_tower/application_controller.rb +80 -14
  8. data/app/controllers/command_tower/auth/email/availability_controller.rb +35 -0
  9. data/app/controllers/command_tower/auth/email_verification/send_controller.rb +24 -0
  10. data/app/controllers/command_tower/auth/email_verification/verify_controller.rb +41 -0
  11. data/app/controllers/command_tower/auth/identity_policy_controller.rb +14 -0
  12. data/app/controllers/command_tower/auth/logout_controller.rb +14 -0
  13. data/app/controllers/command_tower/auth/password_recovery_session/create_controller.rb +17 -0
  14. data/app/controllers/command_tower/auth/password_reset/reset_controller.rb +35 -0
  15. data/app/controllers/command_tower/auth/password_reset/send_controller.rb +40 -0
  16. data/app/controllers/command_tower/auth/password_reset/validate_controller.rb +35 -0
  17. data/app/controllers/command_tower/auth/plain_text/login_controller.rb +22 -0
  18. data/app/controllers/command_tower/auth/register_controller.rb +33 -0
  19. data/app/controllers/command_tower/auth/session_controller.rb +21 -0
  20. data/app/controllers/command_tower/auth/signup_session/create_controller.rb +17 -0
  21. data/app/controllers/command_tower/auth/username/availability_controller.rb +35 -0
  22. data/app/controllers/command_tower/me/inbox_controller.rb +85 -0
  23. data/app/controllers/command_tower/me/name_controller.rb +37 -0
  24. data/app/controllers/command_tower/me/password_controller.rb +37 -0
  25. data/app/controllers/command_tower/me/phone_controller.rb +44 -0
  26. data/app/controllers/command_tower/me/phone_verification/verifications_controller.rb +23 -0
  27. data/app/controllers/command_tower/me/phone_verification/verify_controller.rb +34 -0
  28. data/app/controllers/command_tower/me/preferences_controller.rb +46 -0
  29. data/app/controllers/command_tower/me/pushover/verifications_controller.rb +23 -0
  30. data/app/controllers/command_tower/me/pushover_controller.rb +66 -0
  31. data/app/controllers/command_tower/me_controller.rb +19 -0
  32. data/app/controllers/command_tower/profile_controller.rb +19 -0
  33. data/app/controllers/concerns/command_tower/api/application_response_renderer.rb +82 -0
  34. data/app/controllers/concerns/command_tower/api/deserializer_failure_details.rb +22 -0
  35. data/app/controllers/concerns/command_tower/auth/authentication_boundary.rb +34 -0
  36. data/app/controllers/concerns/command_tower/auth/authorization_boundary.rb +26 -0
  37. data/app/controllers/concerns/command_tower/auth/invalid_credentials_deserializer_renderer.rb +23 -0
  38. data/app/controllers/concerns/command_tower/auth/password_recovery_session_boundary.rb +29 -0
  39. data/app/controllers/concerns/command_tower/auth/signup_session_boundary.rb +29 -0
  40. data/app/deserializers/command_tower/deserializers/admin/messaging/create_announcement_deserializer.rb +106 -0
  41. data/app/deserializers/command_tower/deserializers/application_deserializer.rb +148 -0
  42. data/app/deserializers/command_tower/deserializers/auth/email_availability_deserializer.rb +19 -0
  43. data/app/deserializers/command_tower/deserializers/auth/email_verification/verify_deserializer.rb +21 -0
  44. data/app/deserializers/command_tower/deserializers/auth/password_reset/reset_deserializer.rb +31 -0
  45. data/app/deserializers/command_tower/deserializers/auth/password_reset/send_deserializer.rb +21 -0
  46. data/app/deserializers/command_tower/deserializers/auth/password_reset/validate_deserializer.rb +23 -0
  47. data/app/deserializers/command_tower/deserializers/auth/plain_text/login_deserializer.rb +24 -0
  48. data/app/deserializers/command_tower/deserializers/auth/register_deserializer.rb +42 -0
  49. data/app/deserializers/command_tower/deserializers/auth/username_availability_deserializer.rb +19 -0
  50. data/app/deserializers/command_tower/deserializers/clients/errors.rb +45 -0
  51. data/app/deserializers/command_tower/deserializers/clients/missing.rb +10 -0
  52. data/app/deserializers/command_tower/deserializers/clients/payload.rb +21 -0
  53. data/app/deserializers/command_tower/deserializers/clients/result.rb +122 -0
  54. data/app/deserializers/command_tower/deserializers/clients/types.rb +192 -0
  55. data/app/deserializers/command_tower/deserializers/coercion_result.rb +35 -0
  56. data/app/deserializers/command_tower/deserializers/failure.rb +14 -0
  57. data/app/deserializers/command_tower/deserializers/me/change_password_deserializer.rb +41 -0
  58. data/app/deserializers/command_tower/deserializers/me/phone_verification/verify_deserializer.rb +29 -0
  59. data/app/deserializers/command_tower/deserializers/me/pushover/credentials_deserializer.rb +31 -0
  60. data/app/deserializers/command_tower/deserializers/me/update_name_deserializer.rb +22 -0
  61. data/app/deserializers/command_tower/deserializers/me/update_phone_deserializer.rb +22 -0
  62. data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +77 -0
  63. data/app/deserializers/command_tower/deserializers/messaging/preferences/show_deserializer.rb +17 -0
  64. data/app/deserializers/command_tower/deserializers/messaging/preferences/update_deserializer.rb +97 -0
  65. data/app/errors/command_tower/errors/account/phone_already_verified_error.rb +17 -0
  66. data/app/errors/command_tower/errors/account/phone_missing_error.rb +17 -0
  67. data/app/errors/command_tower/errors/account/phone_verification_code_invalid_error.rb +17 -0
  68. data/app/errors/command_tower/errors/account/phone_verification_expired_error.rb +17 -0
  69. data/app/errors/command_tower/errors/account/phone_verification_send_failed_error.rb +25 -0
  70. data/app/errors/command_tower/errors/account/phone_verification_stale_error.rb +17 -0
  71. data/app/errors/command_tower/errors/account/phone_verification_throttled_error.rb +24 -0
  72. data/app/errors/command_tower/errors/account/pushover_already_configured_error.rb +21 -0
  73. data/app/errors/command_tower/errors/account/pushover_capability_unavailable_error.rb +21 -0
  74. data/app/errors/command_tower/errors/account/pushover_not_configured_error.rb +21 -0
  75. data/app/errors/command_tower/errors/account/pushover_provider_unavailable_error.rb +21 -0
  76. data/app/errors/command_tower/errors/account/pushover_verification_failed_error.rb +27 -0
  77. data/app/errors/command_tower/errors/account/sms_capability_unavailable_error.rb +21 -0
  78. data/app/errors/command_tower/errors/application_error.rb +27 -0
  79. data/app/errors/command_tower/errors/auth/csrf_mismatch_error.rb +17 -0
  80. data/app/errors/command_tower/errors/auth/csrf_missing_error.rb +17 -0
  81. data/app/errors/command_tower/errors/auth/email_already_registered_error.rb +17 -0
  82. data/app/errors/command_tower/errors/auth/email_verification_required_error.rb +21 -0
  83. data/app/errors/command_tower/errors/auth/invalid_credentials_error.rb +21 -0
  84. data/app/errors/command_tower/errors/auth/password_recovery_ip_rate_limit_error.rb +25 -0
  85. data/app/errors/command_tower/errors/auth/password_recovery_session_expired_error.rb +21 -0
  86. data/app/errors/command_tower/errors/auth/password_recovery_session_invalid_error.rb +21 -0
  87. data/app/errors/command_tower/errors/auth/password_recovery_session_missing_error.rb +21 -0
  88. data/app/errors/command_tower/errors/auth/password_recovery_session_rate_limit_error.rb +25 -0
  89. data/app/errors/command_tower/errors/auth/password_reset_invalid_token_error.rb +21 -0
  90. data/app/errors/command_tower/errors/auth/password_reset_unavailable_error.rb +21 -0
  91. data/app/errors/command_tower/errors/auth/signup_ip_rate_limit_error.rb +25 -0
  92. data/app/errors/command_tower/errors/auth/signup_session_expired_error.rb +17 -0
  93. data/app/errors/command_tower/errors/auth/signup_session_invalid_error.rb +17 -0
  94. data/app/errors/command_tower/errors/auth/signup_session_missing_error.rb +17 -0
  95. data/app/errors/command_tower/errors/auth/signup_session_rate_limit_error.rb +25 -0
  96. data/app/errors/command_tower/errors/auth/verification_code_invalid_error.rb +17 -0
  97. data/app/errors/command_tower/errors/auth/verification_send_failed_error.rb +25 -0
  98. data/app/errors/command_tower/errors/forbidden_error.rb +19 -0
  99. data/app/errors/command_tower/errors/internal_error.rb +19 -0
  100. data/app/errors/command_tower/errors/messaging/accept_rejected_error.rb +21 -0
  101. data/app/errors/command_tower/errors/messaging/idempotency_conflict_error.rb +21 -0
  102. data/app/errors/command_tower/errors/messaging/recipient_unresolved_error.rb +21 -0
  103. data/app/errors/command_tower/errors/not_found_error.rb +19 -0
  104. data/app/errors/command_tower/errors/unauthorized_error.rb +19 -0
  105. data/app/errors/command_tower/errors/validation_error.rb +15 -0
  106. data/app/jobs/command_tower/messaging/channel_delivery_execution_job.rb +18 -0
  107. data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +24 -0
  108. data/app/jobs/command_tower/messaging/execution_recovery_job.rb +13 -0
  109. data/app/jobs/command_tower/messaging/handoff_job.rb +15 -0
  110. data/app/jobs/command_tower/messaging/handoff_recovery_job.rb +13 -0
  111. data/app/mailers/command_tower/email_verification_mailer.rb +5 -3
  112. data/app/mailers/command_tower/messaging/channel_mailer.rb +25 -0
  113. data/app/mailers/command_tower/password_reset_mailer.rb +18 -0
  114. data/app/models/command_tower/messaging/channel_delivery.rb +45 -0
  115. data/app/models/command_tower/messaging/communication.rb +60 -0
  116. data/app/models/command_tower/messaging/delivery_attempt.rb +28 -0
  117. data/app/models/command_tower/messaging/destination_plan.rb +17 -0
  118. data/app/models/command_tower/messaging/endpoint.rb +98 -0
  119. data/app/models/command_tower/messaging/endpoint_pushover_credential.rb +18 -0
  120. data/app/models/command_tower/messaging/inbox_item.rb +52 -0
  121. data/app/models/command_tower/messaging/notification_preference.rb +21 -0
  122. data/app/models/user.rb +4 -1
  123. data/app/serializers/command_tower/serializers/admin/messaging/announcement_response_serializer.rb +41 -0
  124. data/app/serializers/command_tower/serializers/application/envelope_serializer.rb +25 -0
  125. data/app/serializers/command_tower/serializers/application/error_entry_serializer.rb +18 -0
  126. data/app/serializers/command_tower/serializers/application_serializer.rb +26 -0
  127. data/app/serializers/command_tower/serializers/auth/email_availability_serializer.rb +17 -0
  128. data/app/serializers/command_tower/serializers/auth/email_verification/message_response_serializer.rb +15 -0
  129. data/app/serializers/command_tower/serializers/auth/identity_policy_serializer.rb +36 -0
  130. data/app/serializers/command_tower/serializers/auth/login_response_serializer.rb +17 -0
  131. data/app/serializers/command_tower/serializers/auth/logout_response_serializer.rb +13 -0
  132. data/app/serializers/command_tower/serializers/auth/password_recovery_session_response_serializer.rb +16 -0
  133. data/app/serializers/command_tower/serializers/auth/password_reset/message_response_serializer.rb +15 -0
  134. data/app/serializers/command_tower/serializers/auth/password_reset/validate_response_serializer.rb +22 -0
  135. data/app/serializers/command_tower/serializers/auth/register_response_serializer.rb +16 -0
  136. data/app/serializers/command_tower/serializers/auth/session_response_serializer.rb +16 -0
  137. data/app/serializers/command_tower/serializers/auth/signup_session_response_serializer.rb +16 -0
  138. data/app/serializers/command_tower/serializers/auth/user_serializer.rb +21 -0
  139. data/app/serializers/command_tower/serializers/auth/username_availability_serializer.rb +17 -0
  140. data/app/serializers/command_tower/serializers/me/account_serializer.rb +45 -0
  141. data/app/serializers/command_tower/serializers/me/change_password_response_serializer.rb +13 -0
  142. data/app/serializers/command_tower/serializers/me/pushover_serializer.rb +59 -0
  143. data/app/serializers/command_tower/serializers/messaging/inbox.rb +45 -0
  144. data/app/serializers/command_tower/serializers/messaging/preferences/catalog_serializer.rb +17 -0
  145. data/app/serializers/command_tower/serializers/messaging/preferences/category_serializer.rb +23 -0
  146. data/app/serializers/command_tower/serializers/messaging/preferences/notification_serializer.rb +30 -0
  147. data/app/serializers/command_tower/serializers/profile/profile_serializer.rb +13 -0
  148. data/app/services/command_tower/README.md +22 -6
  149. data/app/services/command_tower/auth/request_context.rb +21 -0
  150. data/app/services/command_tower/authorize/validate.rb +54 -18
  151. data/app/services/command_tower/clients/client_base.rb +183 -0
  152. data/app/services/command_tower/clients/client_result.rb +45 -0
  153. data/app/services/command_tower/clients/const_resolution.rb +32 -0
  154. data/app/services/command_tower/clients/decoded_response.rb +16 -0
  155. data/app/services/command_tower/clients/endpoint_base.rb +242 -0
  156. data/app/services/command_tower/clients/endpoint_input.rb +18 -0
  157. data/app/services/command_tower/clients/errors/authentication_error.rb +26 -0
  158. data/app/services/command_tower/clients/errors/configuration_error.rb +10 -0
  159. data/app/services/command_tower/clients/errors/deserialization_error.rb +26 -0
  160. data/app/services/command_tower/clients/errors/discovery_error.rb +10 -0
  161. data/app/services/command_tower/clients/errors/upstream_error.rb +26 -0
  162. data/app/services/command_tower/clients/invocation.rb +61 -0
  163. data/app/services/command_tower/clients/namespace_proxy.rb +75 -0
  164. data/app/services/command_tower/clients/scoped_client.rb +88 -0
  165. data/app/services/command_tower/clients/serialized_request.rb +17 -0
  166. data/app/services/command_tower/clients/transport/error.rb +18 -0
  167. data/app/services/command_tower/clients/transport/faraday_adapter.rb +129 -0
  168. data/app/services/command_tower/clients/transport/faraday_config.rb +45 -0
  169. data/app/services/command_tower/clients/transport/request.rb +50 -0
  170. data/app/services/command_tower/clients/transport/response.rb +22 -0
  171. data/app/services/command_tower/clients/url.rb +56 -0
  172. data/app/services/command_tower/identity/phone/normalizer.rb +41 -0
  173. data/app/services/command_tower/identity/phone_verification/sms_configuration.rb +58 -0
  174. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/fake_adapter.rb +35 -0
  175. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/log_adapter.rb +34 -0
  176. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/twilio_adapter.rb +60 -0
  177. data/app/services/command_tower/identity/phone_verification/sms_transport.rb +42 -0
  178. data/app/services/command_tower/jwt/authenticate_user.rb +61 -34
  179. data/app/services/command_tower/jwt/authentication_outcome.rb +36 -0
  180. data/app/services/command_tower/jwt/decode.rb +27 -8
  181. data/app/services/command_tower/jwt/encode.rb +4 -6
  182. data/app/services/command_tower/jwt/login_create.rb +6 -7
  183. data/app/services/command_tower/login_strategy/plain_text/email_verification/required.rb +11 -9
  184. data/app/services/command_tower/messaging/accept/channel_delivery_result.rb +13 -0
  185. data/app/services/command_tower/messaging/accept/coordinator.rb +166 -0
  186. data/app/services/command_tower/messaging/accept/error.rb +9 -0
  187. data/app/services/command_tower/messaging/accept/excluded_destination.rb +24 -0
  188. data/app/services/command_tower/messaging/accept/handoff_scheduler.rb +40 -0
  189. data/app/services/command_tower/messaging/accept/idempotency_conflict_error.rb +9 -0
  190. data/app/services/command_tower/messaging/accept/illegal_override_error.rb +9 -0
  191. data/app/services/command_tower/messaging/accept/impossible_mandatory_plan_error.rb +9 -0
  192. data/app/services/command_tower/messaging/accept/invalid_preference_error.rb +9 -0
  193. data/app/services/command_tower/messaging/accept/invariant_error.rb +9 -0
  194. data/app/services/command_tower/messaging/accept/operation_logger.rb +97 -0
  195. data/app/services/command_tower/messaging/accept/persistence_error.rb +9 -0
  196. data/app/services/command_tower/messaging/accept/persister.rb +90 -0
  197. data/app/services/command_tower/messaging/accept/request_normalizer.rb +100 -0
  198. data/app/services/command_tower/messaging/accept/result.rb +99 -0
  199. data/app/services/command_tower/messaging/accept/unknown_type_error.rb +9 -0
  200. data/app/services/command_tower/messaging/accept/validation_error.rb +9 -0
  201. data/app/services/command_tower/messaging/channel_detectors.rb +52 -0
  202. data/app/services/command_tower/messaging/channels/definition.rb +24 -0
  203. data/app/services/command_tower/messaging/channels/error.rb +9 -0
  204. data/app/services/command_tower/messaging/channels/unknown_channel_error.rb +9 -0
  205. data/app/services/command_tower/messaging/channels.rb +85 -0
  206. data/app/services/command_tower/messaging/contract/communications.rb +29 -0
  207. data/app/services/command_tower/messaging/contract/error.rb +9 -0
  208. data/app/services/command_tower/messaging/contract/internal/finder.rb +43 -0
  209. data/app/services/command_tower/messaging/contract/invariant_error.rb +9 -0
  210. data/app/services/command_tower/messaging/contract/mappers/channel_delivery_mapper.rb +53 -0
  211. data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +61 -0
  212. data/app/services/command_tower/messaging/contract/mappers/delivery_attempt_mapper.rb +27 -0
  213. data/app/services/command_tower/messaging/contract/mappers/destination_plan_mapper.rb +17 -0
  214. data/app/services/command_tower/messaging/contract/mappers/inbox_item_mapper.rb +17 -0
  215. data/app/services/command_tower/messaging/contract/not_found_error.rb +9 -0
  216. data/app/services/command_tower/messaging/contract/observability/correlation.rb +17 -0
  217. data/app/services/command_tower/messaging/contract/observability/operation_logger.rb +101 -0
  218. data/app/services/command_tower/messaging/contract/observability/structured_logger.rb +56 -0
  219. data/app/services/command_tower/messaging/contract/requests/find_communication.rb +21 -0
  220. data/app/services/command_tower/messaging/contract/results/channel_delivery_result.rb +25 -0
  221. data/app/services/command_tower/messaging/contract/results/communication_result.rb +22 -0
  222. data/app/services/command_tower/messaging/contract/results/delivery_attempt_result.rb +21 -0
  223. data/app/services/command_tower/messaging/contract/results/destination_plan_result.rb +11 -0
  224. data/app/services/command_tower/messaging/contract/results/inbox_item_result.rb +11 -0
  225. data/app/services/command_tower/messaging/contract/validation_error.rb +9 -0
  226. data/app/services/command_tower/messaging/delivery_retry_policy.rb +30 -0
  227. data/app/services/command_tower/messaging/endpoints/begin_verification.rb +13 -0
  228. data/app/services/command_tower/messaging/endpoints/channel_gate.rb +39 -0
  229. data/app/services/command_tower/messaging/endpoints/conflict_error.rb +9 -0
  230. data/app/services/command_tower/messaging/endpoints/create.rb +181 -0
  231. data/app/services/command_tower/messaging/endpoints/error.rb +9 -0
  232. data/app/services/command_tower/messaging/endpoints/fingerprinter.rb +50 -0
  233. data/app/services/command_tower/messaging/endpoints/invalid_transition_error.rb +9 -0
  234. data/app/services/command_tower/messaging/endpoints/lifecycle.rb +37 -0
  235. data/app/services/command_tower/messaging/endpoints/mark_invalid.rb +32 -0
  236. data/app/services/command_tower/messaging/endpoints/mark_verification_failed.rb +13 -0
  237. data/app/services/command_tower/messaging/endpoints/mark_verified.rb +13 -0
  238. data/app/services/command_tower/messaging/endpoints/not_found_error.rb +9 -0
  239. data/app/services/command_tower/messaging/endpoints/persistence.rb +91 -0
  240. data/app/services/command_tower/messaging/endpoints/pushover/verify.rb +101 -0
  241. data/app/services/command_tower/messaging/endpoints/query.rb +28 -0
  242. data/app/services/command_tower/messaging/endpoints/replace.rb +181 -0
  243. data/app/services/command_tower/messaging/endpoints/reset_verification.rb +13 -0
  244. data/app/services/command_tower/messaging/endpoints/revoke.rb +40 -0
  245. data/app/services/command_tower/messaging/endpoints/safe_view.rb +50 -0
  246. data/app/services/command_tower/messaging/endpoints/secret_box.rb +86 -0
  247. data/app/services/command_tower/messaging/endpoints/secret_reader.rb +72 -0
  248. data/app/services/command_tower/messaging/endpoints/validation_error.rb +9 -0
  249. data/app/services/command_tower/messaging/endpoints/validators/push.rb +24 -0
  250. data/app/services/command_tower/messaging/endpoints/validators/pushover.rb +68 -0
  251. data/app/services/command_tower/messaging/endpoints/validators.rb +36 -0
  252. data/app/services/command_tower/messaging/endpoints/verification.rb +39 -0
  253. data/app/services/command_tower/messaging/endpoints/verification_failed_error.rb +18 -0
  254. data/app/services/command_tower/messaging/endpoints/verification_transition.rb +20 -0
  255. data/app/services/command_tower/messaging/endpoints.rb +55 -0
  256. data/app/services/command_tower/messaging/execution/adapter_request.rb +45 -0
  257. data/app/services/command_tower/messaging/execution/adapter_result.rb +81 -0
  258. data/app/services/command_tower/messaging/execution/adapters/email/adapter.rb +78 -0
  259. data/app/services/command_tower/messaging/execution/adapters/email/configuration.rb +55 -0
  260. data/app/services/command_tower/messaging/execution/adapters/fake_adapter.rb +34 -0
  261. data/app/services/command_tower/messaging/execution/adapters/pushover/adapter.rb +107 -0
  262. data/app/services/command_tower/messaging/execution/adapters/pushover/configuration.rb +30 -0
  263. data/app/services/command_tower/messaging/execution/adapters/sms/adapter.rb +90 -0
  264. data/app/services/command_tower/messaging/execution/adapters/sms/configuration.rb +58 -0
  265. data/app/services/command_tower/messaging/execution/adapters/sms/log_adapter.rb +34 -0
  266. data/app/services/command_tower/messaging/execution/adapters/sms/twilio_http_client.rb +70 -0
  267. data/app/services/command_tower/messaging/execution/adapters/unconfigured_adapter.rb +20 -0
  268. data/app/services/command_tower/messaging/execution/attempt_ledger.rb +146 -0
  269. data/app/services/command_tower/messaging/execution/claim.rb +35 -0
  270. data/app/services/command_tower/messaging/execution/invalid_adapter_contract_error.rb +9 -0
  271. data/app/services/command_tower/messaging/execution/operation_logger.rb +237 -0
  272. data/app/services/command_tower/messaging/execution/readiness_terminal_mapping.rb +37 -0
  273. data/app/services/command_tower/messaging/execution/recovery.rb +78 -0
  274. data/app/services/command_tower/messaging/execution/resolve_recipient_address.rb +58 -0
  275. data/app/services/command_tower/messaging/execution/revalidate_readiness.rb +73 -0
  276. data/app/services/command_tower/messaging/execution/scheduler.rb +38 -0
  277. data/app/services/command_tower/messaging/execution/select_adapter.rb +63 -0
  278. data/app/services/command_tower/messaging/handoff/advance_communication.rb +39 -0
  279. data/app/services/command_tower/messaging/handoff/operation_logger.rb +78 -0
  280. data/app/services/command_tower/messaging/handoff/recovery.rb +26 -0
  281. data/app/services/command_tower/messaging/inbox/bulk_result.rb +19 -0
  282. data/app/services/command_tower/messaging/inbox/error.rb +9 -0
  283. data/app/services/command_tower/messaging/inbox/invalid_bulk_items_error.rb +19 -0
  284. data/app/services/command_tower/messaging/inbox/invariant_error.rb +9 -0
  285. data/app/services/command_tower/messaging/inbox/item_result.rb +64 -0
  286. data/app/services/command_tower/messaging/inbox/list_result.rb +18 -0
  287. data/app/services/command_tower/messaging/inbox/mutator.rb +252 -0
  288. data/app/services/command_tower/messaging/inbox/not_found_error.rb +9 -0
  289. data/app/services/command_tower/messaging/inbox/operation_logger.rb +37 -0
  290. data/app/services/command_tower/messaging/inbox/reader.rb +117 -0
  291. data/app/services/command_tower/messaging/inbox/scope.rb +24 -0
  292. data/app/services/command_tower/messaging/inbox/unread_count_result.rb +16 -0
  293. data/app/services/command_tower/messaging/inbox/validation_error.rb +9 -0
  294. data/app/services/command_tower/messaging/inbox.rb +61 -0
  295. data/app/services/command_tower/messaging/notification_types/catalog_category.rb +19 -0
  296. data/app/services/command_tower/messaging/notification_types/declaration.rb +96 -0
  297. data/app/services/command_tower/messaging/notification_types/declaration_validator.rb +152 -0
  298. data/app/services/command_tower/messaging/notification_types/duplicate_type_error.rb +9 -0
  299. data/app/services/command_tower/messaging/notification_types/error.rb +9 -0
  300. data/app/services/command_tower/messaging/notification_types/invalid_declaration_error.rb +9 -0
  301. data/app/services/command_tower/messaging/notification_types/not_found_error.rb +9 -0
  302. data/app/services/command_tower/messaging/notification_types/registry.rb +103 -0
  303. data/app/services/command_tower/messaging/notification_types/sealed_registry_error.rb +9 -0
  304. data/app/services/command_tower/messaging/notification_types.rb +37 -0
  305. data/app/services/command_tower/messaging/planner/destination_plan.rb +41 -0
  306. data/app/services/command_tower/messaging/planner/error.rb +9 -0
  307. data/app/services/command_tower/messaging/planner/excluded_destination.rb +28 -0
  308. data/app/services/command_tower/messaging/planner/illegal_override_error.rb +9 -0
  309. data/app/services/command_tower/messaging/planner/impossible_mandatory_plan_error.rb +9 -0
  310. data/app/services/command_tower/messaging/planner/invalid_evaluation_error.rb +9 -0
  311. data/app/services/command_tower/messaging/planner/message_overrides.rb +33 -0
  312. data/app/services/command_tower/messaging/planner/operation_logger.rb +28 -0
  313. data/app/services/command_tower/messaging/planner/planner.rb +261 -0
  314. data/app/services/command_tower/messaging/planner/unknown_type_error.rb +9 -0
  315. data/app/services/command_tower/messaging/planner.rb +25 -0
  316. data/app/services/command_tower/messaging/preferences/catalog.rb +96 -0
  317. data/app/services/command_tower/messaging/preferences/catalog_category_result.rb +19 -0
  318. data/app/services/command_tower/messaging/preferences/catalog_notification_result.rb +55 -0
  319. data/app/services/command_tower/messaging/preferences/catalog_result.rb +13 -0
  320. data/app/services/command_tower/messaging/preferences/error.rb +9 -0
  321. data/app/services/command_tower/messaging/preferences/evaluation_result.rb +61 -0
  322. data/app/services/command_tower/messaging/preferences/evaluator.rb +194 -0
  323. data/app/services/command_tower/messaging/preferences/invalid_preference_state_error.rb +9 -0
  324. data/app/services/command_tower/messaging/preferences/invalid_preference_write_error.rb +10 -0
  325. data/app/services/command_tower/messaging/preferences/not_settings_visible_error.rb +10 -0
  326. data/app/services/command_tower/messaging/preferences/preference_state.rb +61 -0
  327. data/app/services/command_tower/messaging/preferences/reason_classes.rb +17 -0
  328. data/app/services/command_tower/messaging/preferences/resolve.rb +50 -0
  329. data/app/services/command_tower/messaging/preferences/store.rb +126 -0
  330. data/app/services/command_tower/messaging/preferences/store_error.rb +10 -0
  331. data/app/services/command_tower/messaging/preferences/suppressed_destination.rb +24 -0
  332. data/app/services/command_tower/messaging/preferences/unknown_type_error.rb +9 -0
  333. data/app/services/command_tower/messaging/preferences/update.rb +248 -0
  334. data/app/services/command_tower/messaging/preferences.rb +68 -0
  335. data/app/services/command_tower/messaging/pushover/adapters/disabled_adapter.rb +33 -0
  336. data/app/services/command_tower/messaging/pushover/adapters/fake_adapter.rb +90 -0
  337. data/app/services/command_tower/messaging/pushover/adapters/http_adapter.rb +145 -0
  338. data/app/services/command_tower/messaging/pushover/adapters/log_adapter.rb +52 -0
  339. data/app/services/command_tower/messaging/pushover/result.rb +34 -0
  340. data/app/services/command_tower/messaging/pushover/transport.rb +64 -0
  341. data/app/services/command_tower/messaging/recipient_readiness/channel_result.rb +55 -0
  342. data/app/services/command_tower/messaging/recipient_readiness/error.rb +9 -0
  343. data/app/services/command_tower/messaging/recipient_readiness/evaluate.rb +319 -0
  344. data/app/services/command_tower/messaging/recipient_readiness/identity_facts.rb +54 -0
  345. data/app/services/command_tower/messaging/recipient_readiness/reason_codes.rb +33 -0
  346. data/app/services/command_tower/messaging/recipient_readiness/recipient_not_found_error.rb +9 -0
  347. data/app/services/command_tower/messaging/recipient_readiness/recipient_result.rb +38 -0
  348. data/app/services/command_tower/messaging/recipient_readiness/unknown_channel_error.rb +9 -0
  349. data/app/services/command_tower/messaging/recipient_readiness.rb +26 -0
  350. data/app/services/command_tower/messaging/rendering/channel_renderer.rb +127 -0
  351. data/app/services/command_tower/messaging/rendering/render_error.rb +24 -0
  352. data/app/services/command_tower/messaging/rendering/rendered_payload.rb +44 -0
  353. data/app/services/command_tower/messaging/rendering/rendered_pushover_payload.rb +43 -0
  354. data/app/services/command_tower/messaging/rendering/rendered_sms_payload.rb +40 -0
  355. data/app/services/command_tower/messaging.rb +35 -0
  356. data/app/services/command_tower/secrets/cleanse.rb +19 -5
  357. data/app/services/command_tower/secrets/generate.rb +62 -27
  358. data/app/services/command_tower/secrets/verify.rb +46 -12
  359. data/app/services/command_tower/secrets.rb +2 -0
  360. data/app/services/command_tower/services/account/clear_phone.rb +25 -0
  361. data/app/services/command_tower/services/account/phone_verification/send.rb +114 -0
  362. data/app/services/command_tower/services/account/phone_verification/verify.rb +88 -0
  363. data/app/services/command_tower/services/account/pushover/create.rb +28 -0
  364. data/app/services/command_tower/services/account/pushover/ct_support.rb +70 -0
  365. data/app/services/command_tower/services/account/pushover/destroy.rb +30 -0
  366. data/app/services/command_tower/services/account/pushover/replace.rb +28 -0
  367. data/app/services/command_tower/services/account/pushover/show.rb +19 -0
  368. data/app/services/command_tower/services/account/pushover/verify.rb +30 -0
  369. data/app/services/command_tower/services/account/update_phone.rb +50 -0
  370. data/app/services/command_tower/services/application_service.rb +21 -0
  371. data/app/services/command_tower/services/auth/authenticate_session.rb +105 -0
  372. data/app/services/command_tower/services/auth/authorize_request.rb +34 -0
  373. data/app/services/command_tower/services/auth/client_ip_resolver.rb +13 -0
  374. data/app/services/command_tower/services/auth/email_availability.rb +42 -0
  375. data/app/services/command_tower/services/auth/email_verification/send.rb +65 -0
  376. data/app/services/command_tower/services/auth/email_verification/verify.rb +51 -0
  377. data/app/services/command_tower/services/auth/identity_policy.rb +60 -0
  378. data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_send.rb +48 -0
  379. data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_token_issue.rb +35 -0
  380. data/app/services/command_tower/services/auth/password_recovery/rate_limits/enforcement.rb +53 -0
  381. data/app/services/command_tower/services/auth/password_recovery_session/create.rb +26 -0
  382. data/app/services/command_tower/services/auth/password_recovery_session/decode.rb +29 -0
  383. data/app/services/command_tower/services/auth/password_recovery_session/encode.rb +31 -0
  384. data/app/services/command_tower/services/auth/password_recovery_session/validate.rb +58 -0
  385. data/app/services/command_tower/services/auth/password_reset/reset.rb +65 -0
  386. data/app/services/command_tower/services/auth/password_reset/send.rb +77 -0
  387. data/app/services/command_tower/services/auth/password_reset/token_verification.rb +68 -0
  388. data/app/services/command_tower/services/auth/password_reset/validate.rb +29 -0
  389. data/app/services/command_tower/services/auth/plain_text/login.rb +44 -0
  390. data/app/services/command_tower/services/auth/register.rb +77 -0
  391. data/app/services/command_tower/services/auth/signup_rate_limits/check_availability.rb +52 -0
  392. data/app/services/command_tower/services/auth/signup_rate_limits/check_register.rb +24 -0
  393. data/app/services/command_tower/services/auth/signup_rate_limits/check_token_issue.rb +33 -0
  394. data/app/services/command_tower/services/auth/signup_rate_limits/enforcement.rb +50 -0
  395. data/app/services/command_tower/services/auth/signup_session/create.rb +26 -0
  396. data/app/services/command_tower/services/auth/signup_session/decode.rb +29 -0
  397. data/app/services/command_tower/services/auth/signup_session/encode.rb +31 -0
  398. data/app/services/command_tower/services/auth/signup_session/validate.rb +58 -0
  399. data/app/services/command_tower/services/auth/username_availability.rb +28 -0
  400. data/app/services/command_tower/services/me/capabilities.rb +43 -0
  401. data/app/services/command_tower/services/me/change_password.rb +84 -0
  402. data/app/services/command_tower/services/me/pushover_product_gate.rb +18 -0
  403. data/app/services/command_tower/services/me/sms_product_gate.rb +19 -0
  404. data/app/services/command_tower/services/me/update_name.rb +58 -0
  405. data/app/services/command_tower/services/messaging/communications/produce.rb +68 -0
  406. data/app/services/command_tower/services/messaging/communications/produce_many.rb +154 -0
  407. data/app/services/command_tower/services/messaging/inbox.rb +129 -0
  408. data/app/services/command_tower/services/messaging/preferences/platform_enabled_channels.rb +20 -0
  409. data/app/services/command_tower/services/messaging/preferences/show.rb +36 -0
  410. data/app/services/command_tower/services/messaging/preferences/update.rb +43 -0
  411. data/app/services/command_tower/services/messaging/recipients.rb +20 -0
  412. data/app/services/command_tower/services/rate_limits/check.rb +31 -0
  413. data/app/services/command_tower/services/service_result.rb +73 -0
  414. data/app/services/command_tower/user_attributes/mutate.rb +154 -0
  415. data/app/services/command_tower/username/available.rb +24 -11
  416. data/app/views/command_tower/email_verification_mailer/verify_email.html.erb +70 -22
  417. data/app/views/command_tower/messaging/rendering/email.html.erb +28 -0
  418. data/app/views/command_tower/messaging/rendering/email.text.erb +7 -0
  419. data/app/views/command_tower/messaging/rendering/pushover.text.erb +1 -0
  420. data/app/views/command_tower/messaging/rendering/sms.text.erb +1 -0
  421. data/app/views/command_tower/password_reset_mailer/reset_password.html.erb +107 -0
  422. data/app/views/layouts/mailer.html.erb +13 -0
  423. data/app/views/layouts/mailer.text.erb +1 -0
  424. data/app/workflows/command_tower/workflows/admin/messaging/create_announcement_workflow.rb +62 -0
  425. data/app/workflows/command_tower/workflows/application_workflow.rb +68 -0
  426. data/app/workflows/command_tower/workflows/auth/authenticate_request_workflow.rb +40 -0
  427. data/app/workflows/command_tower/workflows/auth/authentication_response_effects.rb +21 -0
  428. data/app/workflows/command_tower/workflows/auth/authorize_request_workflow.rb +28 -0
  429. data/app/workflows/command_tower/workflows/auth/email_availability_workflow.rb +39 -0
  430. data/app/workflows/command_tower/workflows/auth/email_verification/send_workflow.rb +32 -0
  431. data/app/workflows/command_tower/workflows/auth/email_verification/verify_workflow.rb +35 -0
  432. data/app/workflows/command_tower/workflows/auth/identity_error_status.rb +36 -0
  433. data/app/workflows/command_tower/workflows/auth/identity_policy_workflow.rb +26 -0
  434. data/app/workflows/command_tower/workflows/auth/logout_workflow.rb +19 -0
  435. data/app/workflows/command_tower/workflows/auth/password_recovery_session/authenticate_workflow.rb +49 -0
  436. data/app/workflows/command_tower/workflows/auth/password_recovery_session/create_workflow.rb +39 -0
  437. data/app/workflows/command_tower/workflows/auth/password_reset/reset_workflow.rb +40 -0
  438. data/app/workflows/command_tower/workflows/auth/password_reset/send_workflow.rb +45 -0
  439. data/app/workflows/command_tower/workflows/auth/password_reset/validate_workflow.rb +40 -0
  440. data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +46 -0
  441. data/app/workflows/command_tower/workflows/auth/register_workflow.rb +83 -0
  442. data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +44 -0
  443. data/app/workflows/command_tower/workflows/auth/session_error_status.rb +30 -0
  444. data/app/workflows/command_tower/workflows/auth/signup_error_status.rb +31 -0
  445. data/app/workflows/command_tower/workflows/auth/signup_session/authenticate_workflow.rb +49 -0
  446. data/app/workflows/command_tower/workflows/auth/signup_session/create_workflow.rb +37 -0
  447. data/app/workflows/command_tower/workflows/auth/username_availability_workflow.rb +39 -0
  448. data/app/workflows/command_tower/workflows/me/change_password_workflow.rb +34 -0
  449. data/app/workflows/command_tower/workflows/me/clear_phone_workflow.rb +52 -0
  450. data/app/workflows/command_tower/workflows/me/error_mapping.rb +46 -0
  451. data/app/workflows/command_tower/workflows/me/error_status.rb +28 -0
  452. data/app/workflows/command_tower/workflows/me/phone_verification/send_workflow.rb +65 -0
  453. data/app/workflows/command_tower/workflows/me/phone_verification/verify_workflow.rb +57 -0
  454. data/app/workflows/command_tower/workflows/me/pushover/create_workflow.rb +38 -0
  455. data/app/workflows/command_tower/workflows/me/pushover/destroy_workflow.rb +34 -0
  456. data/app/workflows/command_tower/workflows/me/pushover/replace_workflow.rb +38 -0
  457. data/app/workflows/command_tower/workflows/me/pushover/show_workflow.rb +34 -0
  458. data/app/workflows/command_tower/workflows/me/pushover/verify_workflow.rb +34 -0
  459. data/app/workflows/command_tower/workflows/me/pushover/workflow_support.rb +31 -0
  460. data/app/workflows/command_tower/workflows/me/show_workflow.rb +33 -0
  461. data/app/workflows/command_tower/workflows/me/update_name_workflow.rb +48 -0
  462. data/app/workflows/command_tower/workflows/me/update_phone_workflow.rb +55 -0
  463. data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +60 -0
  464. data/app/workflows/command_tower/workflows/messaging/error_mapping.rb +26 -0
  465. data/app/workflows/command_tower/workflows/messaging/execution/deliver_workflow.rb +234 -0
  466. data/app/workflows/command_tower/workflows/messaging/handoff/process_workflow.rb +90 -0
  467. data/app/workflows/command_tower/workflows/messaging/inbox.rb +97 -0
  468. data/app/workflows/command_tower/workflows/messaging/preferences/show_workflow.rb +31 -0
  469. data/app/workflows/command_tower/workflows/messaging/preferences/update_workflow.rb +37 -0
  470. data/app/workflows/command_tower/workflows/profile/show_workflow.rb +27 -0
  471. data/app/workflows/command_tower/workflows/workflow_result.rb +34 -0
  472. data/config/routes.rb +85 -37
  473. data/db/migrate/{20241117043720_create_command_tower_users.rb → 20260805000001_create_command_tower_users.rb} +7 -9
  474. data/db/migrate/{20241204065708_create_command_tower_user_secrets.rb → 20260805000002_create_command_tower_user_secrets.rb} +3 -0
  475. data/db/migrate/20260805000003_create_command_tower_messaging_core.rb +90 -0
  476. data/db/migrate/20260805000004_create_messaging_notification_preferences.rb +17 -0
  477. data/db/migrate/20260805000005_create_messaging_endpoints_and_pushover_credentials.rb +72 -0
  478. data/docs/api_reference.md +438 -0
  479. data/docs/architecture.md +30 -0
  480. data/docs/authentication.md +37 -0
  481. data/docs/authentication_authorization_guide.md +150 -0
  482. data/docs/authorization.md +34 -0
  483. data/docs/change_password_workflow.md +67 -0
  484. data/docs/controllers.md +35 -0
  485. data/docs/cookie_authentication_guide.md +167 -0
  486. data/docs/email_verification_workflow.md +58 -0
  487. data/docs/extending.md +105 -0
  488. data/docs/host_integration_guide.md +158 -0
  489. data/docs/initializing.md +169 -0
  490. data/docs/messaging_integration_guide.md +87 -0
  491. data/docs/models.md +51 -0
  492. data/docs/pagination.md +47 -0
  493. data/docs/password_reset_workflow.md +82 -0
  494. data/docs/sensitive_routes.md +47 -0
  495. data/docs/testing.md +74 -0
  496. data/docs/upgrades/0.10.0.md +58 -0
  497. data/docs/upgrades/README.md +9 -0
  498. data/lib/command_tower/authorization/default.yml +5 -33
  499. data/lib/command_tower/configuration/application/config.rb +2 -2
  500. data/lib/command_tower/configuration/base.rb +0 -1
  501. data/lib/command_tower/configuration/config.rb +41 -5
  502. data/lib/command_tower/configuration/credentials/config.rb +33 -0
  503. data/lib/command_tower/configuration/credentials/smtp.rb +33 -0
  504. data/lib/command_tower/configuration/credentials/twilio.rb +33 -0
  505. data/lib/command_tower/configuration/email/config.rb +1 -18
  506. data/lib/command_tower/configuration/identity/config.rb +19 -0
  507. data/lib/command_tower/configuration/identity/phone_verification.rb +54 -0
  508. data/lib/command_tower/configuration/jwt/config.rb +7 -0
  509. data/lib/command_tower/configuration/jwt/cookie/config.rb +64 -0
  510. data/lib/command_tower/configuration/jwt/cookie/csrf/config.rb +72 -0
  511. data/lib/command_tower/configuration/login/strategy/plain_text/config.rb +6 -0
  512. data/lib/command_tower/configuration/login/strategy/plain_text/email_verify.rb +5 -0
  513. data/lib/command_tower/configuration/login/strategy/plain_text/password_reset.rb +49 -0
  514. data/lib/command_tower/configuration/messaging/config.rb +60 -0
  515. data/lib/command_tower/configuration/messaging/pushover.rb +42 -0
  516. data/lib/command_tower/configuration/messaging/sms.rb +32 -0
  517. data/lib/command_tower/configuration/pagination/config.rb +16 -0
  518. data/lib/command_tower/configuration/password_recovery_session/config.rb +61 -0
  519. data/lib/command_tower/configuration/password_recovery_session/rate_limits.rb +33 -0
  520. data/lib/command_tower/configuration/signup_session/config.rb +67 -0
  521. data/lib/command_tower/configuration/signup_session/email_availability.rb +18 -0
  522. data/lib/command_tower/configuration/signup_session/rate_limits.rb +48 -0
  523. data/lib/command_tower/configuration/user/config.rb +6 -5
  524. data/lib/command_tower/credential_resolution/env_backend.rb +25 -0
  525. data/lib/command_tower/credential_resolution/smtp_action_mailer_bridge.rb +24 -0
  526. data/lib/command_tower/credential_resolution/smtp_credentials.rb +26 -0
  527. data/lib/command_tower/credential_resolution/twilio_credentials.rb +26 -0
  528. data/lib/command_tower/credential_resolution.rb +78 -0
  529. data/lib/command_tower/current.rb +7 -0
  530. data/lib/command_tower/engine.rb +4 -1
  531. data/lib/command_tower/install/baseline.rb +31 -0
  532. data/lib/command_tower/install/doctor.rb +227 -0
  533. data/lib/command_tower/jwt/authorization_helper.rb +136 -0
  534. data/lib/command_tower/jwt/csrf_helper.rb +138 -0
  535. data/lib/command_tower/password_recovery/authorization_helper.rb +25 -0
  536. data/lib/command_tower/redis_connection.rb +21 -0
  537. data/lib/command_tower/schema/error/email_validation_required.rb +15 -0
  538. data/lib/command_tower/schema/shared/user.rb +32 -0
  539. data/lib/command_tower/schema.rb +2 -25
  540. data/lib/command_tower/signup/authorization_helper.rb +25 -0
  541. data/lib/command_tower/spec_helper.rb +58 -1
  542. data/lib/command_tower/testing.rb +62 -0
  543. data/lib/command_tower/version.rb +1 -1
  544. data/lib/command_tower.rb +20 -0
  545. data/lib/generators/command_tower/configure/USAGE +20 -0
  546. data/lib/generators/command_tower/configure/configure_generator.rb +39 -0
  547. data/lib/tasks/auto_annotate_models.rake +23 -55
  548. data/lib/tasks/install.rake +81 -0
  549. data/lib/tasks/users.rake +119 -0
  550. data/spec/factories/entity.rb +37 -0
  551. data/spec/factories/messaging.rb +156 -0
  552. data/spec/factories/role.rb +29 -0
  553. data/spec/factories/user.rb +53 -0
  554. data/spec/factories/user_secret.rb +12 -0
  555. metadata +579 -53
  556. data/app/controllers/command_tower/admin_controller.rb +0 -104
  557. data/app/controllers/command_tower/auth/plain_text_controller.rb +0 -132
  558. data/app/controllers/command_tower/inbox/message_blast_controller.rb +0 -89
  559. data/app/controllers/command_tower/inbox/message_controller.rb +0 -79
  560. data/app/controllers/command_tower/user_controller.rb +0 -49
  561. data/app/controllers/command_tower/username_controller.rb +0 -26
  562. data/app/helpers/command_tower/schema_helper.rb +0 -29
  563. data/app/models/message.rb +0 -30
  564. data/app/models/message_blast.rb +0 -27
  565. data/app/services/command_tower/inbox_service/blast/delete.rb +0 -23
  566. data/app/services/command_tower/inbox_service/blast/metadata.rb +0 -26
  567. data/app/services/command_tower/inbox_service/blast/new_user_blaster.rb +0 -24
  568. data/app/services/command_tower/inbox_service/blast/retrieve.rb +0 -30
  569. data/app/services/command_tower/inbox_service/blast/upsert.rb +0 -67
  570. data/app/services/command_tower/inbox_service/message/metadata.rb +0 -35
  571. data/app/services/command_tower/inbox_service/message/modify.rb +0 -44
  572. data/app/services/command_tower/inbox_service/message/retrieve.rb +0 -36
  573. data/app/services/command_tower/inbox_service/message/send.rb +0 -33
  574. data/app/services/command_tower/jwt/time_delay_token.rb +0 -17
  575. data/app/services/command_tower/login_strategy/plain_text/create.rb +0 -43
  576. data/app/services/command_tower/login_strategy/plain_text/email_verification/generate.rb +0 -29
  577. data/app/services/command_tower/login_strategy/plain_text/email_verification/send.rb +0 -23
  578. data/app/services/command_tower/login_strategy/plain_text/email_verification/verify.rb +0 -24
  579. data/app/services/command_tower/login_strategy/plain_text/login.rb +0 -50
  580. data/app/services/command_tower/user_attributes/modify.rb +0 -68
  581. data/app/services/command_tower/user_attributes/roles.rb +0 -27
  582. data/db/migrate/20250223023306_create_command_tower_messages.rb +0 -12
  583. data/db/migrate/20250223023313_create_command_tower_message_blasts.rb +0 -14
  584. data/lib/command_tower/schema/admin/users.rb +0 -15
  585. data/lib/command_tower/schema/inbox/blast_request.rb +0 -15
  586. data/lib/command_tower/schema/inbox/blast_response.rb +0 -16
  587. data/lib/command_tower/schema/inbox/message_blast_entity.rb +0 -16
  588. data/lib/command_tower/schema/inbox/message_blast_metadata.rb +0 -16
  589. data/lib/command_tower/schema/inbox/message_entity.rb +0 -14
  590. data/lib/command_tower/schema/inbox/metadata.rb +0 -18
  591. data/lib/command_tower/schema/inbox/modified.rb +0 -13
  592. data/lib/command_tower/schema/page.rb +0 -14
  593. data/lib/command_tower/schema/plain_text/create_user_request.rb +0 -18
  594. data/lib/command_tower/schema/plain_text/create_user_response.rb +0 -17
  595. data/lib/command_tower/schema/plain_text/email_verify_request.rb +0 -11
  596. data/lib/command_tower/schema/plain_text/email_verify_response.rb +0 -11
  597. data/lib/command_tower/schema/plain_text/email_verify_send_request.rb +0 -9
  598. data/lib/command_tower/schema/plain_text/email_verify_send_response.rb +0 -11
  599. data/lib/command_tower/schema/plain_text/login_request.rb +0 -15
  600. data/lib/command_tower/schema/plain_text/login_response.rb +0 -13
  601. data/lib/command_tower/schema/user.rb +0 -28
  602. data/lib/generators/api_engine_base/configure/USAGE +0 -8
  603. data/lib/generators/api_engine_base/configure/configure_generator.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4f9f21fbcb6005b0181cb027daaf67f3df5a2a905cbe199b0b8e7bf1f252e72
4
- data.tar.gz: d1e6e49ef8ff63818e0c80bb39352087fc6a448a70b264b8789e9851b93d3687
3
+ metadata.gz: 726d34845596392ec87fbeacf508323aa72fbfa73d87094e40f0747595450ae9
4
+ data.tar.gz: 7f1ff986df01e27008e33867e80039a406534e57801111334e3e3c3798705e13
5
5
  SHA512:
6
- metadata.gz: 2b9751dcdb8d2ed1f970408e296fe2640a8af1bccf8eb5e834745779cb6f201984f29ca1a8a9e10240e477b6ec12bf4a25703cb5a77e1a577633658184e3897e
7
- data.tar.gz: 323c22c584f970e4ff187c6eaba3a66302248242f505eb9470687d704b77bc554fdf0eaa074d1087f8014e0ee9b046786b1512befbf7b3db74263ee8db95fe00
6
+ metadata.gz: 065535eb260884b0982e56e94c071d2d0707133a62e033d74db8c0d3eaed6c5629b924c8a4a27c007cbb9636aee5198070ef7b3a395cde9a77b1c0bb9661a4e0
7
+ data.tar.gz: dfd1337b02b1a6350a8ab5ba16316b77b219a47a383f294f42404dbb78f522f13cc0996e045a2dc9e758f4db0fd05f67bc80cfa2ebd6d9fc970e7db48508a211
data/README.md CHANGED
@@ -1,59 +1,135 @@
1
1
  # CommandTower
2
- This is an API only base engine to build on top of. This Engine takes care of all Authentication, Token Refresh, and RBAC Roles so that you do not have to! For all applications, you can get right to work on implementing the code directly related to your project rather than dealing with the administrative overhead.
3
2
 
4
- While this gem is heavily opinionated, everything can be configured to your liking.
3
+ CommandTower is a mountable **Rails engine** that provides shared platform capabilities for host applications: JWT authentication, RBAC authorization, account/Me surfaces, messaging HTTP (inbox, preferences, endpoints), admin announcements, and shared test factories.
5
4
 
6
- ## Installation
7
- Add this line to your application's Gemfile:
5
+ Hosts mount the engine, configure secrets and product policy, and build product features on top—without re-implementing platform foundations.
6
+
7
+ The engine is opinionated and configurable. Architecture follows a stable layer map (workflows orchestrate; services implement capabilities). See [Architecture](docs/architecture.md).
8
+
9
+ ## Quick start
8
10
 
9
11
  ```ruby
12
+ # Gemfile
10
13
  gem "command_tower"
11
14
  ```
12
15
 
13
- And then execute:
14
16
  ```bash
15
- $ bundle
17
+ bundle install
18
+ bin/rails command_tower:install
19
+ bin/rails db:migrate
20
+ bin/rails command_tower:doctor
16
21
  ```
17
22
 
18
- Or install it yourself as:
19
- ```bash
20
- $ gem install command_tower
23
+ Full install, upgrade, configuration, and troubleshooting: [Initializing CommandTower](docs/initializing.md).
24
+
25
+ After install, complete RBAC, feature gates, and a Me smoke check: [Host integration guide](docs/host_integration_guide.md) (**start here for a new Rails host**).
26
+
27
+ ## Architecture
28
+
29
+ ```text
30
+ Controller / Job → Workflow → Shared Sequences / Services → Models & Clients
31
+ ```
32
+
33
+ - Controllers are transport adapters (one workflow per action).
34
+ - Workflows orchestrate a business action.
35
+ - Services (including `CommandTower::ServiceBase`) implement one capability.
36
+
37
+ See [Architecture](docs/architecture.md) and [ServiceBase](app/services/command_tower/README.md).
38
+
39
+ ## Documentation
40
+
41
+ | Guide | Purpose |
42
+ |-------|---------|
43
+ | [Host integration](docs/host_integration_guide.md) | **Start here** — step-by-step new Rails host path |
44
+ | [Upgrades](docs/upgrades/README.md) | Release upgrade summaries (see [0.10.0](docs/upgrades/0.10.0.md)) |
45
+ | [Initializing](docs/initializing.md) | Install, configure, migrate, doctor, upgrades |
46
+ | [Testing](docs/testing.md) | Shared FactoryBot / `CommandTower::Testing` |
47
+ | [Architecture](docs/architecture.md) | Layer map for hosts and contributors |
48
+ | [Controllers / routes](docs/controllers.md) | Engine route areas (index) |
49
+ | [API reference](docs/api_reference.md) | Endpoint catalog (detailed contracts) |
50
+ | [Extending](docs/extending.md) | Stable extension points / do-not-extend |
51
+ | [Models](docs/models.md) | `User`, `UserSecret`, reopening classes |
52
+ | [Authentication](docs/authentication.md) | JWT auth quick start |
53
+ | [Authorization](docs/authorization.md) | RBAC quick start |
54
+ | [Authentication & authorization guide](docs/authentication_authorization_guide.md) | Deep authn + RBAC |
55
+ | [Cookie authentication](docs/cookie_authentication_guide.md) | HttpOnly cookies, CORS, CSRF |
56
+ | [Sensitive changes](docs/sensitive_routes.md) | Verifier token / session invalidation |
57
+ | [Messaging](docs/messaging_integration_guide.md) | Current messaging surfaces |
58
+ | [Pagination](docs/pagination.md) | Pagination helpers |
59
+ | [ServiceBase](app/services/command_tower/README.md) | Service capability base |
60
+ | [Password reset workflow](docs/password_reset_workflow.md) | Forgot-password flow |
61
+ | [Change password workflow](docs/change_password_workflow.md) | Authenticated password change |
62
+ | [Email verification workflow](docs/email_verification_workflow.md) | Email verification flow |
63
+
64
+ ## Installation and configuration
65
+
66
+ See [Initializing CommandTower](docs/initializing.md) for:
67
+
68
+ - `command_tower:install` / `install:migrations`
69
+ - Configure generator flags (`SKIP_CONFIGURE`, `SKIP_MOUNT`, `FORCE`)
70
+ - Schema ownership (**AD-SCH-01**)
71
+ - Secrets and doctor checks
72
+ - Existing customized hosts
73
+
74
+ ## Testing
75
+
76
+ Shared factories for hosts:
77
+
78
+ ```ruby
79
+ require "command_tower/testing"
80
+ CommandTower::Testing.install!
21
81
  ```
22
82
 
23
- ## Initializing CommandTower
24
- Please follow all steps in [Initializing CommandTower](docs/initializing.md)
83
+ Details: [Testing with CommandTower](docs/testing.md).
84
+
85
+ Engine HTTP coverage lives primarily under `spec/requests/`. Residual journey coverage may remain under `spec/integration_test/`.
25
86
 
87
+ ## Routes
26
88
 
27
- ## Available Routes
89
+ Engine routes cover Auth, Me (profile, inbox, preferences, phone, Pushover), and Admin messaging announcements. Index: [Controllers](docs/controllers.md). Contracts: [API reference](docs/api_reference.md).
28
90
 
29
- For more info, check out [Controllers ReadMe](docs/controllers.md)
91
+ ## Models
30
92
 
31
- Additionally, You can check out [RSpec Integration Testing](/spec/integration_test)
93
+ Core models such as `User` and `UserSecret` are available for hosts to use and reopen carefully. See [Models](docs/models.md).
32
94
 
33
- ## Available Models
95
+ ## Authentication (JWT)
34
96
 
35
- CommandTower provides several Models at the in the root namespace. Core Models like `User` and `UserSecret` are readily available. Don't forget! You can add additional methods to these classes by opening them back up.
97
+ Authentication establishes identity (`401` when it fails).
36
98
 
37
- For more info, check out [Models ReadMe](doc/models.md)
99
+ - **Engine controllers** use `authenticate_request!` / `authorize_request!` (Authentication/Authorization boundaries) and return the application envelope.
100
+ - **Host controllers** may use provisional `before_action :authenticate_user!` (failure shapes can differ — see the deep guide).
38
101
 
39
- ## Authentication (JWT BearerToken)
40
- Authentication ensures that we know which user is requesting the action. When the Engine is unable to authenticate, a `401` status code is returned.
102
+ - Quick start: [Authentication](docs/authentication.md)
103
+ - Deep guide: [Authentication & authorization](docs/authentication_authorization_guide.md)
104
+ - Web / SPA cookies: [Cookie authentication](docs/cookie_authentication_guide.md)
41
105
 
42
- For more info, check out [Authentication ReadMe](docs/authentication.md)
106
+ Cookie mode supports HttpOnly JWT cookies, SameSite, optional double-submit CSRF, and secure cookies in production.
43
107
 
44
108
  ## Authorization (RBAC)
45
- Authorization is only done after authentication. This is the act of ensuring that the user can perform the action it is requesting. Put differently, I know who you are, but I need to validate you have permissions to complete the action. When the engine is unable to authorize the user, a `403` status code is returned.
46
109
 
47
- For more info, check out [Authentication ReadMe](docs/authorization.md)
110
+ Authorization establishes permission after authentication (`403` when it fails).
111
+
112
+ Engine defaults ship `owner` and `admin` (announcements). Hosts **must** supply `rbac_groups.yml` entities for Me/Auth surfaces (fail-closed). Host controllers may use provisional `authorize_user!` after `authenticate_user!`.
113
+
114
+ - Quick start: [Authorization](docs/authorization.md)
115
+ - Deep guide: [Authentication & authorization](docs/authentication_authorization_guide.md)
116
+
117
+ ## Sensitive changes
48
118
 
49
- ## Sensitive Changes
119
+ JWT tokens embed a `verifier_token` bound to the user. Rotating the verifier invalidates outstanding sessions. See [Sensitive changes](docs/sensitive_routes.md).
50
120
 
51
- For more info, check out [Sensitive Routes](docs/sensitive_routes.md)
121
+ ## Messaging
52
122
 
53
- ## ServiceBase
54
- ServiceBase is built on top of Interactor. The ServiceBase is the heart of all logic for CommandTower. It includes Logging and enhanced ArgumentValidation that can directly return back to the API request.
123
+ Modern messaging surfaces: Me Inbox consume, Produce / ProduceMany emit, admin announcements. See [Messaging](docs/messaging_integration_guide.md).
55
124
 
56
- For more info, check out [ServiceBase ReadMe](app/services/command_tower/README.md)
125
+ ## Pagination
126
+
127
+ Pagination helpers are available for controllers and services. See [Pagination](docs/pagination.md).
128
+
129
+ ## Services
130
+
131
+ `CommandTower::ServiceBase` adds logging and argument validation for **service** classes under workflows. See [ServiceBase](app/services/command_tower/README.md).
57
132
 
58
133
  ## License
59
- The engine is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
134
+
135
+ MIT — see [MIT-LICENSE](MIT-LICENSE).
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ AuthContext = Data.define(
6
+ :user,
7
+ :token_expires_at,
8
+ :token_source,
9
+ :roles,
10
+ :principal_type,
11
+ :generated_token
12
+ ) do
13
+ def initialize(user:, token_expires_at:, token_source:, roles:, principal_type:, generated_token: nil)
14
+ super(
15
+ user:,
16
+ token_expires_at:,
17
+ token_source:,
18
+ roles:,
19
+ principal_type:,
20
+ generated_token:
21
+ )
22
+ end
23
+
24
+ def self.from_authenticate_session_result(data:, metadata:)
25
+ new(
26
+ user: data[:user],
27
+ token_expires_at: data[:token_expires_at],
28
+ token_source: metadata[:token_source],
29
+ roles: data[:user].roles,
30
+ principal_type: :user,
31
+ generated_token: data[:generated_token]
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ PasswordRecoverySessionContext = Data.define(:jti, :expires_at, :client_ip) do
6
+ def remaining_seconds
7
+ [expires_at.to_i - Time.now.to_i, 0].max
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ SignupSessionContext = Data.define(:jti, :expires_at, :client_ip) do
6
+ def remaining_seconds
7
+ [expires_at.to_i - Time.now.to_i, 0].max
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Admin
5
+ module Messaging
6
+ class AnnouncementsController < CommandTower::ApplicationController
7
+ include CommandTower::Auth::AuthenticationBoundary
8
+ include CommandTower::Auth::AuthorizationBoundary
9
+ include CommandTower::Api::ApplicationResponseRenderer
10
+
11
+ before_action :authenticate_request!
12
+ before_action :authorize_request!
13
+
14
+ def create
15
+ deserialized = CommandTower::Deserializers::Admin::Messaging::CreateAnnouncementDeserializer.call(params)
16
+ unless deserialized.success?
17
+ return render_application_result(
18
+ CommandTower::Workflows::WorkflowResult.failure(
19
+ errors: [CommandTower::Errors::ValidationError.new(details: { base: "Invalid request parameters" })],
20
+ http_status: :unprocessable_entity
21
+ )
22
+ )
23
+ end
24
+
25
+ render_application_result(
26
+ CommandTower::Workflows::Admin::Messaging::CreateAnnouncementWorkflow.call(
27
+ input: deserialized.input
28
+ )
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module CommandTower
4
4
  class ApplicationController < ActionController::API
5
- AUTHENTICATION_HEADER = "Authentication"
6
- AUTHENTICATION_EXPIRE_HEADER = "X-Authentication-Expire"
7
- AUTHENTICATION_WITH_RESET = "X-Authentication-Reset"
5
+ AUTHENTICATION_HEADER = CommandTower::Jwt::AuthorizationHelper::AUTHENTICATION_HEADER
6
+ AUTHENTICATION_EXPIRE_HEADER = CommandTower::Jwt::AuthorizationHelper::AUTHENTICATION_EXPIRE_HEADER
7
+ AUTHENTICATION_WITH_RESET = CommandTower::Jwt::AuthorizationHelper::AUTHENTICATION_WITH_RESET
8
8
 
9
9
  def safe_boolean(value:)
10
10
  return nil unless [true, false, "true", "false", "0", "1", 0, 1].include?(value)
@@ -13,30 +13,84 @@ module CommandTower
13
13
  end
14
14
 
15
15
  ###
16
- # Authenticate user via the passed in header
17
- # AUTHENTICATION_HEADER="Bearer: {token value}"
16
+ # Authenticate user via the passed in header or cookie
17
+ # AUTHENTICATION_HEADER="Bearer {token value}" or cookie (if enabled)
18
18
  def authenticate_user!(bypass_email_validation: false)
19
- raw_token = request.headers[AUTHENTICATION_HEADER]
20
- if raw_token.nil?
19
+ # Extract and validate token (from header or cookie fallback)
20
+ token_data = CommandTower::Jwt::AuthorizationHelper.extract_token(request)
21
+ if token_data[:error]
21
22
  status = 401
22
- schema = CommandTower::Schema::Error::Base.new(status:, message: "Bearer token missing")
23
+ schema = CommandTower::Schema::Error::Base.new(status:, message: token_data[:message])
23
24
  render(json: schema.to_h, status:)
24
25
  return false
25
26
  end
26
27
 
27
- token = raw_token.split("Bearer:")[1].strip
28
- with_reset = safe_boolean(value: request.headers[AUTHENTICATION_WITH_RESET])
28
+ token = token_data[:token]
29
+ token_source = token_data[:source]
30
+
31
+ # CSRF validation for cookie-authenticated unsafe requests
32
+ # Enforcement is centralized here because token_source is only reliably known in this shared filter
33
+ if csrf_required?(request, token_source)
34
+ csrf_validation = CommandTower::Jwt::CsrfHelper.validate(request)
35
+ unless csrf_validation[:valid]
36
+ status = 403
37
+ schema = CommandTower::Schema::Error::Base.new(
38
+ status: status.to_s,
39
+ message: csrf_validation[:message] # Stable error code: "csrf_missing" or "csrf_mismatch"
40
+ )
41
+ render(json: schema.to_h, status: status)
42
+ return false
43
+ end
44
+ end
45
+
46
+ with_reset = CommandTower::Jwt::AuthorizationHelper.get_with_reset_flag(request)
29
47
  result = CommandTower::Jwt::AuthenticateUser.(token:, bypass_email_validation:, with_reset:)
30
48
  if result.success?
31
49
  @current_user = result.user
32
- response.set_header(AUTHENTICATION_EXPIRE_HEADER, result.expires_at)
33
50
  if with_reset
34
- response.set_header(AUTHENTICATION_WITH_RESET, result.generated_token)
51
+ # Use helper to set both headers and cookie (if enabled)
52
+ CommandTower::Jwt::AuthorizationHelper.set_token(
53
+ response,
54
+ result.generated_token,
55
+ expires_at: result.expires_at
56
+ )
57
+
58
+ # Handle CSRF cookie issuance/rotation for token reset path
59
+ # CSRF cookie issuance rules:
60
+ # - rotate_on_reset = true → force rotate (generate new token)
61
+ # - rotate_on_reset = false → ensure exists only (create if missing, keep if exists)
62
+ if CommandTower::Jwt::CsrfHelper.csrf_enabled?
63
+ if CommandTower.config.jwt.cookie.csrf.rotate_on_reset
64
+ CommandTower::Jwt::CsrfHelper.ensure_cookie(request, response, should_rotate: true)
65
+ else
66
+ # Ensure cookie exists (rotate-or-ensure model)
67
+ CommandTower::Jwt::CsrfHelper.ensure_cookie(request, response, should_rotate: false)
68
+ end
69
+ end
70
+ else
71
+ # Only set expire header when not resetting
72
+ response.set_header(AUTHENTICATION_EXPIRE_HEADER, result.expires_at)
35
73
  end
36
74
  true
37
75
  else
38
- status = 401
39
- schema = CommandTower::Schema::Error::Base.new(status:, message: result.msg)
76
+ # Check if this is an email validation failure (status 412)
77
+ if result.status == 412
78
+ status = 412
79
+ schema = CommandTower::Schema::Error::EmailValidationRequired.new(
80
+ status:,
81
+ message: result.msg,
82
+ meta: { email_validated: false }
83
+ )
84
+ # Do NOT clear cookie for 412 status - user needs to keep cookie to verify email
85
+ else
86
+ # If authentication failed and token came from cookie, clear the invalid cookie
87
+ # Exception: Do not clear cookie for 412 status (email validation required)
88
+ if token_source == :cookie
89
+ CommandTower::Jwt::AuthorizationHelper.clear_cookie(response)
90
+ end
91
+ status = result.status || 401
92
+ schema = CommandTower::Schema::Error::Base.new(status:, message: result.msg)
93
+ end
40
94
  render(json: schema.to_h, status:)
41
95
  # Must return false so callbacks know to halt propagation
42
96
  false
@@ -77,5 +131,17 @@ module CommandTower
77
131
  def current_user
78
132
  @current_user ||= nil
79
133
  end
134
+
135
+ private
136
+
137
+ def csrf_required?(request, token_source)
138
+ return false unless CommandTower::Jwt::CsrfHelper.csrf_enabled?
139
+ # CRITICAL: Check token_source, not cookie presence
140
+ return false unless token_source == :cookie
141
+ return false if request.get? || request.head? || request.options?
142
+
143
+ # Unsafe methods: POST, PUT, PATCH, DELETE
144
+ %w[POST PUT PATCH DELETE].include?(request.method)
145
+ end
80
146
  end
81
147
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ module Email
6
+ class AvailabilityController < CommandTower::ApplicationController
7
+ include CommandTower::Auth::SignupSessionBoundary
8
+
9
+ before_action :authenticate_signup_session!
10
+
11
+ def show
12
+ deserialized = CommandTower::Deserializers::Auth::EmailAvailabilityDeserializer.call(params)
13
+ return render_email_deserializer_errors unless deserialized.success?
14
+
15
+ result = CommandTower::Workflows::Auth::EmailAvailabilityWorkflow.call(
16
+ input: deserialized.input,
17
+ signup_session: current_signup_session
18
+ )
19
+ render_application_result(result)
20
+ end
21
+
22
+ private
23
+
24
+ def render_email_deserializer_errors
25
+ render_application_result(
26
+ CommandTower::Workflows::WorkflowResult.failure(
27
+ errors: [CommandTower::Errors::ValidationError.new(details: { email: "Email is required" })],
28
+ http_status: :unprocessable_entity
29
+ )
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ module EmailVerification
6
+ class SendController < CommandTower::ApplicationController
7
+ include CommandTower::Auth::AuthenticationBoundary
8
+ include CommandTower::Auth::AuthorizationBoundary
9
+
10
+ # Requesting a verification code is exactly the action an unverified user
11
+ # needs, so the email verification precondition is bypassed here.
12
+ before_action -> { authenticate_request!(bypass_email_validation: true) }
13
+ before_action :authorize_request!
14
+
15
+ def create
16
+ result = CommandTower::Workflows::Auth::EmailVerification::SendWorkflow.call(
17
+ current_user: current_user
18
+ )
19
+ render_application_result(result)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ module EmailVerification
6
+ class VerifyController < CommandTower::ApplicationController
7
+ include CommandTower::Auth::AuthenticationBoundary
8
+ include CommandTower::Auth::AuthorizationBoundary
9
+
10
+ before_action -> { authenticate_request!(bypass_email_validation: true) }
11
+ before_action :authorize_request!
12
+
13
+ def create
14
+ deserialized = CommandTower::Deserializers::Auth::EmailVerification::VerifyDeserializer.call(params)
15
+ return render_verify_deserializer_errors unless deserialized.success?
16
+
17
+ result = CommandTower::Workflows::Auth::EmailVerification::VerifyWorkflow.call(
18
+ current_user: current_user,
19
+ input: deserialized.input
20
+ )
21
+ render_application_result(result)
22
+ end
23
+
24
+ private
25
+
26
+ def render_verify_deserializer_errors
27
+ render_application_result(
28
+ CommandTower::Workflows::WorkflowResult.failure(
29
+ errors: [
30
+ CommandTower::Errors::Auth::VerificationCodeInvalidError.new(
31
+ details: { code: "Verification code is required" }
32
+ )
33
+ ],
34
+ http_status: :unprocessable_entity
35
+ )
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ class IdentityPolicyController < CommandTower::ApplicationController
6
+ include CommandTower::Api::ApplicationResponseRenderer
7
+
8
+ def show
9
+ result = CommandTower::Workflows::Auth::IdentityPolicyWorkflow.call
10
+ render_application_result(result)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ class LogoutController < CommandTower::ApplicationController
6
+ include CommandTower::Api::ApplicationResponseRenderer
7
+
8
+ def create
9
+ result = CommandTower::Workflows::Auth::LogoutWorkflow.call
10
+ render_application_result(result)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ module PasswordRecoverySession
6
+ class CreateController < CommandTower::ApplicationController
7
+ include CommandTower::Api::ApplicationResponseRenderer
8
+
9
+ def create
10
+ client_ip = CommandTower::Services::Auth::ClientIpResolver.call(request: request)
11
+ result = CommandTower::Workflows::Auth::PasswordRecoverySession::CreateWorkflow.call(client_ip: client_ip)
12
+ render_application_result(result)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ module PasswordReset
6
+ class ResetController < CommandTower::ApplicationController
7
+ include CommandTower::Api::ApplicationResponseRenderer
8
+ include CommandTower::Api::DeserializerFailureDetails
9
+
10
+ def create
11
+ deserialized = CommandTower::Deserializers::Auth::PasswordReset::ResetDeserializer.call(params)
12
+ return render_reset_deserializer_errors(deserialized) unless deserialized.success?
13
+
14
+ result = CommandTower::Workflows::Auth::PasswordReset::ResetWorkflow.call(input: deserialized.input)
15
+ render_application_result(result)
16
+ end
17
+
18
+ private
19
+
20
+ def render_reset_deserializer_errors(deserialized)
21
+ render_application_result(
22
+ CommandTower::Workflows::WorkflowResult.failure(
23
+ errors: [
24
+ CommandTower::Errors::ValidationError.new(
25
+ details: deserializer_failure_details(deserialized)
26
+ )
27
+ ],
28
+ http_status: :unprocessable_entity
29
+ )
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ module PasswordReset
6
+ class SendController < CommandTower::ApplicationController
7
+ include CommandTower::Auth::PasswordRecoverySessionBoundary
8
+ include CommandTower::Api::DeserializerFailureDetails
9
+
10
+ before_action :authenticate_password_recovery_session!
11
+
12
+ def create
13
+ deserialized = CommandTower::Deserializers::Auth::PasswordReset::SendDeserializer.call(params)
14
+ return render_send_deserializer_errors(deserialized) unless deserialized.success?
15
+
16
+ result = CommandTower::Workflows::Auth::PasswordReset::SendWorkflow.call(
17
+ input: deserialized.input,
18
+ password_recovery_session: current_password_recovery_session
19
+ )
20
+ render_application_result(result)
21
+ end
22
+
23
+ private
24
+
25
+ def render_send_deserializer_errors(deserialized)
26
+ render_application_result(
27
+ CommandTower::Workflows::WorkflowResult.failure(
28
+ errors: [
29
+ CommandTower::Errors::ValidationError.new(
30
+ details: deserializer_failure_details(deserialized)
31
+ )
32
+ ],
33
+ http_status: :unprocessable_entity
34
+ )
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module Auth
5
+ module PasswordReset
6
+ class ValidateController < CommandTower::ApplicationController
7
+ include CommandTower::Api::ApplicationResponseRenderer
8
+ include CommandTower::Api::DeserializerFailureDetails
9
+
10
+ def create
11
+ deserialized = CommandTower::Deserializers::Auth::PasswordReset::ValidateDeserializer.call(params)
12
+ return render_validate_deserializer_errors(deserialized) unless deserialized.success?
13
+
14
+ result = CommandTower::Workflows::Auth::PasswordReset::ValidateWorkflow.call(input: deserialized.input)
15
+ render_application_result(result)
16
+ end
17
+
18
+ private
19
+
20
+ def render_validate_deserializer_errors(deserialized)
21
+ render_application_result(
22
+ CommandTower::Workflows::WorkflowResult.failure(
23
+ errors: [
24
+ CommandTower::Errors::ValidationError.new(
25
+ details: deserializer_failure_details(deserialized)
26
+ )
27
+ ],
28
+ http_status: :unprocessable_entity
29
+ )
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end