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