tasker-engine 0.1.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 (601) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +440 -0
  4. data/Rakefile +10 -0
  5. data/app/controllers/tasker/analytics_controller.rb +179 -0
  6. data/app/controllers/tasker/application_controller.rb +45 -0
  7. data/app/controllers/tasker/graphql_controller.rb +193 -0
  8. data/app/controllers/tasker/handlers_controller.rb +217 -0
  9. data/app/controllers/tasker/health_controller.rb +229 -0
  10. data/app/controllers/tasker/metrics_controller.rb +111 -0
  11. data/app/controllers/tasker/page_sort.rb +97 -0
  12. data/app/controllers/tasker/tasks_controller.rb +123 -0
  13. data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
  14. data/app/graphql/examples/all_tasks.graphql +22 -0
  15. data/app/graphql/examples/pending_tasks.graphql +23 -0
  16. data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
  17. data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
  18. data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
  19. data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
  20. data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
  21. data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
  22. data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
  23. data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
  24. data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
  25. data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
  26. data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
  27. data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
  28. data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
  29. data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
  30. data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
  31. data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
  32. data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
  33. data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
  34. data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
  35. data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
  36. data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
  37. data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
  38. data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
  39. data/app/graphql/tasker/graph_ql_types.rb +42 -0
  40. data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
  41. data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
  42. data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
  43. data/app/graphql/tasker/mutations/create_task.rb +52 -0
  44. data/app/graphql/tasker/mutations/update_step.rb +36 -0
  45. data/app/graphql/tasker/mutations/update_task.rb +41 -0
  46. data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
  47. data/app/graphql/tasker/queries/all_tasks.rb +23 -0
  48. data/app/graphql/tasker/queries/base_query.rb +9 -0
  49. data/app/graphql/tasker/queries/helpers.rb +16 -0
  50. data/app/graphql/tasker/queries/one_step.rb +24 -0
  51. data/app/graphql/tasker/queries/one_task.rb +18 -0
  52. data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
  53. data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
  54. data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
  55. data/app/jobs/tasker/application_job.rb +8 -0
  56. data/app/jobs/tasker/metrics_export_job.rb +252 -0
  57. data/app/jobs/tasker/task_runner_job.rb +224 -0
  58. data/app/models/tasker/annotation_type.rb +26 -0
  59. data/app/models/tasker/application_record.rb +70 -0
  60. data/app/models/tasker/dependent_system.rb +26 -0
  61. data/app/models/tasker/dependent_system_object_map.rb +64 -0
  62. data/app/models/tasker/named_step.rb +41 -0
  63. data/app/models/tasker/named_task.rb +121 -0
  64. data/app/models/tasker/named_tasks_named_step.rb +82 -0
  65. data/app/models/tasker/step_dag_relationship.rb +65 -0
  66. data/app/models/tasker/step_readiness_status.rb +59 -0
  67. data/app/models/tasker/task.rb +414 -0
  68. data/app/models/tasker/task_annotation.rb +36 -0
  69. data/app/models/tasker/task_execution_context.rb +29 -0
  70. data/app/models/tasker/task_namespace.rb +41 -0
  71. data/app/models/tasker/task_transition.rb +235 -0
  72. data/app/models/tasker/workflow_step.rb +461 -0
  73. data/app/models/tasker/workflow_step_edge.rb +95 -0
  74. data/app/models/tasker/workflow_step_transition.rb +434 -0
  75. data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
  76. data/app/serializers/tasker/handler_serializer.rb +109 -0
  77. data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
  78. data/app/serializers/tasker/task_serializer.rb +168 -0
  79. data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
  80. data/app/services/tasker/analytics_service.rb +409 -0
  81. data/config/initializers/dry_struct.rb +11 -0
  82. data/config/initializers/statesman.rb +6 -0
  83. data/config/initializers/tasker_orchestration.rb +17 -0
  84. data/config/initializers/time_formats.rb +4 -0
  85. data/config/routes.rb +34 -0
  86. data/config/tasker/subscriptions/example_integrations.yml +67 -0
  87. data/config/tasker/system_events.yml +305 -0
  88. data/db/functions/calculate_dependency_levels_v01.sql +45 -0
  89. data/db/functions/get_analytics_metrics_v01.sql +137 -0
  90. data/db/functions/get_slowest_steps_v01.sql +82 -0
  91. data/db/functions/get_slowest_tasks_v01.sql +96 -0
  92. data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
  93. data/db/functions/get_step_readiness_status_single_and_batch_v02.sql +223 -0
  94. data/db/functions/get_step_readiness_status_v01.sql +139 -0
  95. data/db/functions/get_system_health_counts_v01.sql +108 -0
  96. data/db/functions/get_task_execution_context_v01.sql +108 -0
  97. data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
  98. data/db/init/schema.sql +2254 -0
  99. data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
  100. data/db/migrate/20250710110830_step_readiness_sql_functions_v02.rb +39 -0
  101. data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
  102. data/docs/APPLICATION_GENERATOR.md +384 -0
  103. data/docs/AUTH.md +1741 -0
  104. data/docs/CIRCUIT_BREAKER.md +224 -0
  105. data/docs/DEVELOPER_GUIDE.md +2664 -0
  106. data/docs/EVENT_SYSTEM.md +637 -0
  107. data/docs/EXECUTION_CONFIGURATION.md +341 -0
  108. data/docs/FLOW_CHART.md +149 -0
  109. data/docs/HEALTH.md +542 -0
  110. data/docs/METRICS.md +731 -0
  111. data/docs/OPTIMIZATION_PLAN.md +1479 -0
  112. data/docs/OVERVIEW.md +548 -0
  113. data/docs/QUICK_START.md +270 -0
  114. data/docs/REGISTRY_SYSTEMS.md +373 -0
  115. data/docs/REST_API.md +632 -0
  116. data/docs/REVERSIONING.md +404 -0
  117. data/docs/ROADMAP.md +221 -0
  118. data/docs/SQL_FUNCTIONS.md +1408 -0
  119. data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
  120. data/docs/TELEMETRY.md +795 -0
  121. data/docs/TROUBLESHOOTING.md +756 -0
  122. data/docs/TaskHandlerGenerator.html +255 -0
  123. data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
  124. data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
  125. data/docs/Tasker/Analysis.html +117 -0
  126. data/docs/Tasker/AnalyticsController.html +450 -0
  127. data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
  128. data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
  129. data/docs/Tasker/AnalyticsService.html +2221 -0
  130. data/docs/Tasker/AnnotationType.html +137 -0
  131. data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
  132. data/docs/Tasker/ApplicationController.html +147 -0
  133. data/docs/Tasker/ApplicationJob.html +128 -0
  134. data/docs/Tasker/ApplicationRecord.html +378 -0
  135. data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
  136. data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
  137. data/docs/Tasker/Authentication/Coordinator.html +242 -0
  138. data/docs/Tasker/Authentication/Interface.html +560 -0
  139. data/docs/Tasker/Authentication/InterfaceError.html +124 -0
  140. data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
  141. data/docs/Tasker/Authentication.html +119 -0
  142. data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
  143. data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
  144. data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
  145. data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
  146. data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +360 -0
  147. data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
  148. data/docs/Tasker/Authorization/ResourceRegistry.html +875 -0
  149. data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
  150. data/docs/Tasker/Authorization.html +582 -0
  151. data/docs/Tasker/CacheCapabilities.html +167 -0
  152. data/docs/Tasker/CacheStrategy.html +1297 -0
  153. data/docs/Tasker/Concerns/Authenticatable.html +116 -0
  154. data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
  155. data/docs/Tasker/Concerns/Authorizable.html +816 -0
  156. data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
  157. data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
  158. data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
  159. data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
  160. data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
  161. data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
  162. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
  163. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
  164. data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
  165. data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
  166. data/docs/Tasker/Concerns.html +117 -0
  167. data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
  168. data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
  169. data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
  170. data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
  171. data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
  172. data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
  173. data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
  174. data/docs/Tasker/Configuration.html +1669 -0
  175. data/docs/Tasker/ConfigurationError.html +143 -0
  176. data/docs/Tasker/ConfiguredTask.html +514 -0
  177. data/docs/Tasker/Constants/EventDefinitions.html +590 -0
  178. data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
  179. data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
  180. data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
  181. data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
  182. data/docs/Tasker/Constants/RegistryEvents.html +285 -0
  183. data/docs/Tasker/Constants/StepEvents.html +177 -0
  184. data/docs/Tasker/Constants/TaskEvents.html +167 -0
  185. data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
  186. data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
  187. data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
  188. data/docs/Tasker/Constants/TaskExecution.html +126 -0
  189. data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
  190. data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
  191. data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
  192. data/docs/Tasker/Constants/TaskFinalization.html +126 -0
  193. data/docs/Tasker/Constants/TaskStatuses.html +223 -0
  194. data/docs/Tasker/Constants/TestEvents.html +163 -0
  195. data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
  196. data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
  197. data/docs/Tasker/Constants.html +561 -0
  198. data/docs/Tasker/DependentSystem.html +137 -0
  199. data/docs/Tasker/DependentSystemObjectMap.html +250 -0
  200. data/docs/Tasker/DetectorRegistry.html +598 -0
  201. data/docs/Tasker/Diagram/Edge.html +1191 -0
  202. data/docs/Tasker/Diagram/Flowchart.html +1539 -0
  203. data/docs/Tasker/Diagram/Node.html +1165 -0
  204. data/docs/Tasker/Diagram.html +117 -0
  205. data/docs/Tasker/Engine.html +215 -0
  206. data/docs/Tasker/Error.html +139 -0
  207. data/docs/Tasker/Events/Bus.html +1226 -0
  208. data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
  209. data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
  210. data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
  211. data/docs/Tasker/Events/Catalog.html +1291 -0
  212. data/docs/Tasker/Events/CustomRegistry.html +943 -0
  213. data/docs/Tasker/Events/DefinitionLoader.html +575 -0
  214. data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
  215. data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
  216. data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
  217. data/docs/Tasker/Events/Publisher.html +365 -0
  218. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
  219. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
  220. data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
  221. data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
  222. data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
  223. data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
  224. data/docs/Tasker/Events/Subscribers.html +117 -0
  225. data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
  226. data/docs/Tasker/Events.html +294 -0
  227. data/docs/Tasker/EventsGenerator.html +459 -0
  228. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
  229. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
  230. data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
  231. data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
  232. data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
  233. data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
  234. data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
  235. data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
  236. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
  237. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
  238. data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
  239. data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
  240. data/docs/Tasker/Functions.html +117 -0
  241. data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
  242. data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
  243. data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
  244. data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
  245. data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
  246. data/docs/Tasker/Generators.html +117 -0
  247. data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
  248. data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
  249. data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
  250. data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
  251. data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
  252. data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
  253. data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
  254. data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
  255. data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
  256. data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
  257. data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
  258. data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
  259. data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
  260. data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
  261. data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
  262. data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
  263. data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
  264. data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
  265. data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
  266. data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
  267. data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
  268. data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
  269. data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
  270. data/docs/Tasker/GraphQLTypes.html +130 -0
  271. data/docs/Tasker/GraphqlController.html +251 -0
  272. data/docs/Tasker/HandlerFactory.html +1528 -0
  273. data/docs/Tasker/HandlerSerializer.html +682 -0
  274. data/docs/Tasker/HandlersController.html +574 -0
  275. data/docs/Tasker/HashIdentityStrategy.html +278 -0
  276. data/docs/Tasker/Health/ReadinessChecker.html +712 -0
  277. data/docs/Tasker/Health/StatusChecker.html +653 -0
  278. data/docs/Tasker/Health.html +117 -0
  279. data/docs/Tasker/HealthController.html +523 -0
  280. data/docs/Tasker/IdentityStrategy.html +276 -0
  281. data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
  282. data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
  283. data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
  284. data/docs/Tasker/LifecycleEvents/Events.html +204 -0
  285. data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
  286. data/docs/Tasker/LifecycleEvents.html +799 -0
  287. data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
  288. data/docs/Tasker/Logging.html +115 -0
  289. data/docs/Tasker/MetricsController.html +293 -0
  290. data/docs/Tasker/MetricsExportJob.html +414 -0
  291. data/docs/Tasker/Mutations/BaseMutation.html +128 -0
  292. data/docs/Tasker/Mutations/CancelStep.html +219 -0
  293. data/docs/Tasker/Mutations/CancelTask.html +221 -0
  294. data/docs/Tasker/Mutations/CreateTask.html +243 -0
  295. data/docs/Tasker/Mutations/UpdateStep.html +243 -0
  296. data/docs/Tasker/Mutations/UpdateTask.html +243 -0
  297. data/docs/Tasker/Mutations.html +117 -0
  298. data/docs/Tasker/NamedStep.html +216 -0
  299. data/docs/Tasker/NamedTask.html +910 -0
  300. data/docs/Tasker/NamedTasksNamedStep.html +435 -0
  301. data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
  302. data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
  303. data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
  304. data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
  305. data/docs/Tasker/Orchestration/Coordinator.html +641 -0
  306. data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
  307. data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
  308. data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
  309. data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
  310. data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
  311. data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
  312. data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
  313. data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
  314. data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
  315. data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
  316. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
  317. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
  318. data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
  319. data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
  320. data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
  321. data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
  322. data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
  323. data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
  324. data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
  325. data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
  326. data/docs/Tasker/Orchestration.html +130 -0
  327. data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
  328. data/docs/Tasker/PageSort.html +247 -0
  329. data/docs/Tasker/PermanentError.html +518 -0
  330. data/docs/Tasker/ProceduralError.html +147 -0
  331. data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
  332. data/docs/Tasker/Queries/AllTasks.html +221 -0
  333. data/docs/Tasker/Queries/BaseQuery.html +128 -0
  334. data/docs/Tasker/Queries/Helpers.html +187 -0
  335. data/docs/Tasker/Queries/OneStep.html +225 -0
  336. data/docs/Tasker/Queries/OneTask.html +217 -0
  337. data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
  338. data/docs/Tasker/Queries/TasksByStatus.html +233 -0
  339. data/docs/Tasker/Queries.html +119 -0
  340. data/docs/Tasker/Railtie.html +124 -0
  341. data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
  342. data/docs/Tasker/Registry/EventPublisher.html +667 -0
  343. data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
  344. data/docs/Tasker/Registry/RegistrationError.html +132 -0
  345. data/docs/Tasker/Registry/RegistryError.html +139 -0
  346. data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
  347. data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
  348. data/docs/Tasker/Registry/ValidationError.html +132 -0
  349. data/docs/Tasker/Registry.html +119 -0
  350. data/docs/Tasker/RetryableError.html +515 -0
  351. data/docs/Tasker/StateMachine/Compatibility.html +282 -0
  352. data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
  353. data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
  354. data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
  355. data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
  356. data/docs/Tasker/StateMachine.html +602 -0
  357. data/docs/Tasker/StepDagRelationship.html +657 -0
  358. data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
  359. data/docs/Tasker/StepHandler/Api.html +884 -0
  360. data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
  361. data/docs/Tasker/StepHandler/Base.html +970 -0
  362. data/docs/Tasker/StepHandler.html +119 -0
  363. data/docs/Tasker/StepReadinessStatus.html +836 -0
  364. data/docs/Tasker/Task.html +2478 -0
  365. data/docs/Tasker/TaskAnnotation.html +137 -0
  366. data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
  367. data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
  368. data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
  369. data/docs/Tasker/TaskBuilder.html +764 -0
  370. data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
  371. data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
  372. data/docs/Tasker/TaskDiagram.html +548 -0
  373. data/docs/Tasker/TaskDiagramsController.html +240 -0
  374. data/docs/Tasker/TaskExecutionContext.html +469 -0
  375. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
  376. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
  377. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
  378. data/docs/Tasker/TaskHandler/ClassMethods.html +395 -0
  379. data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
  380. data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
  381. data/docs/Tasker/TaskHandler.html +271 -0
  382. data/docs/Tasker/TaskNamespace.html +312 -0
  383. data/docs/Tasker/TaskRunnerJob.html +406 -0
  384. data/docs/Tasker/TaskSerializer.html +474 -0
  385. data/docs/Tasker/TaskTransition.html +1517 -0
  386. data/docs/Tasker/TaskWorkflowSummary.html +988 -0
  387. data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
  388. data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
  389. data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
  390. data/docs/Tasker/TaskerRailsSchema.html +384 -0
  391. data/docs/Tasker/TasksController.html +595 -0
  392. data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
  393. data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
  394. data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
  395. data/docs/Tasker/Telemetry/Events.html +115 -0
  396. data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
  397. data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
  398. data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
  399. data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
  400. data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
  401. data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
  402. data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
  403. data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
  404. data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
  405. data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
  406. data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
  407. data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
  408. data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
  409. data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
  410. data/docs/Tasker/Telemetry/Plugins.html +117 -0
  411. data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
  412. data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
  413. data/docs/Tasker/Telemetry.html +130 -0
  414. data/docs/Tasker/Types/AuthConfig.html +886 -0
  415. data/docs/Tasker/Types/BackoffConfig.html +1063 -0
  416. data/docs/Tasker/Types/BaseConfig.html +227 -0
  417. data/docs/Tasker/Types/CacheConfig.html +1731 -0
  418. data/docs/Tasker/Types/DatabaseConfig.html +388 -0
  419. data/docs/Tasker/Types/DependencyGraph.html +526 -0
  420. data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
  421. data/docs/Tasker/Types/EngineConfig.html +1181 -0
  422. data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
  423. data/docs/Tasker/Types/GraphEdge.html +517 -0
  424. data/docs/Tasker/Types/GraphMetadata.html +781 -0
  425. data/docs/Tasker/Types/GraphNode.html +694 -0
  426. data/docs/Tasker/Types/HealthConfig.html +784 -0
  427. data/docs/Tasker/Types/StepSequence.html +353 -0
  428. data/docs/Tasker/Types/StepTemplate.html +1193 -0
  429. data/docs/Tasker/Types/TaskRequest.html +1179 -0
  430. data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
  431. data/docs/Tasker/Types.html +154 -0
  432. data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
  433. data/docs/Tasker/WorkflowStep.html +2724 -0
  434. data/docs/Tasker/WorkflowStepEdge.html +306 -0
  435. data/docs/Tasker/WorkflowStepSerializer.html +305 -0
  436. data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
  437. data/docs/Tasker/WorkflowStepTransition.html +2201 -0
  438. data/docs/Tasker/WorkflowStepsController.html +462 -0
  439. data/docs/Tasker.html +468 -0
  440. data/docs/VISION.md +584 -0
  441. data/docs/WHY.md +21 -0
  442. data/docs/_index.html +2319 -0
  443. data/docs/class_list.html +54 -0
  444. data/docs/css/common.css +1 -0
  445. data/docs/css/full_list.css +58 -0
  446. data/docs/css/style.css +503 -0
  447. data/docs/events/migration_plan_outcomes.md +80 -0
  448. data/docs/file.README.html +537 -0
  449. data/docs/file_list.html +59 -0
  450. data/docs/frames.html +22 -0
  451. data/docs/index.html +537 -0
  452. data/docs/js/app.js +344 -0
  453. data/docs/js/full_list.js +242 -0
  454. data/docs/js/jquery.js +4 -0
  455. data/docs/method_list.html +8854 -0
  456. data/docs/top-level-namespace.html +110 -0
  457. data/lib/generators/tasker/authenticator_generator.rb +301 -0
  458. data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
  459. data/lib/generators/tasker/events_generator.rb +91 -0
  460. data/lib/generators/tasker/subscriber_generator.rb +107 -0
  461. data/lib/generators/tasker/task_handler_generator.rb +138 -0
  462. data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
  463. data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
  464. data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +82 -0
  465. data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +136 -0
  466. data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
  467. data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
  468. data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
  469. data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
  470. data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
  471. data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
  472. data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
  473. data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
  474. data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
  475. data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
  476. data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
  477. data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
  478. data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
  479. data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
  480. data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
  481. data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
  482. data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
  483. data/lib/generators/tasker/templates/task_handler.rb.erb +60 -0
  484. data/lib/generators/tasker/templates/task_handler_spec.rb.erb +165 -0
  485. data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
  486. data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
  487. data/lib/tasker/authentication/coordinator.rb +78 -0
  488. data/lib/tasker/authentication/errors.rb +9 -0
  489. data/lib/tasker/authentication/interface.rb +36 -0
  490. data/lib/tasker/authentication/none_authenticator.rb +26 -0
  491. data/lib/tasker/authorization/base_coordinator.rb +112 -0
  492. data/lib/tasker/authorization/errors.rb +26 -0
  493. data/lib/tasker/authorization/resource_constants.rb +73 -0
  494. data/lib/tasker/authorization/resource_registry.rb +136 -0
  495. data/lib/tasker/authorization.rb +75 -0
  496. data/lib/tasker/cache_capabilities.rb +131 -0
  497. data/lib/tasker/cache_strategy.rb +469 -0
  498. data/lib/tasker/concerns/authenticatable.rb +41 -0
  499. data/lib/tasker/concerns/authorizable.rb +204 -0
  500. data/lib/tasker/concerns/controller_authorizable.rb +124 -0
  501. data/lib/tasker/concerns/event_publisher.rb +716 -0
  502. data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
  503. data/lib/tasker/concerns/state_machine_base.rb +218 -0
  504. data/lib/tasker/concerns/structured_logging.rb +387 -0
  505. data/lib/tasker/configuration.rb +325 -0
  506. data/lib/tasker/constants/event_definitions.rb +147 -0
  507. data/lib/tasker/constants/registry_events.rb +54 -0
  508. data/lib/tasker/constants.rb +417 -0
  509. data/lib/tasker/engine.rb +90 -0
  510. data/lib/tasker/errors.rb +90 -0
  511. data/lib/tasker/events/catalog.rb +432 -0
  512. data/lib/tasker/events/custom_registry.rb +175 -0
  513. data/lib/tasker/events/definition_loader.rb +199 -0
  514. data/lib/tasker/events/event_payload_builder.rb +461 -0
  515. data/lib/tasker/events/publisher.rb +149 -0
  516. data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
  517. data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
  518. data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
  519. data/lib/tasker/events/subscription_loader.rb +161 -0
  520. data/lib/tasker/events.rb +37 -0
  521. data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
  522. data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
  523. data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
  524. data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
  525. data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
  526. data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
  527. data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
  528. data/lib/tasker/functions/function_wrapper.rb +42 -0
  529. data/lib/tasker/functions.rb +12 -0
  530. data/lib/tasker/handler_factory.rb +327 -0
  531. data/lib/tasker/health/readiness_checker.rb +186 -0
  532. data/lib/tasker/health/status_checker.rb +203 -0
  533. data/lib/tasker/identity_strategy.rb +38 -0
  534. data/lib/tasker/logging/correlation_id_generator.rb +120 -0
  535. data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
  536. data/lib/tasker/orchestration/connection_builder.rb +122 -0
  537. data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
  538. data/lib/tasker/orchestration/coordinator.rb +119 -0
  539. data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
  540. data/lib/tasker/orchestration/plugin_integration.rb +124 -0
  541. data/lib/tasker/orchestration/response_processor.rb +168 -0
  542. data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
  543. data/lib/tasker/orchestration/step_executor.rb +941 -0
  544. data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
  545. data/lib/tasker/orchestration/task_finalizer.rb +564 -0
  546. data/lib/tasker/orchestration/task_initializer.rb +140 -0
  547. data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
  548. data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
  549. data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
  550. data/lib/tasker/orchestration.rb +45 -0
  551. data/lib/tasker/railtie.rb +9 -0
  552. data/lib/tasker/registry/base_registry.rb +177 -0
  553. data/lib/tasker/registry/event_publisher.rb +91 -0
  554. data/lib/tasker/registry/interface_validator.rb +140 -0
  555. data/lib/tasker/registry/statistics_collector.rb +381 -0
  556. data/lib/tasker/registry/subscriber_registry.rb +285 -0
  557. data/lib/tasker/registry.rb +22 -0
  558. data/lib/tasker/state_machine/step_state_machine.rb +508 -0
  559. data/lib/tasker/state_machine/task_state_machine.rb +192 -0
  560. data/lib/tasker/state_machine.rb +83 -0
  561. data/lib/tasker/step_handler/api.rb +410 -0
  562. data/lib/tasker/step_handler/base.rb +206 -0
  563. data/lib/tasker/task_builder.rb +432 -0
  564. data/lib/tasker/task_handler/class_methods.rb +327 -0
  565. data/lib/tasker/task_handler/instance_methods.rb +293 -0
  566. data/lib/tasker/task_handler/step_group.rb +182 -0
  567. data/lib/tasker/task_handler.rb +43 -0
  568. data/lib/tasker/telemetry/event_mapping.rb +126 -0
  569. data/lib/tasker/telemetry/event_router.rb +318 -0
  570. data/lib/tasker/telemetry/events/export_events.rb +38 -0
  571. data/lib/tasker/telemetry/export_coordinator.rb +497 -0
  572. data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
  573. data/lib/tasker/telemetry/log_backend.rb +224 -0
  574. data/lib/tasker/telemetry/metric_types.rb +416 -0
  575. data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
  576. data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
  577. data/lib/tasker/telemetry/plugin_registry.rb +333 -0
  578. data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
  579. data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
  580. data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
  581. data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
  582. data/lib/tasker/telemetry/trace_backend.rb +186 -0
  583. data/lib/tasker/telemetry.rb +59 -0
  584. data/lib/tasker/types/auth_config.rb +81 -0
  585. data/lib/tasker/types/backoff_config.rb +142 -0
  586. data/lib/tasker/types/cache_config.rb +257 -0
  587. data/lib/tasker/types/database_config.rb +39 -0
  588. data/lib/tasker/types/dependency_graph.rb +225 -0
  589. data/lib/tasker/types/dependency_graph_config.rb +149 -0
  590. data/lib/tasker/types/engine_config.rb +131 -0
  591. data/lib/tasker/types/execution_config.rb +289 -0
  592. data/lib/tasker/types/health_config.rb +84 -0
  593. data/lib/tasker/types/step_sequence.rb +24 -0
  594. data/lib/tasker/types/step_template.rb +63 -0
  595. data/lib/tasker/types/task_request.rb +60 -0
  596. data/lib/tasker/types/telemetry_config.rb +273 -0
  597. data/lib/tasker/types.rb +64 -0
  598. data/lib/tasker/version.rb +8 -0
  599. data/lib/tasker.rb +82 -0
  600. data/lib/tasks/tasker_tasks.rake +383 -0
  601. metadata +954 -0
