seams 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +335 -0
- data/LICENSE +21 -0
- data/README.md +104 -0
- data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
- data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
- data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
- data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
- data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
- data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
- data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
- data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
- data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
- data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
- data/lib/generators/seams/admin/admin_generator.rb +852 -0
- data/lib/generators/seams/admin/templates/README.md.tt +266 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
- data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
- data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
- data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
- data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
- data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
- data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
- data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
- data/lib/generators/seams/auth/auth_generator.rb +311 -0
- data/lib/generators/seams/auth/templates/README.md.tt +289 -0
- data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
- data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
- data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
- data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
- data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
- data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
- data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
- data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
- data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
- data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
- data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
- data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
- data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
- data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
- data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
- data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
- data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
- data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
- data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
- data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
- data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
- data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
- data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
- data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
- data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
- data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
- data/lib/generators/seams/billing/billing_generator.rb +476 -0
- data/lib/generators/seams/billing/templates/README.md.tt +355 -0
- data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
- data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
- data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
- data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
- data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
- data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
- data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
- data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
- data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
- data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
- data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
- data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
- data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
- data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
- data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
- data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
- data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
- data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
- data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
- data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
- data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
- data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
- data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
- data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
- data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
- data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
- data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
- data/lib/generators/seams/core/core_generator.rb +191 -0
- data/lib/generators/seams/core/templates/README.md.tt +45 -0
- data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
- data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
- data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
- data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
- data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
- data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
- data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
- data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
- data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
- data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
- data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
- data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
- data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
- data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
- data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
- data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
- data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
- data/lib/generators/seams/engine/engine_generator.rb +165 -0
- data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
- data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
- data/lib/generators/seams/engine/templates/README.md.tt +40 -0
- data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
- data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
- data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
- data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
- data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
- data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
- data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
- data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
- data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
- data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
- data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
- data/lib/generators/seams/install/install_generator.rb +211 -0
- data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
- data/lib/generators/seams/install/templates/Procfile.tt +14 -0
- data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
- data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
- data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
- data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
- data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
- data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
- data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
- data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
- data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
- data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
- data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
- data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
- data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
- data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
- data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
- data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
- data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
- data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
- data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
- data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
- data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
- data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
- data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
- data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
- data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
- data/lib/generators/seams/remove/remove_generator.rb +259 -0
- data/lib/generators/seams/teams/teams_generator.rb +298 -0
- data/lib/generators/seams/teams/templates/README.md.tt +88 -0
- data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
- data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
- data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
- data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
- data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
- data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
- data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
- data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
- data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
- data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
- data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
- data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
- data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
- data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
- data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
- data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
- data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
- data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
- data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
- data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
- data/lib/seams/cli/list.rb +111 -0
- data/lib/seams/cli/quality.rb +99 -0
- data/lib/seams/cli/resolve.rb +276 -0
- data/lib/seams/cli/test_changed.rb +116 -0
- data/lib/seams/cli.rb +48 -0
- data/lib/seams/configuration.rb +19 -0
- data/lib/seams/cops/known_queue_names.rb +42 -0
- data/lib/seams/cops/migration_comments.rb +68 -0
- data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
- data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
- data/lib/seams/cops.rb +18 -0
- data/lib/seams/event_registry.rb +49 -0
- data/lib/seams/events/adapter.rb +24 -0
- data/lib/seams/events/adapters/active_support.rb +31 -0
- data/lib/seams/events/publisher.rb +178 -0
- data/lib/seams/events.rb +39 -0
- data/lib/seams/generators/dummy_app_writer.rb +424 -0
- data/lib/seams/generators/eject_aware.rb +102 -0
- data/lib/seams/generators/follow_up_generator.rb +148 -0
- data/lib/seams/generators/host_injector.rb +124 -0
- data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
- data/lib/seams/generators/splicer.rb +217 -0
- data/lib/seams/observability/adapter.rb +33 -0
- data/lib/seams/observability/adapters/rails_logger.rb +59 -0
- data/lib/seams/observability.rb +34 -0
- data/lib/seams/runtime.rb +23 -0
- data/lib/seams/version.rb +5 -0
- data/lib/seams.rb +23 -0
- metadata +493 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "seams"
|
|
5
|
+
require "seams/generators/follow_up_generator"
|
|
6
|
+
|
|
7
|
+
module Seams
|
|
8
|
+
module Generators
|
|
9
|
+
module Auth
|
|
10
|
+
# Adds a new OAuth provider adapter to an already-generated Auth
|
|
11
|
+
# engine. The first showcase follow-up generator built on top of
|
|
12
|
+
# the Wave 10 Phase 1 insertion-point machinery.
|
|
13
|
+
#
|
|
14
|
+
# Run with:
|
|
15
|
+
#
|
|
16
|
+
# bin/rails generate seams:auth:add_oauth_provider linkedin
|
|
17
|
+
# bin/rails generate seams:auth:add_oauth_provider apple
|
|
18
|
+
# bin/rails generate seams:auth:add_oauth_provider microsoft
|
|
19
|
+
#
|
|
20
|
+
# What it does:
|
|
21
|
+
#
|
|
22
|
+
# 1. Creates engines/auth/lib/auth/oauth/<name>.rb (subclasses
|
|
23
|
+
# Auth::OAuth::Abstract, with TODOs the host fills in).
|
|
24
|
+
# 2. Splices a configuration entry into
|
|
25
|
+
# engines/auth/lib/auth/configuration.rb at the
|
|
26
|
+
# `auth.configuration.oauth_providers` marker.
|
|
27
|
+
# 3. Creates engines/auth/spec/lib/auth/oauth/<name>_spec.rb
|
|
28
|
+
# covering the abstract contract.
|
|
29
|
+
#
|
|
30
|
+
# The existing `scope "/oauth/:provider"` route in
|
|
31
|
+
# engines/auth/config/routes.rb already handles every configured
|
|
32
|
+
# provider via the `:provider` param — the generator does NOT
|
|
33
|
+
# splice into auth.routes.after_oauth or any other route marker.
|
|
34
|
+
# Likewise, the `_oauth_buttons.html.erb` partial iterates
|
|
35
|
+
# `Auth.configuration.oauth_providers.each_key`, so the new entry
|
|
36
|
+
# auto-renders without a partial edit.
|
|
37
|
+
#
|
|
38
|
+
# Idempotent on rerun: the underlying Splicer detects the splice
|
|
39
|
+
# has already happened and skips. The adapter file itself is
|
|
40
|
+
# written via `template`, which Thor's --skip flag (default)
|
|
41
|
+
# leaves alone if it already exists.
|
|
42
|
+
class AddOauthProviderGenerator < Seams::Generators::FollowUpGenerator
|
|
43
|
+
engine_name "auth"
|
|
44
|
+
|
|
45
|
+
source_root File.expand_path("templates", __dir__)
|
|
46
|
+
|
|
47
|
+
argument :name, type: :string, banner: "<provider>",
|
|
48
|
+
desc: "The provider name (e.g. linkedin, apple, microsoft)"
|
|
49
|
+
|
|
50
|
+
# Provider names share the engine-name regex: lowercase letters,
|
|
51
|
+
# digits, underscores, starting with a letter. Hyphens, mixed
|
|
52
|
+
# case, and dots are normalised to underscores by `snake_name`.
|
|
53
|
+
NAME_PATTERN = /\A[a-z][a-z0-9_]*\z/
|
|
54
|
+
|
|
55
|
+
CONFIGURATION_FILE = "lib/auth/configuration.rb"
|
|
56
|
+
CONFIGURATION_MARKER = "auth.configuration.oauth_providers"
|
|
57
|
+
|
|
58
|
+
def normalise_name
|
|
59
|
+
@snake_name = name.to_s.strip.downcase
|
|
60
|
+
.gsub(/[^a-z0-9]+/, "_").squeeze("_")
|
|
61
|
+
.gsub(/^_|_$/, "")
|
|
62
|
+
|
|
63
|
+
if @snake_name.empty?
|
|
64
|
+
raise Seams::GeneratorError,
|
|
65
|
+
"Provider name #{name.inspect} normalises to an empty string. " \
|
|
66
|
+
"Pass a name like `linkedin`, `apple`, or `microsoft`."
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
return if NAME_PATTERN.match?(@snake_name)
|
|
70
|
+
|
|
71
|
+
raise Seams::GeneratorError,
|
|
72
|
+
"Provider name #{name.inspect} must be lowercase letters, digits, " \
|
|
73
|
+
"and underscores, starting with a letter."
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def assert_engine_present
|
|
77
|
+
assert_marker_exists!(file: CONFIGURATION_FILE, marker: CONFIGURATION_MARKER)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def create_adapter
|
|
81
|
+
template "adapter.rb.tt", engine_path("lib/auth/oauth/#{snake_name}.rb")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def splice_configuration_entry
|
|
85
|
+
splice(
|
|
86
|
+
file: CONFIGURATION_FILE,
|
|
87
|
+
marker: CONFIGURATION_MARKER,
|
|
88
|
+
content: configuration_entry
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def create_adapter_spec
|
|
93
|
+
template "adapter_spec.rb.tt",
|
|
94
|
+
engine_path("spec/lib/auth/oauth/#{snake_name}_spec.rb")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# rubocop:disable Metrics/AbcSize
|
|
98
|
+
def report_summary
|
|
99
|
+
say ""
|
|
100
|
+
say " Auth OAuth provider `#{snake_name}` added.", :green
|
|
101
|
+
say ""
|
|
102
|
+
say " Files:", :yellow
|
|
103
|
+
say " engines/auth/lib/auth/oauth/#{snake_name}.rb"
|
|
104
|
+
say " engines/auth/spec/lib/auth/oauth/#{snake_name}_spec.rb"
|
|
105
|
+
say " Spliced:", :yellow
|
|
106
|
+
say " engines/auth/lib/auth/configuration.rb @ #{CONFIGURATION_MARKER}"
|
|
107
|
+
say ""
|
|
108
|
+
say " Routes: the existing `scope \"/oauth/:provider\"` block matches", :blue
|
|
109
|
+
say " every configured provider — no route splice needed."
|
|
110
|
+
say " View: the engine's _oauth_buttons.html.erb partial iterates", :blue
|
|
111
|
+
say " Auth.configuration.oauth_providers — no partial edit needed."
|
|
112
|
+
say ""
|
|
113
|
+
say " Next steps:", :yellow
|
|
114
|
+
say " 1. Set #{upper_name}_OAUTH_CLIENT_ID and #{upper_name}_OAUTH_CLIENT_SECRET in your environment."
|
|
115
|
+
say " 2. Replace the TODO placeholders in lib/auth/oauth/#{snake_name}.rb with"
|
|
116
|
+
say " the provider's real OAuth endpoints + userinfo mapping."
|
|
117
|
+
say " 3. Run the spec: bin/rails seams:test[auth] (or rspec the new file)."
|
|
118
|
+
say " 4. Test the OAuth flow against the provider's sandbox."
|
|
119
|
+
say ""
|
|
120
|
+
say " Want to customise the adapter beyond the TODOs (override behaviour, change the", :blue
|
|
121
|
+
say " spec shape, etc.)? Eject it so subsequent regenerations of auth leave it alone:"
|
|
122
|
+
say " bin/seams resolve --eject auth/lib/auth/oauth/#{snake_name}.rb"
|
|
123
|
+
say ""
|
|
124
|
+
end
|
|
125
|
+
# rubocop:enable Metrics/AbcSize
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
# Exposed to the templates via ERB binding.
|
|
130
|
+
attr_reader :snake_name
|
|
131
|
+
|
|
132
|
+
def camel_name
|
|
133
|
+
snake_name.split("_").map(&:capitalize).join
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def upper_name
|
|
137
|
+
snake_name.upcase
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# The hash entry to splice after the marker. Two-space indent
|
|
141
|
+
# inside the entry so the splicer's auto-detected outer indent
|
|
142
|
+
# (8 spaces, matching the marker line) lands the inner keys at
|
|
143
|
+
# 10 spaces — same shape Phase 2A's marker placement implies.
|
|
144
|
+
def configuration_entry
|
|
145
|
+
<<~RUBY
|
|
146
|
+
#{snake_name}: {
|
|
147
|
+
adapter: "Auth::OAuth::#{camel_name}",
|
|
148
|
+
client_id: ENV.fetch("#{upper_name}_OAUTH_CLIENT_ID", nil),
|
|
149
|
+
client_secret: ENV.fetch("#{upper_name}_OAUTH_CLIENT_SECRET", nil),
|
|
150
|
+
scopes: %w[profile email]
|
|
151
|
+
},
|
|
152
|
+
RUBY
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "auth/oauth/abstract"
|
|
4
|
+
|
|
5
|
+
module Auth
|
|
6
|
+
module OAuth
|
|
7
|
+
# <%= camel_name %> OAuth 2.0 adapter — generated by
|
|
8
|
+
# `bin/rails generate seams:auth:add_oauth_provider <%= snake_name %>`.
|
|
9
|
+
#
|
|
10
|
+
# This is a starting point: the URLs, scopes, and userinfo mapping
|
|
11
|
+
# below are placeholders. Fill them in by following the provider's
|
|
12
|
+
# OAuth documentation, then delete each TODO once verified.
|
|
13
|
+
#
|
|
14
|
+
# Provider docs to consult (replace with the canonical URLs):
|
|
15
|
+
#
|
|
16
|
+
# https://example.com/<%= snake_name %>/oauth/docs
|
|
17
|
+
# https://example.com/<%= snake_name %>/oauth/userinfo
|
|
18
|
+
#
|
|
19
|
+
# The host wires this adapter via Auth.configuration.oauth_providers
|
|
20
|
+
# (the generator already added a stub entry there). Set the
|
|
21
|
+
# <%= upper_name %>_OAUTH_CLIENT_ID and <%= upper_name %>_OAUTH_CLIENT_SECRET environment
|
|
22
|
+
# variables before exercising the OAuth flow.
|
|
23
|
+
class <%= camel_name %> < Abstract
|
|
24
|
+
# TODO(<%= snake_name %>): replace with the provider's authorization endpoint.
|
|
25
|
+
AUTHORIZE_URL = "https://example.com/<%= snake_name %>/oauth/authorize"
|
|
26
|
+
# TODO(<%= snake_name %>): replace with the provider's token endpoint.
|
|
27
|
+
TOKEN_URL = "https://example.com/<%= snake_name %>/oauth/token"
|
|
28
|
+
# TODO(<%= snake_name %>): replace with the provider's userinfo endpoint.
|
|
29
|
+
USERINFO_URL = "https://example.com/<%= snake_name %>/oauth/userinfo"
|
|
30
|
+
|
|
31
|
+
# TODO(<%= snake_name %>): pick the minimum scopes that yield email + a stable
|
|
32
|
+
# provider_uid. Most providers gate `email` behind a named scope.
|
|
33
|
+
DEFAULT_SCOPES = %w[profile email].freeze
|
|
34
|
+
|
|
35
|
+
def initialize(client_id:, client_secret:, scopes: DEFAULT_SCOPES)
|
|
36
|
+
super
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# TODO(<%= snake_name %>): tailor params to the provider. Many providers want
|
|
40
|
+
# `response_type=code` + `state=<csrf>` + `scope=<space-joined>`;
|
|
41
|
+
# some require `response_mode`, `nonce`, or `audience`.
|
|
42
|
+
def authorize_url(state:, redirect_uri:)
|
|
43
|
+
params = {
|
|
44
|
+
client_id: client_id,
|
|
45
|
+
redirect_uri: redirect_uri,
|
|
46
|
+
response_type: "code",
|
|
47
|
+
scope: scopes.join(" "),
|
|
48
|
+
state: state
|
|
49
|
+
}
|
|
50
|
+
"#{AUTHORIZE_URL}?#{URI.encode_www_form(params)}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# TODO(<%= snake_name %>): some providers want JSON, some want form-urlencoded;
|
|
54
|
+
# some require `Authorization: Basic` instead of body credentials.
|
|
55
|
+
# Verify against the provider's token-exchange docs.
|
|
56
|
+
def exchange_code(code:, redirect_uri:)
|
|
57
|
+
response = conn(TOKEN_URL).post("", {
|
|
58
|
+
client_id: client_id,
|
|
59
|
+
client_secret: client_secret,
|
|
60
|
+
code: code,
|
|
61
|
+
redirect_uri: redirect_uri,
|
|
62
|
+
grant_type: "authorization_code"
|
|
63
|
+
})
|
|
64
|
+
assert_success!(response, action: "token exchange")
|
|
65
|
+
body = parse_json(response, action: "token exchange")
|
|
66
|
+
{
|
|
67
|
+
access_token: body["access_token"],
|
|
68
|
+
refresh_token: body["refresh_token"],
|
|
69
|
+
expires_in: body["expires_in"],
|
|
70
|
+
token_type: body["token_type"] || "Bearer",
|
|
71
|
+
scope: body["scope"]
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# TODO(<%= snake_name %>): map the provider's userinfo response onto Profile.
|
|
76
|
+
# `provider_uid` MUST be stable across logins (an email isn't —
|
|
77
|
+
# users can change them); prefer the provider's numeric/uuid id.
|
|
78
|
+
def fetch_user_info(access_token:)
|
|
79
|
+
response = conn(USERINFO_URL).get("") do |req|
|
|
80
|
+
req.headers["Authorization"] = "Bearer #{access_token}"
|
|
81
|
+
end
|
|
82
|
+
assert_success!(response, action: "userinfo")
|
|
83
|
+
body = parse_json(response, action: "userinfo")
|
|
84
|
+
Profile.new(
|
|
85
|
+
provider_uid: body["id"] || body["sub"],
|
|
86
|
+
email: body["email"],
|
|
87
|
+
email_verified: body["email_verified"],
|
|
88
|
+
name: body["name"],
|
|
89
|
+
avatar_url: body["avatar_url"] || body["picture"],
|
|
90
|
+
raw: body
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails_helper"
|
|
4
|
+
require "auth/oauth/<%= snake_name %>"
|
|
5
|
+
|
|
6
|
+
RSpec.describe Auth::OAuth::<%= camel_name %> do
|
|
7
|
+
let(:client_id) { "test-client-id" }
|
|
8
|
+
let(:client_secret) { "test-client-secret" }
|
|
9
|
+
|
|
10
|
+
subject(:adapter) do
|
|
11
|
+
described_class.new(
|
|
12
|
+
client_id: client_id,
|
|
13
|
+
client_secret: client_secret
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "the abstract contract" do
|
|
18
|
+
it "is a subclass of Auth::OAuth::Abstract" do
|
|
19
|
+
expect(described_class.ancestors).to include(Auth::OAuth::Abstract)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "exposes default scopes appropriate for the provider" do
|
|
23
|
+
expect(described_class::DEFAULT_SCOPES).to be_a(Array)
|
|
24
|
+
expect(described_class::DEFAULT_SCOPES).not_to be_empty
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "#authorize_url" do
|
|
29
|
+
# Until the TODOs in lib/auth/oauth/<%= snake_name %>.rb are filled in,
|
|
30
|
+
# the URL still points at the example.com placeholder. This test
|
|
31
|
+
# documents that — and fails the moment a host overrides the
|
|
32
|
+
# AUTHORIZE_URL with a real endpoint, which is when this spec
|
|
33
|
+
# should be replaced with provider-specific assertions.
|
|
34
|
+
it "builds an authorize URL containing the state and redirect_uri" do
|
|
35
|
+
url = adapter.authorize_url(state: "csrf-token", redirect_uri: "https://host.example/callback")
|
|
36
|
+
|
|
37
|
+
expect(url).to include("state=csrf-token")
|
|
38
|
+
expect(url).to include("redirect_uri=")
|
|
39
|
+
expect(url).to include("client_id=#{client_id}")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "#exchange_code" do
|
|
44
|
+
# TODO(<%= snake_name %>): replace this with a Faraday stub once the
|
|
45
|
+
# provider's token endpoint is wired up.
|
|
46
|
+
it "is implemented (raises only if the abstract methods are kept)" do
|
|
47
|
+
expect(adapter.method(:exchange_code).owner).to eq(described_class)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "#fetch_user_info" do
|
|
52
|
+
# TODO(<%= snake_name %>): replace this with a Faraday stub once the
|
|
53
|
+
# provider's userinfo endpoint is wired up.
|
|
54
|
+
it "is implemented (raises only if the abstract methods are kept)" do
|
|
55
|
+
expect(adapter.method(:fetch_user_info).owner).to eq(described_class)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "rails/generators"
|
|
5
|
+
require "seams"
|
|
6
|
+
require "generators/seams/engine/engine_generator"
|
|
7
|
+
require "seams/generators/host_injector"
|
|
8
|
+
require "seams/generators/eject_aware"
|
|
9
|
+
require "seams/generators/dummy_app_writer"
|
|
10
|
+
|
|
11
|
+
module Seams
|
|
12
|
+
module Generators
|
|
13
|
+
# Generates a canonical Auth engine on top of the generic engine
|
|
14
|
+
# scaffold. Adds:
|
|
15
|
+
#
|
|
16
|
+
# - Identity and Session ActiveRecord models (has_secure_password).
|
|
17
|
+
# - SessionsController + RegistrationsController with sign in /
|
|
18
|
+
# sign up / sign out.
|
|
19
|
+
# - Authenticatable concern that the host application's user-like
|
|
20
|
+
# model can `include Auth::Authenticatable` (added to the
|
|
21
|
+
# engine's ExposedConcerns automatically) — OPTIONAL post-Wave-9.
|
|
22
|
+
# - Migrations for auth_identities and auth_sessions.
|
|
23
|
+
# - lib/auth/engine.rb registers the canonical events:
|
|
24
|
+
# identity.signed_up.auth, identity.signed_in.auth,
|
|
25
|
+
# identity.signed_out.auth, session.expired.auth.
|
|
26
|
+
#
|
|
27
|
+
# Run with: bin/rails generate seams:auth
|
|
28
|
+
# rubocop:disable Metrics/ClassLength
|
|
29
|
+
class AuthGenerator < Rails::Generators::Base
|
|
30
|
+
include Seams::Generators::HostInjector
|
|
31
|
+
include Seams::Generators::EjectAware
|
|
32
|
+
|
|
33
|
+
source_root File.expand_path("templates", __dir__)
|
|
34
|
+
|
|
35
|
+
ENGINE_NAME = "auth"
|
|
36
|
+
|
|
37
|
+
def create_base_engine
|
|
38
|
+
EngineGenerator.start([ENGINE_NAME], destination_root: destination_root)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def overwrite_engine_entry_point
|
|
42
|
+
# engine.rb / lib/auth.rb stay framework-managed (NOT
|
|
43
|
+
# eject-eligible — see Seams::Generators::EjectAware).
|
|
44
|
+
# configuration.rb is eject-eligible so a host that has
|
|
45
|
+
# ejected it keeps its overrides on regenerate.
|
|
46
|
+
template "lib/engine.rb.tt", engine_path("lib/auth/engine.rb"), force: true
|
|
47
|
+
template "lib/auth.rb.tt", engine_path("lib/auth.rb"), force: true
|
|
48
|
+
template_unless_ejected "lib/configuration.rb.tt",
|
|
49
|
+
engine_path("lib/auth/configuration.rb")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def overwrite_routes
|
|
53
|
+
template_unless_ejected "config/routes.rb.tt", engine_path("config/routes.rb"), force: true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def create_models
|
|
57
|
+
template_unless_ejected "app/models/application_record.rb.tt",
|
|
58
|
+
engine_path("app/models/auth/application_record.rb")
|
|
59
|
+
template_unless_ejected "app/models/identity.rb.tt",
|
|
60
|
+
engine_path("app/models/auth/identity.rb")
|
|
61
|
+
template_unless_ejected "app/models/session.rb.tt",
|
|
62
|
+
engine_path("app/models/auth/session.rb")
|
|
63
|
+
template_unless_ejected "app/models/current.rb.tt",
|
|
64
|
+
engine_path("app/models/auth/current.rb")
|
|
65
|
+
# OAuth identity link table — issue #2 section 2A. Lives under
|
|
66
|
+
# the Auth::OAuth namespace alongside the lib/ adapter classes
|
|
67
|
+
# (Abstract, Google, Github) so the Zeitwerk inflector only
|
|
68
|
+
# needs the single "oauth" => "OAuth" mapping at engine boot.
|
|
69
|
+
template_unless_ejected "app/models/oauth/provider.rb.tt",
|
|
70
|
+
engine_path("app/models/auth/oauth/provider.rb")
|
|
71
|
+
# Bearer-token API access — issue #2 section 2A.
|
|
72
|
+
template_unless_ejected "app/models/api_token.rb.tt",
|
|
73
|
+
engine_path("app/models/auth/api_token.rb")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def create_controllers
|
|
77
|
+
template_unless_ejected "app/controllers/sessions_controller.rb.tt",
|
|
78
|
+
engine_path("app/controllers/auth/sessions_controller.rb")
|
|
79
|
+
template_unless_ejected "app/controllers/registrations_controller.rb.tt",
|
|
80
|
+
engine_path("app/controllers/auth/registrations_controller.rb")
|
|
81
|
+
template_unless_ejected "app/controllers/password_resets_controller.rb.tt",
|
|
82
|
+
engine_path("app/controllers/auth/password_resets_controller.rb")
|
|
83
|
+
template_unless_ejected "app/controllers/oauth/callbacks_controller.rb.tt",
|
|
84
|
+
engine_path("app/controllers/auth/oauth/callbacks_controller.rb")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def create_services
|
|
88
|
+
template_unless_ejected "app/services/register_identity.rb.tt",
|
|
89
|
+
engine_path("app/services/auth/register_identity.rb")
|
|
90
|
+
template_unless_ejected "app/services/authenticate_identity.rb.tt",
|
|
91
|
+
engine_path("app/services/auth/authenticate_identity.rb")
|
|
92
|
+
template_unless_ejected "app/services/reset_password.rb.tt",
|
|
93
|
+
engine_path("app/services/auth/reset_password.rb")
|
|
94
|
+
template_unless_ejected "app/services/oauth/authenticator.rb.tt",
|
|
95
|
+
engine_path("app/services/auth/oauth/authenticator.rb")
|
|
96
|
+
template_unless_ejected "app/services/generate_api_token.rb.tt",
|
|
97
|
+
engine_path("app/services/auth/generate_api_token.rb")
|
|
98
|
+
# Phase Wave 5 (review fix): the revoke path was documented in
|
|
99
|
+
# the README + registered in engine.rb but had no implementer.
|
|
100
|
+
template_unless_ejected "app/services/revoke_api_token.rb.tt",
|
|
101
|
+
engine_path("app/services/auth/revoke_api_token.rb")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def create_jobs
|
|
105
|
+
template_unless_ejected "app/jobs/application_job.rb.tt",
|
|
106
|
+
engine_path("app/jobs/auth/application_job.rb")
|
|
107
|
+
template_unless_ejected "app/jobs/cleanup_expired_sessions_job.rb.tt",
|
|
108
|
+
engine_path("app/jobs/auth/cleanup_expired_sessions_job.rb")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def create_rake_tasks
|
|
112
|
+
# Wave 11: PII rotation task for hosts upgrading from Wave ≤10.
|
|
113
|
+
template_unless_ejected "lib/tasks/auth_pii.rake.tt",
|
|
114
|
+
engine_path("lib/tasks/auth_pii.rake")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def create_oauth_adapters
|
|
118
|
+
template_unless_ejected "lib/oauth/abstract.rb.tt",
|
|
119
|
+
engine_path("lib/auth/oauth/abstract.rb")
|
|
120
|
+
template_unless_ejected "lib/oauth/google.rb.tt",
|
|
121
|
+
engine_path("lib/auth/oauth/google.rb")
|
|
122
|
+
template_unless_ejected "lib/oauth/github.rb.tt",
|
|
123
|
+
engine_path("lib/auth/oauth/github.rb")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def create_mailer
|
|
127
|
+
template_unless_ejected "app/mailers/passwords_mailer.rb.tt",
|
|
128
|
+
engine_path("app/mailers/auth/passwords_mailer.rb")
|
|
129
|
+
template_unless_ejected "app/views/passwords_mailer/reset_email.html.erb.tt",
|
|
130
|
+
engine_path("app/views/auth/passwords_mailer/reset_email.html.erb")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def create_views
|
|
134
|
+
template_unless_ejected "app/views/sessions/new.html.erb.tt",
|
|
135
|
+
engine_path("app/views/auth/sessions/new.html.erb")
|
|
136
|
+
template_unless_ejected "app/views/sessions/_oauth_buttons.html.erb.tt",
|
|
137
|
+
engine_path("app/views/auth/sessions/_oauth_buttons.html.erb")
|
|
138
|
+
template_unless_ejected "app/views/registrations/new.html.erb.tt",
|
|
139
|
+
engine_path("app/views/auth/registrations/new.html.erb")
|
|
140
|
+
template_unless_ejected "app/views/password_resets/new.html.erb.tt",
|
|
141
|
+
engine_path("app/views/auth/password_resets/new.html.erb")
|
|
142
|
+
template_unless_ejected "app/views/password_resets/edit.html.erb.tt",
|
|
143
|
+
engine_path("app/views/auth/password_resets/edit.html.erb")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def create_concerns
|
|
147
|
+
template_unless_ejected "lib/concerns/authenticatable.rb.tt",
|
|
148
|
+
engine_path("lib/auth/concerns/authenticatable.rb")
|
|
149
|
+
template_unless_ejected "lib/concerns/authentication.rb.tt",
|
|
150
|
+
engine_path("lib/auth/concerns/authentication.rb")
|
|
151
|
+
# Bearer-token controller auth for API endpoints.
|
|
152
|
+
template_unless_ejected "lib/concerns/api_authenticatable.rb.tt",
|
|
153
|
+
engine_path("lib/auth/concerns/api_authenticatable.rb")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def create_migrations
|
|
157
|
+
template "db/migrate/create_auth_identities.rb.tt",
|
|
158
|
+
engine_path("db/migrate/#{timestamp(0)}_create_auth_identities.rb")
|
|
159
|
+
template "db/migrate/create_auth_sessions.rb.tt",
|
|
160
|
+
engine_path("db/migrate/#{timestamp(1)}_create_auth_sessions.rb")
|
|
161
|
+
template "db/migrate/create_auth_oauth_providers.rb.tt",
|
|
162
|
+
engine_path("db/migrate/#{timestamp(2)}_create_auth_oauth_providers.rb")
|
|
163
|
+
template "db/migrate/create_auth_api_tokens.rb.tt",
|
|
164
|
+
engine_path("db/migrate/#{timestamp(3)}_create_auth_api_tokens.rb")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def create_specs
|
|
168
|
+
template_unless_ejected "spec/models/identity_spec.rb.tt",
|
|
169
|
+
engine_path("spec/models/auth/identity_spec.rb")
|
|
170
|
+
template_unless_ejected "spec/models/session_spec.rb.tt",
|
|
171
|
+
engine_path("spec/models/auth/session_spec.rb")
|
|
172
|
+
# Phase 2A finish — coverage for the new Wave-9/10 models.
|
|
173
|
+
template_unless_ejected "spec/models/api_token_spec.rb.tt",
|
|
174
|
+
engine_path("spec/models/auth/api_token_spec.rb")
|
|
175
|
+
template_unless_ejected "spec/models/oauth/provider_spec.rb.tt",
|
|
176
|
+
engine_path("spec/models/auth/oauth/provider_spec.rb")
|
|
177
|
+
template_unless_ejected "spec/mailers/passwords_mailer_spec.rb.tt",
|
|
178
|
+
engine_path("spec/mailers/auth/passwords_mailer_spec.rb")
|
|
179
|
+
template_unless_ejected "spec/factories/auth.rb.tt",
|
|
180
|
+
engine_path("spec/factories/auth.rb")
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def overwrite_readme
|
|
184
|
+
template "README.md.tt", engine_path("README.md"), force: true
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def update_exposed_concerns
|
|
188
|
+
rubocop_path = engine_path(".rubocop.yml")
|
|
189
|
+
return unless File.exist?(rubocop_path)
|
|
190
|
+
|
|
191
|
+
contents = File.read(rubocop_path)
|
|
192
|
+
replacement = " ExposedConcerns:\n - Auth::Authenticatable\n - Auth::Authentication"
|
|
193
|
+
contents.sub!(/^ ExposedConcerns: \[\]$/, replacement)
|
|
194
|
+
File.write(rubocop_path, contents)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def create_dummy_app
|
|
198
|
+
# Post Wave 9: the dummy app no longer ships a host User
|
|
199
|
+
# model. Auth::Identity is the human now, and the auth specs
|
|
200
|
+
# don't need a host User to exercise the engine. Hosts that
|
|
201
|
+
# keep a User model can include Auth::Authenticatable in their
|
|
202
|
+
# own test fixture.
|
|
203
|
+
Seams::Generators::DummyAppWriter.write!(
|
|
204
|
+
engine_path: File.join(destination_root, "engines", ENGINE_NAME),
|
|
205
|
+
engine_module: "Auth",
|
|
206
|
+
mount_at: "/auth",
|
|
207
|
+
schema: dummy_schema
|
|
208
|
+
)
|
|
209
|
+
template "spec/runtime/boot_spec.rb.tt",
|
|
210
|
+
engine_path("spec/runtime/auth_boot_spec.rb")
|
|
211
|
+
template "spec/runtime/event_payload_spec.rb.tt",
|
|
212
|
+
engine_path("spec/runtime/auth_event_payload_spec.rb")
|
|
213
|
+
# Phase 2A finish — login flow round-trip via the request stack.
|
|
214
|
+
template "spec/runtime/login_flow_spec.rb.tt",
|
|
215
|
+
engine_path("spec/runtime/auth_login_flow_spec.rb")
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def wire_into_host
|
|
219
|
+
host_inject_gem("bcrypt", "~> 3.1")
|
|
220
|
+
# OAuth adapters speak HTTP via Faraday (no Net::HTTP — see
|
|
221
|
+
# memory feedback_external_apis.md). The host needs Faraday on
|
|
222
|
+
# the load path even if it never configures an OAuth provider.
|
|
223
|
+
host_inject_gem("faraday", "~> 2.0")
|
|
224
|
+
# factory_bot_rails powers the engine's spec/factories/*. Lives
|
|
225
|
+
# in the host's test group only.
|
|
226
|
+
host_inject_gem("factory_bot_rails", "~> 6.4", group: :test)
|
|
227
|
+
host_inject_mount(engine_class: "Auth::Engine", at: "/auth")
|
|
228
|
+
# NB: post-Wave-9 the canonical host has no `app/models/user.rb`,
|
|
229
|
+
# so this call is a best-effort no-op (the helper logs `skip`
|
|
230
|
+
# and moves on). Hosts that DO maintain a domain User on top
|
|
231
|
+
# of `Auth::Identity` get the include wired automatically.
|
|
232
|
+
host_inject_include_in_user("Auth::Authenticatable")
|
|
233
|
+
host_inject_include_in_application_controller("Auth::Authentication")
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def report_summary
|
|
237
|
+
say ""
|
|
238
|
+
say " Auth engine generated at engines/auth/", :green
|
|
239
|
+
say ""
|
|
240
|
+
say " Next steps:", :yellow
|
|
241
|
+
say " 1. bundle install (picks up bcrypt + Auth::Engine)"
|
|
242
|
+
say " 2. bin/rails db:migrate"
|
|
243
|
+
say " 3. Run the engine specs: bin/rails seams:test[auth]"
|
|
244
|
+
say ""
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
private
|
|
248
|
+
|
|
249
|
+
def engine_path(relative)
|
|
250
|
+
File.join(destination_root, "engines", ENGINE_NAME, relative)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def timestamp(offset)
|
|
254
|
+
# Microsecond-resolution timestamp so migrations generated
|
|
255
|
+
# back-to-back (and across sibling generators in the same
|
|
256
|
+
# second) don't collide. The 14-digit format is what Rails
|
|
257
|
+
# uses for its own generators.
|
|
258
|
+
base = Time.now.utc
|
|
259
|
+
seconds = base.strftime("%Y%m%d%H%M%S").to_i
|
|
260
|
+
(seconds + offset).to_s
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def dummy_schema
|
|
264
|
+
<<~SCHEMA
|
|
265
|
+
create_table :auth_identities do |t|
|
|
266
|
+
t.text :email, null: false
|
|
267
|
+
t.string :password_digest, null: false
|
|
268
|
+
t.boolean :staff, null: false, default: false
|
|
269
|
+
t.timestamps
|
|
270
|
+
end
|
|
271
|
+
add_index :auth_identities, :email, unique: true
|
|
272
|
+
add_index :auth_identities, :staff, where: "staff = true"
|
|
273
|
+
|
|
274
|
+
create_table :auth_sessions do |t|
|
|
275
|
+
t.references :identity, null: false, foreign_key: { to_table: :auth_identities }
|
|
276
|
+
t.string :token, null: false
|
|
277
|
+
t.datetime :expires_at, null: false
|
|
278
|
+
t.timestamps
|
|
279
|
+
end
|
|
280
|
+
add_index :auth_sessions, :token, unique: true
|
|
281
|
+
|
|
282
|
+
create_table :auth_oauth_providers do |t|
|
|
283
|
+
t.references :identity, null: false, foreign_key: { to_table: :auth_identities }
|
|
284
|
+
t.string :provider, null: false
|
|
285
|
+
t.text :provider_uid, null: false
|
|
286
|
+
t.text :access_token
|
|
287
|
+
t.text :refresh_token
|
|
288
|
+
t.datetime :expires_at
|
|
289
|
+
t.string :token_type, default: "Bearer"
|
|
290
|
+
t.jsonb :profile_data, null: false, default: {}
|
|
291
|
+
t.timestamps
|
|
292
|
+
end
|
|
293
|
+
add_index :auth_oauth_providers, %i[provider provider_uid], unique: true
|
|
294
|
+
add_index :auth_oauth_providers, %i[identity_id provider], unique: true
|
|
295
|
+
|
|
296
|
+
create_table :auth_api_tokens do |t|
|
|
297
|
+
t.references :identity, null: false, foreign_key: { to_table: :auth_identities }
|
|
298
|
+
t.string :name, null: false
|
|
299
|
+
t.string :token_digest, null: false
|
|
300
|
+
t.string :token_prefix, null: false
|
|
301
|
+
t.datetime :expires_at
|
|
302
|
+
t.datetime :last_used_at
|
|
303
|
+
t.timestamps
|
|
304
|
+
end
|
|
305
|
+
add_index :auth_api_tokens, :token_digest, unique: true
|
|
306
|
+
SCHEMA
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
# rubocop:enable Metrics/ClassLength
|
|
310
|
+
end
|
|
311
|
+
end
|