data/docs/AUTH.md ADDED
@@ -0,0 +1,1741 @@
1
+ # Tasker Authentication & Authorization Guide
2
+
3
+ ## Overview
4
+
5
+ Tasker provides a comprehensive, flexible authentication and authorization system that works with any Rails authentication solution. The system uses **dependency injection** and **resource-based authorization** to allow host applications to provide their own authentication logic while maintaining enterprise-grade security for both REST APIs and GraphQL endpoints.
6
+
7
+ ## 🎉 Latest Updates - Complete Authorization System
8
+
9
+ **Phase 5 Controller Integration - COMPLETED! ✅**
10
+
11
+ We've successfully implemented **revolutionary GraphQL authorization** and complete controller integration:
12
+
13
+ - **✅ GraphQL Operation-Level Authorization**: Automatically maps GraphQL operations to resource:action permissions
14
+ - **✅ Automatic Controller Authorization**: All REST and GraphQL endpoints protected seamlessly
15
+ - **✅ Resource Registry**: Centralized constants eliminate hardcoded strings throughout codebase
16
+ - **✅ Complete Test Coverage**: 674/674 tests passing with comprehensive integration testing
17
+ - **✅ Zero Breaking Changes**: All features are opt-in and backward compatible
18
+ - **✅ State Isolation**: Robust test infrastructure prevents configuration leakage
19
+
20
+ **Ready for Production**: The complete authentication and authorization system is now production-ready with enterprise-grade security for both REST APIs and GraphQL endpoints.
21
+
22
+ ## Key Benefits
23
+
24
+ - **Provider Agnostic**: Works with Devise, JWT, OmniAuth, custom authentication, or no authentication
25
+ - **Dependency Injection**: Host applications implement authenticators rather than building provider-specific code into Tasker
26
+ - **Resource-Based Authorization**: Granular permissions using resource:action patterns (e.g., `tasker.task:create`)
27
+ - **GraphQL Operation-Level Authorization**: Revolutionary security for GraphQL that maps operations to resource permissions
28
+ - **Automatic Controller Integration**: Authentication and authorization work seamlessly across REST and GraphQL
29
+ - **Interface Validation**: Ensures authenticators implement required methods with helpful error messages
30
+ - **Configuration Validation**: Built-in validation with security best practices
31
+ - **Flexible Configuration**: Support for multiple strategies and environment-specific settings
32
+ - **Zero Breaking Changes**: All features are opt-in and backward compatible
33
+
34
+ ## Table of Contents
35
+
36
+ - [Quick Start](#quick-start)
37
+ - [Authorization Quick Start](#authorization-quick-start)
38
+ - [GraphQL Authorization](#graphql-authorization)
39
+ - [Authenticator Generator](#authenticator-generator)
40
+ - [Configuration Options](#configuration-options)
41
+ - [Authentication Strategies](#authentication-strategies)
42
+ - [Authorization System](#authorization-system)
43
+ - [Building Custom Authenticators](#building-custom-authenticators)
44
+ - [Building Authorization Coordinators](#building-authorization-coordinators)
45
+ - [JWT Authentication Example](#jwt-authentication-example)
46
+ - [Integration with Controllers](#integration-with-controllers)
47
+ - [Error Handling](#error-handling)
48
+ - [Testing Authentication](#testing-authentication)
49
+ - [Testing Authorization](#testing-authorization)
50
+ - [Best Practices](#best-practices)
51
+
52
+ ## Quick Start
53
+
54
+ ### 1. No Authentication (Default)
55
+
56
+ By default, Tasker requires no authentication:
57
+
58
+ ```ruby
59
+ # config/initializers/tasker.rb
60
+ Tasker::Configuration.configuration do |config|
61
+ config.auth do |auth|
62
+ auth.authentication_enabled = false # Default - no configuration needed
63
+ end
64
+ end
65
+ ```
66
+
67
+ ### 2. Custom Authentication
68
+
69
+ For any authentication system, enable authentication and specify your authenticator class:
70
+
71
+ ```ruby
72
+ # config/initializers/tasker.rb
73
+ Tasker::Configuration.configuration do |config|
74
+ config.auth do |auth|
75
+ auth.authentication_enabled = true
76
+ auth.authenticator_class = 'YourCustomAuthenticator'
77
+ # Additional options specific to your authenticator can be passed to the authenticator
78
+ end
79
+ end
80
+ ```
81
+
82
+ ### 3. With Authorization (Recommended)
83
+
84
+ Enable both authentication and authorization for complete security:
85
+
86
+ ```ruby
87
+ # config/initializers/tasker.rb
88
+ Tasker::Configuration.configuration do |config|
89
+ config.auth do |auth|
90
+ auth.authentication_enabled = true
91
+ auth.authenticator_class = 'YourCustomAuthenticator'
92
+ auth.authorization_enabled = true
93
+ auth.authorization_coordinator_class = 'YourAuthorizationCoordinator'
94
+ auth.user_class = 'User'
95
+ end
96
+ end
97
+ ```
98
+
99
+ ## Authorization Quick Start
100
+
101
+ Authorization in Tasker uses a **resource:action** permission model that works seamlessly with both REST APIs and GraphQL.
102
+
103
+ ### 1. Enable Authorization
104
+
105
+ ```ruby
106
+ # config/initializers/tasker.rb
107
+ Tasker::Configuration.configuration do |config|
108
+ config.auth do |auth|
109
+ auth.authorization_enabled = true
110
+ auth.authorization_coordinator_class = 'YourAuthorizationCoordinator'
111
+ auth.user_class = 'User'
112
+ end
113
+ end
114
+ ```
115
+
116
+ ### 2. Create Authorization Coordinator
117
+
118
+ ```ruby
119
+ # app/tasker/authorization/your_authorization_coordinator.rb
120
+ class YourAuthorizationCoordinator < Tasker::Authorization::BaseCoordinator
121
+ protected
122
+
123
+ def authorized?(resource, action, context = {})
124
+ case resource
125
+ when Tasker::Authorization::ResourceConstants::RESOURCES::TASK
126
+ authorize_task_action(action, context)
127
+ when Tasker::Authorization::ResourceConstants::RESOURCES::WORKFLOW_STEP
128
+ authorize_step_action(action, context)
129
+ when Tasker::Authorization::ResourceConstants::RESOURCES::HEALTH_STATUS
130
+ authorize_health_status_action(action, context)
131
+ else
132
+ false
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def authorize_task_action(action, context)
139
+ case action
140
+ when :index, :show
141
+ # Regular users can view tasks
142
+ user.tasker_admin? || user.has_tasker_permission?("#{Tasker::Authorization::ResourceConstants::RESOURCES::TASK}:#{action}")
143
+ when :create, :update, :destroy, :retry, :cancel
144
+ # Only admins can modify tasks
145
+ user.tasker_admin? || user.has_tasker_permission?("#{Tasker::Authorization::ResourceConstants::RESOURCES::TASK}:#{action}")
146
+ else
147
+ false
148
+ end
149
+ end
150
+
151
+ def authorize_step_action(action, context)
152
+ case action
153
+ when :index, :show
154
+ user.tasker_admin? || user.has_tasker_permission?("#{Tasker::Authorization::ResourceConstants::RESOURCES::WORKFLOW_STEP}:#{action}")
155
+ when :update, :destroy, :retry, :cancel
156
+ # Step modifications require admin access
157
+ user.tasker_admin?
158
+ else
159
+ false
160
+ end
161
+ end
162
+
163
+ def authorize_health_status_action(action, context)
164
+ case action
165
+ when :index
166
+ # Health status access: admin users or explicit permission
167
+ user.tasker_admin? || user.has_tasker_permission?("#{Tasker::Authorization::ResourceConstants::RESOURCES::HEALTH_STATUS}:#{action}")
168
+ else
169
+ false
170
+ end
171
+ end
172
+ end
173
+ ```
174
+
175
+ ### 3. Add Authorization to User Model
176
+
177
+ ```ruby
178
+ # app/models/user.rb
179
+ class User < ApplicationRecord
180
+ include Tasker::Concerns::Authorizable
181
+
182
+ def has_tasker_permission?(permission)
183
+ # Your permission checking logic
184
+ permissions.include?(permission)
185
+ end
186
+
187
+ def tasker_admin?
188
+ # Your admin checking logic
189
+ role == 'admin' || roles.include?('admin')
190
+ end
191
+ end
192
+ ```
193
+
194
+ ### 4. Automatic Protection
195
+
196
+ With authorization enabled, **all Tasker endpoints are automatically protected**:
197
+
198
+ ```ruby
199
+ # REST API calls now require proper permissions
200
+ GET /tasker/tasks # Requires tasker.task:index permission
201
+ POST /tasker/tasks # Requires tasker.task:create permission
202
+ GET /tasker/tasks/123 # Requires tasker.task:show permission
203
+
204
+ # Health endpoints with optional authorization
205
+ GET /tasker/health/ready # Never requires authorization (K8s compatibility)
206
+ GET /tasker/health/live # Never requires authorization (K8s compatibility)
207
+ GET /tasker/health/status # Requires tasker.health_status:index permission (if enabled)
208
+
209
+ # GraphQL operations are automatically mapped to permissions
210
+ query { tasks { taskId } } # Requires tasker.task:index
211
+ mutation { createTask(input: {...}) { ... } } # Requires tasker.task:create
212
+ ```
213
+
214
+ ## Health Status Authorization
215
+
216
+ Tasker provides optional authorization for health monitoring endpoints, designed for production security while maintaining Kubernetes compatibility:
217
+
218
+ ### Health Endpoint Security Model
219
+
220
+ ```ruby
221
+ # Kubernetes-compatible endpoints (never require authorization)
222
+ GET /tasker/health/ready # Always accessible - K8s readiness probe
223
+ GET /tasker/health/live # Always accessible - K8s liveness probe
224
+
225
+ # Status endpoint with optional authorization
226
+ GET /tasker/health/status # Requires tasker.health_status:index permission (if enabled)
227
+ ```
228
+
229
+ ### Configuration
230
+
231
+ Enable health status authorization:
232
+
233
+ ```ruby
234
+ # config/initializers/tasker.rb
235
+ Tasker::Configuration.configuration do |config|
236
+ config.auth do |auth|
237
+ auth.authorization_enabled = true
238
+ auth.authorization_coordinator_class = 'YourAuthorizationCoordinator'
239
+ end
240
+
241
+ config.health do |health|
242
+ health.status_requires_authentication = true # Optional authentication
243
+ end
244
+ end
245
+ ```
246
+
247
+ ### Authorization Implementation
248
+
249
+ Add health status authorization to your coordinator:
250
+
251
+ ```ruby
252
+ class YourAuthorizationCoordinator < Tasker::Authorization::BaseCoordinator
253
+ include Tasker::Authorization::ResourceConstants
254
+
255
+ protected
256
+
257
+ def authorized?(resource, action, context = {})
258
+ case resource
259
+ when RESOURCES::HEALTH_STATUS
260
+ authorize_health_status_action(action, context)
261
+ # ... other resources
262
+ end
263
+ end
264
+
265
+ private
266
+
267
+ def authorize_health_status_action(action, context)
268
+ case action
269
+ when :index
270
+ # Admin users always have access
271
+ user.tasker_admin? ||
272
+ # Regular users need explicit permission
273
+ user.has_tasker_permission?("#{RESOURCES::HEALTH_STATUS}:#{action}")
274
+ else
275
+ false
276
+ end
277
+ end
278
+ end
279
+ ```
280
+
281
+ ### Security Benefits
282
+
283
+ - **K8s Compatibility**: Ready/live endpoints never require authorization
284
+ - **Granular Control**: Status endpoint uses specific `health_status:index` permission
285
+ - **Admin Override**: Admin users always have health status access
286
+ - **Optional Authentication**: Can require authentication without authorization
287
+ - **Production Ready**: Designed for enterprise security requirements
288
+
289
+ For complete health monitoring documentation, see **[Health Monitoring Guide](HEALTH.md)**.
290
+
291
+ ## GraphQL Authorization
292
+
293
+ Tasker provides **revolutionary GraphQL authorization** that automatically maps GraphQL operations to resource:action permissions.
294
+
295
+ ### How It Works
296
+
297
+ The system **parses GraphQL queries and mutations** to extract the underlying operations, then checks permissions for each operation:
298
+
299
+ ```ruby
300
+ # This GraphQL query:
301
+ query {
302
+ tasks {
303
+ taskId
304
+ status
305
+ workflowSteps {
306
+ workflowStepId
307
+ status
308
+ }
309
+ }
310
+ }
311
+
312
+ # Is automatically mapped to these permission checks:
313
+ # - tasker.task:index (for tasks query)
314
+ # - tasker.workflow_step:index (for workflowSteps query)
315
+ ```
316
+
317
+ ### GraphQL Operation Mapping
318
+
319
+ | GraphQL Operation | Resource:Action Permission |
320
+ |------------------|---------------------------|
321
+ | `query { tasks }` | `tasker.task:index` |
322
+ | `query { task(taskId: "123") }` | `tasker.task:show` |
323
+ | `mutation { createTask(...) }` | `tasker.task:create` |
324
+ | `mutation { updateTask(...) }` | `tasker.task:update` |
325
+ | `mutation { cancelTask(...) }` | `tasker.task:cancel` |
326
+ | `query { step(...) }` | `tasker.workflow_step:show` |
327
+ | `mutation { updateStep(...) }` | `tasker.workflow_step:update` |
328
+ | `mutation { cancelStep(...) }` | `tasker.workflow_step:cancel` |
329
+
330
+ ### GraphQL Authorization Examples
331
+
332
+ ```ruby
333
+ # Admin user - Full access
334
+ admin_user.tasker_admin? # => true
335
+
336
+ # This query succeeds for admin
337
+ query {
338
+ tasks {
339
+ taskId
340
+ workflowSteps { workflowStepId }
341
+ }
342
+ }
343
+ # ✅ 200 OK - Admin has access to all operations
344
+
345
+ # Regular user with limited permissions
346
+ user.has_tasker_permission?('tasker.task:index') # => true
347
+ user.has_tasker_permission?('tasker.workflow_step:index') # => false
348
+
349
+ # Same query for regular user
350
+ query {
351
+ tasks {
352
+ taskId
353
+ workflowSteps { workflowStepId }
354
+ }
355
+ }
356
+ # ❌ 403 Forbidden - User lacks tasker.workflow_step:index permission
357
+
358
+ # User can make this simpler query
359
+ query {
360
+ tasks {
361
+ taskId
362
+ status
363
+ }
364
+ }
365
+ # ✅ 200 OK - User has tasker.task:index permission
366
+ ```
367
+
368
+ ### GraphQL with Context
369
+
370
+ GraphQL authorization includes context information for advanced logic:
371
+
372
+ ```ruby
373
+ class YourAuthorizationCoordinator < Tasker::Authorization::BaseCoordinator
374
+ protected
375
+
376
+ def authorized?(resource, action, context = {})
377
+ # Context includes:
378
+ # - controller: GraphQL controller instance
379
+ # - query_string: Original GraphQL query
380
+ # - operation_name: Named operation (if provided)
381
+ # - variables: Query variables
382
+
383
+ case resource
384
+ when Tasker::Authorization::ResourceConstants::RESOURCES::TASK
385
+ authorize_task_with_context(action, context)
386
+ end
387
+ end
388
+
389
+ private
390
+
391
+ def authorize_task_with_context(action, context)
392
+ case action
393
+ when :show
394
+ # Allow users to view their own tasks
395
+ task_id = extract_task_id_from_context(context)
396
+ user.tasker_admin? || user_owns_task?(task_id)
397
+ when :index
398
+ # Regular index permission
399
+ user.has_tasker_permission?("tasker.task:index")
400
+ end
401
+ end
402
+ end
403
+ ```
404
+
405
+ ## Authenticator Generator
406
+
407
+ Tasker provides a Rails generator to quickly create authenticator templates for common authentication systems:
408
+
409
+ ### Basic Usage
410
+
411
+ ```bash
412
+ # Generate a JWT authenticator
413
+ rails generate tasker:authenticator CompanyJWT --type=jwt
414
+
415
+ # Generate a Devise authenticator
416
+ rails generate tasker:authenticator AdminAuth --type=devise --user-class=Admin
417
+
418
+ # Generate an API token authenticator
419
+ rails generate tasker:authenticator ApiAuth --type=api_token
420
+
421
+ # Generate an OmniAuth authenticator
422
+ rails generate tasker:authenticator SocialAuth --type=omniauth
423
+
424
+ # Generate a custom authenticator template
425
+ rails generate tasker:authenticator CustomAuth --type=custom
426
+ ```
427
+
428
+ ### Generator Options
429
+
430
+ - `--type`: Authenticator type (jwt, devise, api_token, omniauth, custom)
431
+ - `--user-class`: User model class name (default: User)
432
+ - `--directory`: Output directory (default: app/tasker/authenticators)
433
+ - `--with-spec/--no-with-spec`: Generate spec file (default: true)
434
+
435
+ ### What the Generator Creates
436
+
437
+ The generator creates:
438
+
439
+ 1. **Authenticator Class**: Complete implementation with security best practices
440
+ 2. **Spec File**: Comprehensive test suite with example test cases
441
+ 3. **Configuration Example**: Ready-to-use configuration for your initializer
442
+ 4. **Usage Instructions**: Step-by-step setup guide with next steps
443
+
444
+ ### Example: JWT Authenticator Generation
445
+
446
+ ```bash
447
+ rails generate tasker:authenticator CompanyJWT --type=jwt --user-class=User
448
+ ```
449
+
450
+ **Creates:**
451
+ - `app/tasker/authenticators/company_jwt_authenticator.rb` - Full JWT implementation
452
+ - `spec/tasker/authenticators/company_jwt_authenticator_spec.rb` - Test suite
453
+ - Configuration example and setup instructions
454
+
455
+ **Generated features:**
456
+ - JWT signature verification with configurable algorithms
457
+ - Bearer token and raw token support
458
+ - Comprehensive validation with security checks
459
+ - Test token generation helper for testing
460
+ - Memoized user lookup for performance
461
+
462
+ ## Configuration Options
463
+
464
+ ### Authentication Configuration Block
465
+
466
+ ```ruby
467
+ Tasker::Configuration.configuration do |config|
468
+ config.auth do |auth|
469
+ # Authentication settings
470
+ auth.authentication_enabled = true | false # Enable/disable authentication
471
+ auth.authenticator_class = 'String' # Your authenticator class name
472
+
473
+ # Authorization settings
474
+ auth.authorization_enabled = true | false # Enable/disable authorization
475
+ auth.authorization_coordinator_class = 'String' # Your authorization coordinator class
476
+ auth.user_class = 'String' # Your user model class name
477
+ end
478
+ end
479
+ ```
480
+
481
+ ### Configuration Validation
482
+
483
+ Tasker validates configuration at startup and provides helpful error messages:
484
+
485
+ ```ruby
486
+ # Missing authenticator class when authentication is enabled
487
+ auth.authentication_enabled = true
488
+ auth.authenticator_class = nil
489
+ # => ConfigurationError: "Authentication is enabled but no authenticator_class is specified"
490
+
491
+ # Invalid authenticator class
492
+ auth.authenticator_class = 'NonExistentClass'
493
+ # => ConfigurationError: "Authenticator configuration errors: User class 'NonExistentClass' not found"
494
+ ```
495
+
496
+ ## Authentication Configuration
497
+
498
+ ### No Authentication (Default)
499
+
500
+ By default, authentication is disabled. All users are considered "authenticated" with no user object.
501
+
502
+ ```ruby
503
+ config.auth do |auth|
504
+ auth.authentication_enabled = false # Default
505
+ end
506
+ ```
507
+
508
+ **Use Cases:**
509
+ - Development environments
510
+ - Internal tools without user management
511
+ - Public APIs
512
+ - Testing scenarios
513
+
514
+ ### Custom Authentication
515
+
516
+ Host application provides a custom authenticator class that implements the authentication interface.
517
+
518
+ ```ruby
519
+ config.auth do |auth|
520
+ auth.authentication_enabled = true
521
+ auth.authenticator_class = 'DeviseAuthenticator'
522
+ # Your authenticator can accept any configuration options in its initialize method
523
+ end
524
+ ```
525
+
526
+ **Use Cases:**
527
+ - Devise integration
528
+ - JWT authentication
529
+ - OmniAuth integration
530
+ - Custom authentication systems
531
+ - Multi-tenant authentication
532
+
533
+ ## Authorization System
534
+
535
+ Tasker's authorization system provides enterprise-grade security through a resource-based permission model. The system uses **resource constants** and **authorization coordinators** to ensure consistent, maintainable authorization logic.
536
+
537
+ ### Resource Registry
538
+
539
+ All authorization revolves around the central resource registry:
540
+
541
+ ```ruby
542
+ # Available Resources and Actions
543
+ Resources:
544
+ - tasker.task (index, show, create, update, destroy, retry, cancel)
545
+ - tasker.workflow_step (index, show, update, destroy, retry, cancel)
546
+
547
+ # Permission Examples:
548
+ 'tasker.task:index' # List all tasks
549
+ 'tasker.task:create' # Create new tasks
550
+ 'tasker.workflow_step:show' # View individual workflow steps
551
+ ```
552
+
553
+ ### Authorization Coordinator Interface
554
+
555
+ Authorization coordinators must implement the `BaseCoordinator` interface:
556
+
557
+ ```ruby
558
+ class YourAuthorizationCoordinator < Tasker::Authorization::BaseCoordinator
559
+ protected
560
+
561
+ # Required: Implement authorization logic
562
+ def authorized?(resource, action, context = {})
563
+ # Return true if user is authorized for resource:action
564
+ # Context provides additional information like controller, params
565
+ end
566
+ end
567
+ ```
568
+
569
+ ### Controller Integration
570
+
571
+ Authorization is **automatically applied** to all Tasker controllers:
572
+
573
+ ```ruby
574
+ # app/controllers/tasker/application_controller.rb
575
+ module Tasker
576
+ class ApplicationController < ActionController::Base
577
+ include Tasker::Concerns::Authenticatable # Authentication
578
+ include Tasker::Concerns::ControllerAuthorizable # Authorization
579
+
580
+ # All controllers automatically inherit both authentication and authorization
581
+ end
582
+ end
583
+ ```
584
+
585
+ ### Permission Checking Flow
586
+
587
+ 1. **Request arrives** at Tasker controller (REST or GraphQL)
588
+ 2. **Authentication** runs first (if enabled)
589
+ 3. **Authorization** extracts `resource:action` from route/operation
590
+ 4. **Coordinator** checks if current user has permission
591
+ 5. **Access granted** (200 OK) or **denied** (403 Forbidden)
592
+
593
+ ### Available Resources and Actions
594
+
595
+ #### Tasks (`tasker.task`)
596
+ - `index` - List all tasks
597
+ - `show` - View specific task
598
+ - `create` - Create new task
599
+ - `update` - Modify existing task
600
+ - `destroy` - Delete task
601
+ - `retry` - Retry failed task
602
+ - `cancel` - Cancel running task
603
+
604
+ #### Workflow Steps (`tasker.workflow_step`)
605
+ - `index` - List workflow steps
606
+ - `show` - View specific step
607
+ - `update` - Modify step
608
+ - `destroy` - Delete step
609
+ - `retry` - Retry failed step
610
+ - `cancel` - Cancel running step
611
+
612
+ ## Building Authorization Coordinators
613
+
614
+ Authorization coordinators provide the business logic for permission checking. Here's how to build effective coordinators:
615
+
616
+ ### Basic Coordinator Structure
617
+
618
+ ```ruby
619
+ class CompanyAuthorizationCoordinator < Tasker::Authorization::BaseCoordinator
620
+ protected
621
+
622
+ def authorized?(resource, action, context = {})
623
+ # Route to resource-specific methods
624
+ case resource
625
+ when Tasker::Authorization::ResourceConstants::RESOURCES::TASK
626
+ authorize_task(action, context)
627
+ when Tasker::Authorization::ResourceConstants::RESOURCES::WORKFLOW_STEP
628
+ authorize_workflow_step(action, context)
629
+ else
630
+ false
631
+ end
632
+ end
633
+
634
+ private
635
+
636
+ def authorize_task(action, context)
637
+ case action
638
+ when :index, :show
639
+ # Read operations - regular users allowed
640
+ user.has_tasker_permission?("tasker.task:#{action}")
641
+ when :create, :update, :destroy, :retry, :cancel
642
+ # Write operations - admin or explicit permission
643
+ user.tasker_admin? || user.has_tasker_permission?("tasker.task:#{action}")
644
+ else
645
+ false
646
+ end
647
+ end
648
+
649
+ def authorize_workflow_step(action, context)
650
+ case action
651
+ when :index, :show
652
+ # Read operations
653
+ user.has_tasker_permission?("tasker.workflow_step:#{action}")
654
+ when :update, :destroy, :retry, :cancel
655
+ # Write operations - admin only for steps
656
+ user.tasker_admin?
657
+ else
658
+ false
659
+ end
660
+ end
661
+ end
662
+ ```
663
+
664
+ ### Advanced Authorization Patterns
665
+
666
+ #### Role-Based Authorization
667
+ ```ruby
668
+ def authorize_task(action, context)
669
+ case user.primary_role
670
+ when 'admin'
671
+ true # Admins can do everything
672
+ when 'manager'
673
+ [:index, :show, :create, :update, :retry].include?(action)
674
+ when 'operator'
675
+ [:index, :show, :retry].include?(action)
676
+ when 'viewer'
677
+ [:index, :show].include?(action)
678
+ else
679
+ false
680
+ end
681
+ end
682
+ ```
683
+
684
+ #### Context-Based Authorization
685
+ ```ruby
686
+ def authorize_task(action, context)
687
+ case action
688
+ when :show, :update, :cancel
689
+ task_id = context[:resource_id]
690
+
691
+ # Users can manage their own tasks
692
+ return true if user_owns_task?(task_id)
693
+
694
+ # Managers can manage team tasks
695
+ return true if user.manager? && team_owns_task?(task_id)
696
+
697
+ # Admins can manage all tasks
698
+ user.tasker_admin?
699
+ end
700
+ end
701
+
702
+ private
703
+
704
+ def user_owns_task?(task_id)
705
+ task = Tasker::Task.find_by(task_id: task_id)
706
+ return false unless task
707
+
708
+ task.context['created_by_user_id'] == user.id.to_s
709
+ end
710
+
711
+ def team_owns_task?(task_id)
712
+ task = Tasker::Task.find_by(task_id: task_id)
713
+ return false unless task
714
+
715
+ team_id = task.context['team_id']
716
+ user.managed_teams.include?(team_id)
717
+ end
718
+ ```
719
+
720
+ #### Time-Based Authorization
721
+ ```ruby
722
+ def authorize_task(action, context)
723
+ # Prevent modifications during maintenance windows
724
+ if maintenance_window_active?
725
+ return [:index, :show].include?(action)
726
+ end
727
+
728
+ # Business hours restrictions for certain actions
729
+ if [:destroy, :cancel].include?(action) && !business_hours?
730
+ return user.tasker_admin?
731
+ end
732
+
733
+ # Standard authorization
734
+ user.has_tasker_permission?("tasker.task:#{action}")
735
+ end
736
+ ```
737
+
738
+ ## Building Custom Authenticators
739
+
740
+ ### Authentication Interface
741
+
742
+ All custom authenticators must implement the `Tasker::Authentication::Interface`:
743
+
744
+ ```ruby
745
+ class YourCustomAuthenticator
746
+ include Tasker::Authentication::Interface
747
+
748
+ def initialize(options = {})
749
+ # Initialize with configuration options
750
+ @options = options
751
+ end
752
+
753
+ # Required: Authenticate the request, raise exception if fails
754
+ def authenticate!(controller)
755
+ # Implementation depends on your authentication system
756
+ # Raise Tasker::Authentication::AuthenticationError if authentication fails
757
+ end
758
+
759
+ # Required: Get the current authenticated user
760
+ def current_user(controller)
761
+ # Return user object or nil
762
+ end
763
+
764
+ # Optional: Check if user is authenticated (uses current_user by default)
765
+ def authenticated?(controller)
766
+ current_user(controller).present?
767
+ end
768
+
769
+ # Optional: Configuration validation
770
+ def validate_configuration(options = {})
771
+ errors = []
772
+ # Add validation logic, return array of error messages
773
+ errors
774
+ end
775
+
776
+ private
777
+
778
+ attr_reader :options
779
+ end
780
+ ```
781
+
782
+ ### Required Methods
783
+
784
+ #### `authenticate!(controller)`
785
+
786
+ **Purpose**: Authenticate the current request and raise an exception if authentication fails.
787
+
788
+ **Parameters**:
789
+ - `controller`: The Rails controller instance
790
+
791
+ **Behavior**:
792
+ - Must raise `Tasker::Authentication::AuthenticationError` if authentication fails
793
+ - Should return truthy value on success
794
+ - Called automatically by the `Authenticatable` concern
795
+
796
+ #### `current_user(controller)`
797
+
798
+ **Purpose**: Return the currently authenticated user object.
799
+
800
+ **Parameters**:
801
+ - `controller`: The Rails controller instance
802
+
803
+ **Returns**:
804
+ - User object if authenticated
805
+ - `nil` if not authenticated
806
+
807
+ **Notes**:
808
+ - Should be memoized for performance
809
+ - User object can be any class that represents your authenticated user
810
+
811
+ ### Optional Methods
812
+
813
+ #### `authenticated?(controller)`
814
+
815
+ **Purpose**: Check if the current request is authenticated.
816
+
817
+ **Default Implementation**: Returns `current_user(controller).present?`
818
+
819
+ **Override**: When you need custom authentication logic beyond user presence.
820
+
821
+ #### `validate_configuration(options = {})`
822
+
823
+ **Purpose**: Validate authenticator-specific configuration options.
824
+
825
+ **Parameters**:
826
+ - `options`: Hash of configuration options
827
+
828
+ **Returns**: Array of error message strings (empty array if valid)
829
+
830
+ **Best Practices**:
831
+ - Check for required configuration options
832
+ - Validate external dependencies (gems, classes)
833
+ - Verify security settings (key lengths, algorithm choices)
834
+
835
+ ## JWT Authentication Example
836
+
837
+ Our `ExampleJWTAuthenticator` demonstrates a production-ready JWT implementation:
838
+
839
+ ### Basic Configuration
840
+
841
+ ```ruby
842
+ # config/initializers/tasker.rb
843
+ Tasker::Configuration.configuration do |config|
844
+ config.auth do |auth|
845
+ auth.authentication_enabled = true
846
+ auth.authenticator_class = 'ExampleJWTAuthenticator'
847
+ auth.user_class = 'User'
848
+ end
849
+ end
850
+
851
+ # Your JWT authenticator can receive configuration in its initialize method:
852
+ class ExampleJWTAuthenticator
853
+ def initialize(options = {})
854
+ @secret = Rails.application.credentials.jwt_secret
855
+ @algorithm = 'HS256'
856
+ @header_name = 'Authorization'
857
+ @user_class = 'User'
858
+ end
859
+ # ... rest of implementation
860
+ end
861
+ ```
862
+
863
+ ### Environment-Specific Configuration
864
+
865
+ ```ruby
866
+ # config/initializers/tasker.rb
867
+ Tasker::Configuration.configuration do |config|
868
+ config.auth do |auth|
869
+ auth.authentication_enabled = true
870
+ auth.authenticator_class = 'ExampleJWTAuthenticator'
871
+ auth.user_class = 'User'
872
+ end
873
+ end
874
+
875
+ # Your JWT authenticator handles environment-specific configuration internally:
876
+ class ExampleJWTAuthenticator
877
+ def initialize(options = {})
878
+ @secret = Rails.env.production? ?
879
+ Rails.application.credentials.jwt_secret :
880
+ 'development-secret-key-32-chars-min'
881
+ @algorithm = Rails.env.production? ? 'HS512' : 'HS256'
882
+ @user_class = 'User'
883
+ end
884
+ # ... rest of implementation
885
+ end
886
+ ```
887
+
888
+ ### Implementation Highlights
889
+
890
+ The `ExampleJWTAuthenticator` includes:
891
+
892
+ ```ruby
893
+ class ExampleJWTAuthenticator
894
+ include Tasker::Authentication::Interface
895
+
896
+ def initialize(options = {})
897
+ @secret = options[:secret]
898
+ @algorithm = options[:algorithm] || 'HS256'
899
+ @header_name = options[:header_name] || 'Authorization'
900
+ @user_class = options[:user_class] || 'User'
901
+ end
902
+
903
+ def authenticate!(controller)
904
+ user = current_user(controller)
905
+ unless user
906
+ raise Tasker::Authentication::AuthenticationError,
907
+ "Invalid or missing JWT token"
908
+ end
909
+ true
910
+ end
911
+
912
+ def current_user(controller)
913
+ return @current_user if defined?(@current_user)
914
+
915
+ @current_user = begin
916
+ token = extract_token(controller.request)
917
+ return nil unless token
918
+
919
+ payload = decode_token(token)
920
+ return nil unless payload
921
+
922
+ find_user(payload)
923
+ rescue JWT::DecodeError, StandardError
924
+ nil
925
+ end
926
+ end
927
+
928
+ def validate_configuration(options = {})
929
+ errors = []
930
+
931
+ # Validate JWT secret
932
+ secret = options[:secret]
933
+ if secret.blank?
934
+ errors << "JWT secret is required"
935
+ elsif secret.length < 32
936
+ errors << "JWT secret should be at least 32 characters for security"
937
+ end
938
+
939
+ # Validate algorithm
940
+ algorithm = options[:algorithm] || 'HS256'
941
+ allowed_algorithms = %w[HS256 HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512]
942
+ unless allowed_algorithms.include?(algorithm)
943
+ errors << "JWT algorithm must be one of: #{allowed_algorithms.join(', ')}"
944
+ end
945
+
946
+ errors
947
+ end
948
+
949
+ private
950
+
951
+ def extract_token(request)
952
+ header = request.headers[@header_name]
953
+ return nil unless header.present?
954
+
955
+ # Support both "Bearer <token>" and raw token formats
956
+ header.start_with?('Bearer ') ? header.sub(/^Bearer /, '') : header
957
+ end
958
+
959
+ def decode_token(token)
960
+ payload, _header = JWT.decode(
961
+ token,
962
+ @secret,
963
+ true, # verify signature
964
+ {
965
+ algorithm: @algorithm,
966
+ verify_expiration: true,
967
+ verify_iat: true
968
+ }
969
+ )
970
+ payload
971
+ rescue JWT::ExpiredSignature, JWT::InvalidIatError
972
+ nil
973
+ end
974
+
975
+ def find_user(payload)
976
+ user_id = payload['user_id'] || payload['sub']
977
+ return nil unless user_id
978
+
979
+ user_model = @user_class.constantize
980
+ user_model.find_by(id: user_id)
981
+ rescue ActiveRecord::RecordNotFound, NoMethodError
982
+ nil
983
+ end
984
+ end
985
+ ```
986
+
987
+ ### Security Features
988
+
989
+ - **Signature Verification**: Validates JWT signatures to prevent tampering
990
+ - **Expiration Checking**: Automatically rejects expired tokens
991
+ - **Algorithm Validation**: Ensures only approved algorithms are used
992
+ - **Secret Length Validation**: Enforces minimum security standards
993
+ - **Error Handling**: Graceful handling of malformed or invalid tokens
994
+
995
+ ## Integration with Controllers
996
+
997
+ ### Automatic Integration
998
+
999
+ Controllers inherit **both authentication and authorization** automatically:
1000
+
1001
+ ```ruby
1002
+ # app/controllers/tasker/application_controller.rb
1003
+ module Tasker
1004
+ class ApplicationController < ActionController::Base
1005
+ include Tasker::Concerns::Authenticatable # Authentication
1006
+ include Tasker::Concerns::ControllerAuthorizable # Authorization
1007
+
1008
+ # Both authentication and authorization happen automatically
1009
+ # before_action :authenticate_tasker_user!
1010
+ # before_action :authorize_tasker_action!
1011
+ end
1012
+ end
1013
+ ```
1014
+
1015
+ ### Automatic Authorization
1016
+
1017
+ All Tasker controllers automatically enforce authorization when enabled:
1018
+
1019
+ ```ruby
1020
+ class Tasker::TasksController < ApplicationController
1021
+ # These actions are automatically protected:
1022
+
1023
+ def index
1024
+ # Requires 'tasker.task:index' permission
1025
+ # Authorization runs before this method
1026
+ end
1027
+
1028
+ def show
1029
+ # Requires 'tasker.task:show' permission
1030
+ end
1031
+
1032
+ def create
1033
+ # Requires 'tasker.task:create' permission
1034
+ end
1035
+
1036
+ def update
1037
+ # Requires 'tasker.task:update' permission
1038
+ end
1039
+
1040
+ def destroy
1041
+ # Requires 'tasker.task:destroy' permission
1042
+ end
1043
+ end
1044
+ ```
1045
+
1046
+ ### Available Helper Methods
1047
+
1048
+ In any Tasker controller, you have access to:
1049
+
1050
+ ```ruby
1051
+ class Tasker::TasksController < ApplicationController
1052
+ def index
1053
+ # Authentication methods
1054
+ if tasker_user_authenticated?
1055
+ user = current_tasker_user # Get current user object
1056
+
1057
+ # Authorization methods
1058
+ coordinator = authorization_coordinator
1059
+ if coordinator.can?('tasker.task', :create)
1060
+ # User can create tasks
1061
+ end
1062
+ end
1063
+ end
1064
+ end
1065
+ ```
1066
+
1067
+ ### Controller Methods
1068
+
1069
+ **Authentication Methods:**
1070
+ - `current_tasker_user`: Returns the current user object (or nil)
1071
+ - `tasker_user_authenticated?`: Returns boolean authentication status
1072
+ - `authenticate_tasker_user!`: Manually trigger authentication (called automatically)
1073
+
1074
+ **Authorization Methods:**
1075
+ - `authorization_coordinator`: Returns the current authorization coordinator
1076
+ - `authorize_tasker_action!`: Manually trigger authorization (called automatically)
1077
+ - `skip_authorization?`: Check if authorization should be skipped
1078
+
1079
+ ### REST API Authorization
1080
+
1081
+ REST endpoints map directly to resource:action permissions:
1082
+
1083
+ ```ruby
1084
+ # HTTP Method + Route = Resource:Action Permission
1085
+
1086
+ GET /tasker/tasks → tasker.task:index
1087
+ GET /tasker/tasks/123 → tasker.task:show
1088
+ POST /tasker/tasks → tasker.task:create
1089
+ PATCH /tasker/tasks/123 → tasker.task:update
1090
+ DELETE /tasker/tasks/123 → tasker.task:destroy
1091
+
1092
+ GET /tasker/tasks/123/workflow_steps → tasker.workflow_step:index
1093
+ GET /tasker/workflow_steps/456 → tasker.workflow_step:show
1094
+ PATCH /tasker/workflow_steps/456 → tasker.workflow_step:update
1095
+ ```
1096
+
1097
+ ### GraphQL Integration
1098
+
1099
+ GraphQL endpoints inherit **both authentication and authorization** with operation-level granular security:
1100
+
1101
+ ```ruby
1102
+ # app/controllers/tasker/graphql_controller.rb
1103
+ module Tasker
1104
+ class GraphqlController < ApplicationController
1105
+ # Inherits Authenticatable and ControllerAuthorizable
1106
+ # Skip standard controller authorization - we handle GraphQL operations manually
1107
+ skip_before_action :authorize_tasker_action!, if: :authorization_enabled?
1108
+
1109
+ def execute
1110
+ # Authentication runs automatically
1111
+ # GraphQL authorization runs per-operation
1112
+
1113
+ # Context includes authenticated user
1114
+ context = {
1115
+ current_user: current_tasker_user,
1116
+ authenticated: tasker_user_authenticated?
1117
+ }
1118
+
1119
+ # Operations are authorized individually
1120
+ result = Tasker::TaskerRailsSchema.execute(query, variables: variables, context: context)
1121
+ render(json: result)
1122
+ end
1123
+ end
1124
+ end
1125
+ ```
1126
+
1127
+ ### GraphQL Resolver Authorization
1128
+
1129
+ In GraphQL resolvers, you have access to authorization context:
1130
+
1131
+ ```ruby
1132
+ # app/graphql/tasker/queries/tasks_query.rb
1133
+ module Tasker
1134
+ module Queries
1135
+ class TasksQuery < BaseQuery
1136
+ def resolve(**args)
1137
+ user = context[:current_user]
1138
+
1139
+ # Authorization was already checked before this resolver runs
1140
+ # The query 'tasks' required 'tasker.task:index' permission
1141
+
1142
+ # Your query logic here
1143
+ Tasker::Task.all
1144
+ end
1145
+ end
1146
+ end
1147
+ end
1148
+ ```
1149
+
1150
+ ### Manual Authorization
1151
+
1152
+ You can also perform manual authorization checks:
1153
+
1154
+ ```ruby
1155
+ class CustomController < Tasker::ApplicationController
1156
+ def custom_action
1157
+ # Manual authorization check
1158
+ coordinator = authorization_coordinator
1159
+
1160
+ unless coordinator.can?('tasker.task', :create)
1161
+ render json: { error: 'Not authorized' }, status: :forbidden
1162
+ return
1163
+ end
1164
+
1165
+ # Proceed with authorized logic
1166
+ end
1167
+ end
1168
+ ```
1169
+
1170
+ ## Error Handling
1171
+
1172
+ ### Authentication Errors
1173
+
1174
+ The system provides standardized error handling:
1175
+
1176
+ ```ruby
1177
+ # HTTP Status Codes
1178
+ 401 Unauthorized # Authentication required or failed
1179
+ 500 Internal Server Error # Configuration or interface errors
1180
+ ```
1181
+
1182
+ ### Error Types
1183
+
1184
+ ```ruby
1185
+ # Authentication failed
1186
+ Tasker::Authentication::AuthenticationError
1187
+ # => 401 Unauthorized response
1188
+
1189
+ # Invalid authenticator configuration
1190
+ Tasker::Authentication::ConfigurationError
1191
+ # => 500 Internal Server Error response
1192
+
1193
+ # Authenticator doesn't implement required interface
1194
+ Tasker::Authentication::InterfaceError
1195
+ # => 500 Internal Server Error response
1196
+ ```
1197
+
1198
+ ### Custom Error Messages
1199
+
1200
+ Authenticators can provide meaningful error messages:
1201
+
1202
+ ```ruby
1203
+ def authenticate!(controller)
1204
+ token = extract_token(controller.request)
1205
+
1206
+ unless token
1207
+ raise Tasker::Authentication::AuthenticationError,
1208
+ "Authorization header missing. Please provide a valid JWT token."
1209
+ end
1210
+
1211
+ unless valid_token?(token)
1212
+ raise Tasker::Authentication::AuthenticationError,
1213
+ "Invalid JWT token. Please check your credentials and try again."
1214
+ end
1215
+ end
1216
+ ```
1217
+
1218
+ ## Testing Authentication
1219
+
1220
+ ### Test Authenticator
1221
+
1222
+ For testing, use the provided `TestAuthenticator`:
1223
+
1224
+ ```ruby
1225
+ # spec/support/authentication_helper.rb
1226
+ RSpec.configure do |config|
1227
+ config.before(:each) do
1228
+ # Reset authentication state
1229
+ TestAuthenticator.reset!
1230
+ end
1231
+ end
1232
+
1233
+ # In tests
1234
+ describe 'authenticated endpoint' do
1235
+ before do
1236
+ # Configure test authentication
1237
+ TestAuthenticator.set_authentication_result(true)
1238
+ TestAuthenticator.set_current_user(TestUser.new(id: 1, name: 'Test User'))
1239
+ end
1240
+
1241
+ it 'allows access for authenticated users' do
1242
+ get '/tasker/tasks'
1243
+ expect(response).to have_http_status(:ok)
1244
+ end
1245
+ end
1246
+
1247
+ describe 'unauthenticated access' do
1248
+ before do
1249
+ TestAuthenticator.set_authentication_result(false)
1250
+ TestAuthenticator.set_current_user(nil)
1251
+ end
1252
+
1253
+ it 'denies access for unauthenticated users' do
1254
+ get '/tasker/tasks'
1255
+ expect(response).to have_http_status(:unauthorized)
1256
+ end
1257
+ end
1258
+ ```
1259
+
1260
+ ### JWT Testing
1261
+
1262
+ For JWT authenticator testing:
1263
+
1264
+ ```ruby
1265
+ # Generate test tokens
1266
+ test_secret = 'test-secret-key-32-characters-plus'
1267
+ user_token = ExampleJWTAuthenticator.generate_test_token(
1268
+ user_id: 1,
1269
+ secret: test_secret
1270
+ )
1271
+
1272
+ # Use in request specs
1273
+ headers = { 'Authorization' => "Bearer #{user_token}" }
1274
+ get '/tasker/tasks', headers: headers
1275
+ ```
1276
+
1277
+ ## Testing Authorization
1278
+
1279
+ ### Authorization Test Setup
1280
+
1281
+ For authorization testing, use comprehensive integration tests:
1282
+
1283
+ ```ruby
1284
+ # spec/support/shared_contexts/configuration_test_isolation.rb
1285
+ RSpec.shared_context 'configuration test isolation' do
1286
+ around(:each) do |example|
1287
+ original_config = Tasker::Configuration.configuration
1288
+ example.run
1289
+ ensure
1290
+ # Reset to clean state
1291
+ Tasker.instance_variable_set(:@configuration, original_config)
1292
+ end
1293
+ end
1294
+
1295
+ # spec/requests/tasker/authorization_integration_spec.rb
1296
+ require 'rails_helper'
1297
+
1298
+ RSpec.describe 'Authorization Integration', type: :request do
1299
+ include_context 'configuration test isolation'
1300
+
1301
+ let(:admin_user) do
1302
+ TestUser.new(
1303
+ id: 1,
1304
+ permissions: [],
1305
+ roles: ['admin'],
1306
+ admin: true
1307
+ )
1308
+ end
1309
+
1310
+ let(:regular_user) do
1311
+ TestUser.new(
1312
+ id: 2,
1313
+ permissions: [
1314
+ 'tasker.task:index',
1315
+ 'tasker.task:show',
1316
+ 'tasker.workflow_step:index'
1317
+ ],
1318
+ roles: ['user'],
1319
+ admin: false
1320
+ )
1321
+ end
1322
+
1323
+ before do
1324
+ configure_tasker_auth(
1325
+ strategy: :custom,
1326
+ options: { authenticator_class: 'TestAuthenticator' },
1327
+ enabled: true,
1328
+ coordinator_class: 'CustomAuthorizationCoordinator'
1329
+ )
1330
+ end
1331
+
1332
+ describe 'with admin user' do
1333
+ before do
1334
+ TestAuthenticator.set_authentication_result(true)
1335
+ TestAuthenticator.set_current_user(admin_user)
1336
+ end
1337
+
1338
+ it 'allows full access to all resources' do
1339
+ get '/tasker/tasks'
1340
+ expect(response).to have_http_status(:ok)
1341
+
1342
+ post '/tasker/tasks', params: { task: valid_task_params }.to_json,
1343
+ headers: { 'Content-Type' => 'application/json' }
1344
+ expect(response).to have_http_status(:created)
1345
+ end
1346
+ end
1347
+
1348
+ describe 'with regular user' do
1349
+ before do
1350
+ TestAuthenticator.set_authentication_result(true)
1351
+ TestAuthenticator.set_current_user(regular_user)
1352
+ end
1353
+
1354
+ it 'allows access to permitted resources' do
1355
+ get '/tasker/tasks'
1356
+ expect(response).to have_http_status(:ok)
1357
+ end
1358
+
1359
+ it 'denies access to forbidden resources' do
1360
+ post '/tasker/tasks', params: { task: valid_task_params }.to_json,
1361
+ headers: { 'Content-Type' => 'application/json' }
1362
+ expect(response).to have_http_status(:forbidden)
1363
+ end
1364
+ end
1365
+ end
1366
+ ```
1367
+
1368
+ ### GraphQL Authorization Testing
1369
+
1370
+ ```ruby
1371
+ describe 'GraphQL Authorization' do
1372
+ it 'allows authorized GraphQL operations' do
1373
+ TestAuthenticator.set_current_user(admin_user)
1374
+
1375
+ post '/tasker/graphql', params: {
1376
+ query: 'query { tasks { taskId status } }'
1377
+ }
1378
+ expect(response).to have_http_status(:ok)
1379
+ end
1380
+
1381
+ it 'blocks unauthorized GraphQL operations' do
1382
+ TestAuthenticator.set_current_user(regular_user)
1383
+
1384
+ post '/tasker/graphql', params: {
1385
+ query: 'mutation { createTask(input: { name: "test" }) { taskId } }'
1386
+ }
1387
+ expect(response).to have_http_status(:forbidden)
1388
+ end
1389
+
1390
+ it 'handles mixed operations correctly' do
1391
+ TestAuthenticator.set_current_user(regular_user)
1392
+
1393
+ # User has tasker.task:index but not tasker.workflow_step:index
1394
+ post '/tasker/graphql', params: {
1395
+ query: 'query { tasks { taskId workflowSteps { workflowStepId } } }'
1396
+ }
1397
+ expect(response).to have_http_status(:forbidden)
1398
+ end
1399
+ end
1400
+ ```
1401
+
1402
+ ### Custom Authorization Coordinator Testing
1403
+
1404
+ ```ruby
1405
+ describe CustomAuthorizationCoordinator do
1406
+ let(:coordinator) { described_class.new(user) }
1407
+
1408
+ describe 'task authorization' do
1409
+ context 'with admin user' do
1410
+ let(:user) { admin_user }
1411
+
1412
+ it 'allows all task operations' do
1413
+ expect(coordinator.can?('tasker.task', :index)).to be true
1414
+ expect(coordinator.can?('tasker.task', :create)).to be true
1415
+ expect(coordinator.can?('tasker.task', :destroy)).to be true
1416
+ end
1417
+ end
1418
+
1419
+ context 'with regular user' do
1420
+ let(:user) { regular_user }
1421
+
1422
+ it 'allows read operations' do
1423
+ expect(coordinator.can?('tasker.task', :index)).to be true
1424
+ expect(coordinator.can?('tasker.task', :show)).to be true
1425
+ end
1426
+
1427
+ it 'denies write operations' do
1428
+ expect(coordinator.can?('tasker.task', :create)).to be false
1429
+ expect(coordinator.can?('tasker.task', :destroy)).to be false
1430
+ end
1431
+ end
1432
+ end
1433
+ end
1434
+ ```
1435
+
1436
+ ### State Isolation
1437
+
1438
+ Ensure tests don't leak configuration state:
1439
+
1440
+ ```ruby
1441
+ # spec/rails_helper.rb (automatic cleanup)
1442
+ RSpec.configure do |config|
1443
+ config.after(:each) do
1444
+ # Automatic cleanup of authentication/authorization state
1445
+ if defined?(Tasker) && Tasker.respond_to?(:configuration)
1446
+ current_config = Tasker::Configuration.configuration
1447
+ if current_config&.auth&.authorization_enabled == true
1448
+ needs_reset = true
1449
+ end
1450
+
1451
+ if current_config&.auth&.authentication_enabled == true
1452
+ authenticator_class = current_config.auth.authenticator_class
1453
+ needs_reset = true if authenticator_class&.include?('Test')
1454
+ end
1455
+
1456
+ if needs_reset
1457
+ Tasker::Configuration.configuration do |config|
1458
+ config.auth.authentication_enabled = false
1459
+ config.auth.authorization_enabled = false
1460
+ config.auth.authenticator_class = nil
1461
+ config.auth.authorization_coordinator_class = nil
1462
+ end
1463
+ end
1464
+ end
1465
+ end
1466
+ end
1467
+ ```
1468
+
1469
+ ## Best Practices
1470
+
1471
+ ### Security
1472
+
1473
+ #### Authentication Security
1474
+ 1. **Use Strong Secrets**: Minimum 32 characters for JWT secrets
1475
+ 2. **Choose Secure Algorithms**: Prefer HS256/HS512 for HMAC, RS256+ for RSA
1476
+ 3. **Validate Configuration**: Implement `validate_configuration` for security checks
1477
+ 4. **Handle Errors Gracefully**: Never expose sensitive information in error messages
1478
+ 5. **Implement Token Expiration**: Always set reasonable expiration times
1479
+
1480
+ #### Authorization Security
1481
+ 1. **Default Deny**: Always default to denying access unless explicitly granted
1482
+ 2. **Resource-Specific Logic**: Implement granular permissions per resource type
1483
+ 3. **Context-Aware Authorization**: Use context for ownership and relationship checks
1484
+ 4. **Admin Override Pattern**: Allow admins to bypass specific restrictions safely
1485
+ 5. **Audit Trails**: Log authorization decisions for security monitoring
1486
+
1487
+ ```ruby
1488
+ # Good: Default deny with explicit grants
1489
+ def authorize_task(action, context)
1490
+ return false unless user.present? # Default deny
1491
+
1492
+ case action
1493
+ when :index, :show
1494
+ user.has_tasker_permission?("tasker.task:#{action}")
1495
+ when :create, :update, :destroy
1496
+ user.tasker_admin? || user.has_tasker_permission?("tasker.task:#{action}")
1497
+ else
1498
+ false # Explicit deny for unknown actions
1499
+ end
1500
+ end
1501
+
1502
+ # Bad: Default allow
1503
+ def authorize_task(action, context)
1504
+ return true if user.tasker_admin? # Too broad
1505
+ # ... other logic
1506
+ end
1507
+ ```
1508
+
1509
+ ### Performance
1510
+
1511
+ #### Authentication Performance
1512
+ 1. **Memoize User Lookups**: Cache user objects within request scope
1513
+ 2. **Efficient Database Queries**: Use `find_by` instead of exceptions for user lookup
1514
+ 3. **Minimal Token Validation**: Only decode/validate tokens once per request
1515
+
1516
+ #### Authorization Performance
1517
+ 1. **Cache Permission Checks**: Memoize authorization decisions within request scope
1518
+ 2. **Efficient Permission Storage**: Use optimized data structures for permission lookups
1519
+ 3. **Minimal Database Hits**: Load user permissions once per request
1520
+ 4. **Smart GraphQL Batching**: Group permission checks for related operations
1521
+
1522
+ ```ruby
1523
+ # Good: Memoized authorization
1524
+ class YourAuthorizationCoordinator < Tasker::Authorization::BaseCoordinator
1525
+ def can?(resource, action, context = {})
1526
+ cache_key = "#{resource}:#{action}"
1527
+ @authorization_cache ||= {}
1528
+ @authorization_cache[cache_key] ||= super
1529
+ end
1530
+ end
1531
+
1532
+ # Good: Efficient permission checking
1533
+ def user_permissions
1534
+ @user_permissions ||= user.permissions.to_set
1535
+ end
1536
+
1537
+ def has_permission?(permission)
1538
+ user_permissions.include?(permission)
1539
+ end
1540
+ ```
1541
+
1542
+ ### Development
1543
+
1544
+ #### General Development
1545
+ 1. **Use Different Configs for Environments**: Separate dev/test/production settings
1546
+ 2. **Provide Clear Error Messages**: Help developers debug configuration issues
1547
+ 3. **Document Your Authenticator**: Include usage examples and configuration options
1548
+ 4. **Test Edge Cases**: Expired tokens, malformed headers, missing users
1549
+
1550
+ #### Authorization Development
1551
+ 1. **Resource Constants**: Always use `ResourceConstants` instead of hardcoded strings
1552
+ 2. **Comprehensive Testing**: Test both positive and negative authorization scenarios
1553
+ 3. **Clear Coordinator Logic**: Separate resource authorization into dedicated methods
1554
+ 4. **Context Documentation**: Document what context information your coordinator uses
1555
+
1556
+ ```ruby
1557
+ # Good: Using constants
1558
+ when Tasker::Authorization::ResourceConstants::RESOURCES::TASK
1559
+ authorize_task_action(action, context)
1560
+
1561
+ # Bad: Hardcoded strings
1562
+ when 'tasker.task'
1563
+ authorize_task_action(action, context)
1564
+ ```
1565
+
1566
+ ### Code Organization
1567
+
1568
+ ```ruby
1569
+ # Recommended file structure
1570
+ app/
1571
+ tasker/
1572
+ authenticators/
1573
+ company_jwt_authenticator.rb
1574
+ company_devise_authenticator.rb
1575
+ authorization/
1576
+ company_authorization_coordinator.rb
1577
+ models/
1578
+ user.rb # Include Tasker::Concerns::Authorizable
1579
+ config/
1580
+ initializers/
1581
+ tasker.rb # Authentication & authorization configuration
1582
+
1583
+ # Authorization coordinator organization
1584
+ class CompanyAuthorizationCoordinator < Tasker::Authorization::BaseCoordinator
1585
+ protected
1586
+
1587
+ def authorized?(resource, action, context = {})
1588
+ case resource
1589
+ when ResourceConstants::RESOURCES::TASK
1590
+ authorize_task(action, context)
1591
+ when ResourceConstants::RESOURCES::WORKFLOW_STEP
1592
+ authorize_workflow_step(action, context)
1593
+ else
1594
+ false
1595
+ end
1596
+ end
1597
+
1598
+ private
1599
+
1600
+ # Separate methods for each resource type
1601
+ def authorize_task(action, context)
1602
+ # Task-specific authorization logic
1603
+ end
1604
+
1605
+ def authorize_workflow_step(action, context)
1606
+ # Workflow step authorization logic
1607
+ end
1608
+ end
1609
+ ```
1610
+
1611
+ ### Production Considerations
1612
+
1613
+ #### Monitoring & Observability
1614
+ 1. **Log Authorization Failures**: Track unauthorized access attempts
1615
+ 2. **Monitor Performance**: Track authorization overhead
1616
+ 3. **Alert on Anomalies**: Detect unusual permission patterns
1617
+ 4. **Audit Admin Actions**: Log all administrative overrides
1618
+
1619
+ #### Scaling Authorization
1620
+ 1. **Permission Caching**: Cache user permissions with appropriate TTL
1621
+ 2. **Database Optimization**: Index permission lookup columns
1622
+ 3. **Background Processing**: Refresh permissions asynchronously when possible
1623
+ 4. **Circuit Breakers**: Graceful degradation when authorization services are unavailable
1624
+
1625
+ ### Example Authenticator Template
1626
+
1627
+ ```ruby
1628
+ class YourAuthenticator
1629
+ include Tasker::Authentication::Interface
1630
+
1631
+ def initialize(options = {})
1632
+ @options = options
1633
+ # Initialize your authenticator
1634
+ end
1635
+
1636
+ def authenticate!(controller)
1637
+ # Your authentication logic
1638
+ user = current_user(controller)
1639
+ unless user
1640
+ raise Tasker::Authentication::AuthenticationError, "Authentication failed"
1641
+ end
1642
+ true
1643
+ end
1644
+
1645
+ def current_user(controller)
1646
+ return @current_user if defined?(@current_user)
1647
+
1648
+ @current_user = begin
1649
+ # Your user lookup logic
1650
+ rescue StandardError
1651
+ nil
1652
+ end
1653
+ end
1654
+
1655
+ def validate_configuration(options = {})
1656
+ errors = []
1657
+ # Add your validation logic
1658
+ errors
1659
+ end
1660
+
1661
+ private
1662
+
1663
+ attr_reader :options
1664
+
1665
+ # Your private helper methods
1666
+ end
1667
+ ```
1668
+
1669
+ ## Common Integration Patterns
1670
+
1671
+ ### Devise Integration
1672
+
1673
+ ```ruby
1674
+ class DeviseAuthenticator
1675
+ include Tasker::Authentication::Interface
1676
+
1677
+ def initialize(options = {})
1678
+ @scope = options[:scope] || :user
1679
+ end
1680
+
1681
+ def authenticate!(controller)
1682
+ controller.send("authenticate_#{@scope}!")
1683
+ end
1684
+
1685
+ def current_user(controller)
1686
+ controller.send("current_#{@scope}")
1687
+ end
1688
+
1689
+ def validate_configuration(options = {})
1690
+ errors = []
1691
+ unless defined?(Devise)
1692
+ errors << "Devise gem is required for DeviseAuthenticator"
1693
+ end
1694
+ errors
1695
+ end
1696
+
1697
+ private
1698
+
1699
+ attr_reader :scope
1700
+ end
1701
+ ```
1702
+
1703
+ ### API Token Authentication
1704
+
1705
+ ```ruby
1706
+ class ApiTokenAuthenticator
1707
+ include Tasker::Authentication::Interface
1708
+
1709
+ def initialize(options = {})
1710
+ @header_name = options[:header_name] || 'X-API-Token'
1711
+ @user_class = options[:user_class] || 'User'
1712
+ end
1713
+
1714
+ def authenticate!(controller)
1715
+ user = current_user(controller)
1716
+ unless user
1717
+ raise Tasker::Authentication::AuthenticationError, "Invalid API token"
1718
+ end
1719
+ true
1720
+ end
1721
+
1722
+ def current_user(controller)
1723
+ return @current_user if defined?(@current_user)
1724
+
1725
+ @current_user = begin
1726
+ token = controller.request.headers[@header_name]
1727
+ return nil unless token
1728
+
1729
+ user_class.constantize.find_by(api_token: token)
1730
+ rescue ActiveRecord::RecordNotFound
1731
+ nil
1732
+ end
1733
+ end
1734
+
1735
+ private
1736
+
1737
+ attr_reader :header_name, :user_class
1738
+ end
1739
+ ```
1740
+
1741
+ This authentication system provides the flexibility to integrate with any authentication solution while maintaining security, performance, and developer experience. The dependency injection pattern ensures that Tasker remains authentication-agnostic while providing a robust foundation for secure applications.