open-loam 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -8
- data/LICENSE +1 -1
- data/README.md +24 -24
- data/app/jobs/{loam → open_loam}/custom_field_reindex_job.rb +6 -6
- data/app/jobs/{loam → open_loam}/event_delivery_job.rb +8 -8
- data/app/jobs/{loam → open_loam}/event_redelivery_sweep_job.rb +4 -4
- data/app/jobs/{loam → open_loam}/webhook_delivery_job.rb +7 -7
- data/app/models/{loam → open_loam}/api_token.rb +7 -7
- data/app/models/{loam → open_loam}/audit_record.rb +3 -3
- data/app/models/{loam → open_loam}/auth_attempt.rb +5 -5
- data/app/models/{loam → open_loam}/business_rule.rb +10 -10
- data/app/models/{loam → open_loam}/business_rule_run.rb +5 -5
- data/app/models/{loam → open_loam}/comment.rb +9 -9
- data/app/models/{loam → open_loam}/config.rb +7 -7
- data/app/models/{loam → open_loam}/custom_field_value.rb +5 -5
- data/app/models/{loam → open_loam}/dashboard_widget.rb +5 -5
- data/app/models/open_loam/dictionary.rb +20 -0
- data/app/models/{loam → open_loam}/dictionary_entry.rb +7 -7
- data/app/models/{loam → open_loam}/event_delivery.rb +4 -4
- data/app/models/{loam → open_loam}/field_definition.rb +8 -8
- data/app/models/{loam → open_loam}/inbound_webhook_delivery.rb +4 -4
- data/app/models/{loam → open_loam}/inbound_webhook_source.rb +13 -13
- data/app/models/{loam → open_loam}/membership.rb +6 -6
- data/app/models/{loam → open_loam}/mfa_credential.rb +8 -8
- data/app/models/{loam → open_loam}/notification.rb +10 -10
- data/app/models/{loam → open_loam}/pending_action.rb +23 -23
- data/app/models/{loam → open_loam}/perspective.rb +5 -5
- data/app/models/{loam → open_loam}/progress_job.rb +7 -7
- data/app/models/{loam → open_loam}/record_lock.rb +5 -5
- data/app/models/{loam → open_loam}/scheduled_job.rb +7 -7
- data/app/models/open_loam/search_token.rb +9 -0
- data/app/models/{loam → open_loam}/sso_identity.rb +4 -4
- data/app/models/{loam → open_loam}/sso_provider.rb +8 -8
- data/app/models/open_loam/tenant.rb +20 -0
- data/app/models/{loam → open_loam}/translation.rb +5 -5
- data/app/models/{loam → open_loam}/webhook_endpoint.rb +6 -6
- data/app/views/{loam → open_loam}/attachments/_attachments.html.erb +1 -1
- data/app/views/{loam → open_loam}/comments/_comments.html.erb +1 -1
- data/app/views/{loam → open_loam}/custom_fields/_fields.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/entity/entity_generator.rb +9 -9
- data/lib/generators/{loam → open_loam}/entity/templates/api_controller.rb +1 -1
- data/lib/generators/{loam → open_loam}/entity/templates/controller.rb +21 -21
- data/lib/generators/{loam → open_loam}/entity/templates/entity_test.rb +12 -12
- data/lib/generators/{loam → open_loam}/entity/templates/migration.rb +5 -5
- data/lib/generators/{loam → open_loam}/entity/templates/model.rb +10 -10
- data/lib/generators/{loam → open_loam}/entity/templates/policy.rb +1 -1
- data/lib/generators/{loam → open_loam}/entity/templates/views/_form.html.erb +4 -4
- data/lib/generators/{loam → open_loam}/entity/templates/views/deleted.html.erb +7 -7
- data/lib/generators/open_loam/entity/templates/views/edit.html.erb +3 -0
- data/lib/generators/open_loam/entity/templates/views/index.html.erb +90 -0
- data/lib/generators/open_loam/entity/templates/views/new.html.erb +3 -0
- data/lib/generators/open_loam/entity/templates/views/show.html.erb +36 -0
- data/lib/generators/{loam → open_loam}/install/install_generator.rb +47 -47
- data/lib/generators/open_loam/install/templates/AGENTS.md +341 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_controller.rb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_controller.rb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/base_controller.rb +38 -38
- data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_form.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/comments_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/admin/configs_controller.rb +9 -9
- data/lib/generators/open_loam/install/templates/admin/dashboard_controller.rb +10 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_form.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/dictionary_entries_controller.rb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/events_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/features_controller.rb +9 -9
- data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_new.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/history_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/history_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/imports_controller.rb +12 -12
- data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_controller.rb +6 -6
- data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_new.html.erb +2 -2
- data/lib/generators/open_loam/install/templates/admin/layout.html.erb +76 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_controller.rb +6 -6
- data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_controller.rb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_controller.rb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_index.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_controller.rb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_index.html.erb +1 -1
- data/lib/generators/open_loam/install/templates/admin/progress_jobs_controller.rb +19 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/progress_jobs_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/record_locks_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_form.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/search_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_controller.rb +32 -32
- data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_controller.rb +6 -6
- data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_form.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_new.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/translations_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/translations_index.html.erb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin.css +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/api_base_controller.rb +15 -15
- data/lib/generators/{loam → open_loam}/install/templates/guardrails_test.rb +17 -17
- data/lib/generators/{loam → open_loam}/install/templates/import_job.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/inbound_webhooks_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/initializer.rb +74 -74
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_api_tokens.rb +15 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_audit_records.rb +14 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_auth_attempts.rb → open_loam/install/templates/migrations/create_open_loam_auth_attempts.rb} +3 -3
- data/lib/generators/{loam/install/templates/migrations/create_loam_business_rules.rb → open_loam/install/templates/migrations/create_open_loam_business_rules.rb} +10 -10
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_comments.rb +13 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_configs.rb → open_loam/install/templates/migrations/create_open_loam_configs.rb} +7 -7
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_custom_field_values.rb +19 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dashboard_widgets.rb +12 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dictionaries.rb +11 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_dictionary_entries.rb → open_loam/install/templates/migrations/create_open_loam_dictionary_entries.rb} +6 -6
- data/lib/generators/{loam/install/templates/migrations/create_loam_event_deliveries.rb → open_loam/install/templates/migrations/create_open_loam_event_deliveries.rb} +5 -5
- data/lib/generators/{loam/install/templates/migrations/create_loam_field_definitions.rb → open_loam/install/templates/migrations/create_open_loam_field_definitions.rb} +4 -4
- data/lib/generators/{loam/install/templates/migrations/create_loam_inbound_webhooks.rb → open_loam/install/templates/migrations/create_open_loam_inbound_webhooks.rb} +9 -9
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_memberships.rb +11 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_mfa_credentials.rb → open_loam/install/templates/migrations/create_open_loam_mfa_credentials.rb} +4 -4
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_notifications.rb +15 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_pending_actions.rb → open_loam/install/templates/migrations/create_open_loam_pending_actions.rb} +9 -9
- data/lib/generators/{loam/install/templates/migrations/create_loam_perspectives.rb → open_loam/install/templates/migrations/create_open_loam_perspectives.rb} +5 -5
- data/lib/generators/{loam/install/templates/migrations/create_loam_progress_jobs.rb → open_loam/install/templates/migrations/create_open_loam_progress_jobs.rb} +5 -5
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_record_locks.rb +16 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_scheduled_jobs.rb → open_loam/install/templates/migrations/create_open_loam_scheduled_jobs.rb} +5 -5
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_search_tokens.rb +18 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_sso_providers.rb → open_loam/install/templates/migrations/create_open_loam_sso_providers.rb} +12 -12
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_tenants.rb +9 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_translations.rb +15 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_webhook_endpoints.rb +13 -0
- data/lib/generators/{loam → open_loam}/install/templates/migrations/create_users.rb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/user.rb +5 -5
- data/lib/generators/{loam → open_loam}/primary_key_options.rb +23 -23
- data/lib/open-loam.rb +2 -2
- data/lib/{loam → open_loam}/attachable.rb +4 -4
- data/lib/{loam → open_loam}/auditable.rb +23 -23
- data/lib/{loam → open_loam}/auth_throttle.rb +10 -10
- data/lib/{loam → open_loam}/base32.rb +1 -1
- data/lib/{loam → open_loam}/bulk.rb +7 -7
- data/lib/{loam → open_loam}/business_rules/actions.rb +7 -7
- data/lib/{loam → open_loam}/business_rules/condition.rb +5 -5
- data/lib/{loam → open_loam}/business_rules.rb +19 -19
- data/lib/{loam → open_loam}/commentable.rb +6 -6
- data/lib/{loam → open_loam}/configs.rb +19 -19
- data/lib/{loam → open_loam}/cron.rb +2 -2
- data/lib/{loam → open_loam}/csv.rb +2 -2
- data/lib/{loam → open_loam}/current.rb +5 -5
- data/lib/{loam → open_loam}/custom_field_index.rb +23 -23
- data/lib/{loam → open_loam}/custom_fields.rb +17 -17
- data/lib/{loam → open_loam}/dashboard.rb +5 -5
- data/lib/{loam → open_loam}/dictionaries.rb +12 -12
- data/lib/{loam → open_loam}/durable_events.rb +19 -19
- data/lib/{loam → open_loam}/encryptable.rb +33 -33
- data/lib/{loam → open_loam}/encryption/cipher.rb +3 -3
- data/lib/{loam → open_loam}/encryption/key_provider.rb +3 -3
- data/lib/{loam → open_loam}/encryption.rb +11 -11
- data/lib/open_loam/engine.rb +57 -0
- data/lib/{loam → open_loam}/enrichers.rb +5 -5
- data/lib/{loam → open_loam}/errors.rb +10 -10
- data/lib/{loam → open_loam}/eval.rb +2 -2
- data/lib/{loam → open_loam}/event_stream.rb +14 -14
- data/lib/{loam → open_loam}/eventful.rb +12 -12
- data/lib/{loam → open_loam}/events.rb +5 -5
- data/lib/{loam → open_loam}/export.rb +8 -8
- data/lib/{loam → open_loam}/features.rb +15 -15
- data/lib/{loam → open_loam}/generated_key.rb +6 -6
- data/lib/{loam → open_loam}/import.rb +12 -12
- data/lib/{loam → open_loam}/inbound_webhooks.rb +7 -7
- data/lib/{loam → open_loam}/lifecycle.rb +25 -25
- data/lib/{loam/locales/loam.en.yml → open_loam/locales/open_loam.en.yml} +5 -5
- data/lib/{loam → open_loam}/mcp/server.rb +9 -9
- data/lib/{loam → open_loam}/mcp.rb +21 -21
- data/lib/{loam → open_loam}/notifications.rb +5 -5
- data/lib/{loam → open_loam}/open_api.rb +16 -16
- data/lib/{loam → open_loam}/overrides.rb +10 -10
- data/lib/{loam → open_loam}/pending_actions.rb +9 -9
- data/lib/{loam → open_loam}/permissions.rb +8 -8
- data/lib/{loam → open_loam}/perspectives.rb +9 -9
- data/lib/{loam → open_loam}/policy.rb +6 -6
- data/lib/{loam → open_loam}/progress.rb +6 -6
- data/lib/{loam → open_loam}/record_locks.rb +9 -9
- data/lib/{loam → open_loam}/scheduler.rb +20 -20
- data/lib/{loam → open_loam}/search/driver.rb +3 -3
- data/lib/{loam → open_loam}/search/like_driver.rb +5 -5
- data/lib/{loam → open_loam}/search/token_driver.rb +15 -15
- data/lib/{loam → open_loam}/search.rb +5 -5
- data/lib/{loam → open_loam}/searchable.rb +22 -22
- data/lib/{loam → open_loam}/soft_deletable.rb +10 -10
- data/lib/{loam → open_loam}/sso/claims.rb +1 -1
- data/lib/{loam → open_loam}/sso/fake_provider.rb +1 -1
- data/lib/{loam → open_loam}/sso/http_client.rb +3 -3
- data/lib/{loam → open_loam}/sso/oidc_provider.rb +2 -2
- data/lib/{loam → open_loam}/sso.rb +17 -17
- data/lib/{loam → open_loam}/telemetry.rb +6 -6
- data/lib/{loam → open_loam}/tenant_record.rb +10 -10
- data/lib/{loam → open_loam}/test_helpers.rb +4 -4
- data/lib/{loam → open_loam}/totp.rb +3 -3
- data/lib/{loam → open_loam}/translatable.rb +24 -24
- data/lib/{loam → open_loam}/undo.rb +11 -11
- data/lib/open_loam/version.rb +3 -0
- data/lib/{loam → open_loam}/webhooks.rb +4 -4
- data/lib/{loam → open_loam}/widgets.rb +9 -9
- data/lib/{loam → open_loam}/workflow.rb +45 -45
- data/lib/open_loam.rb +122 -0
- data/lib/tasks/{loam.rake → open_loam.rake} +43 -43
- data/lib/tasks/{loam_eval.rake → open_loam_eval.rake} +7 -7
- data/lib/tasks/open_loam_mcp.rake +20 -0
- metadata +242 -242
- data/app/models/loam/dictionary.rb +0 -20
- data/app/models/loam/search_token.rb +0 -9
- data/app/models/loam/tenant.rb +0 -20
- data/lib/generators/loam/entity/templates/views/edit.html.erb +0 -3
- data/lib/generators/loam/entity/templates/views/index.html.erb +0 -90
- data/lib/generators/loam/entity/templates/views/new.html.erb +0 -3
- data/lib/generators/loam/entity/templates/views/show.html.erb +0 -36
- data/lib/generators/loam/install/templates/AGENTS.md +0 -341
- data/lib/generators/loam/install/templates/admin/dashboard_controller.rb +0 -10
- data/lib/generators/loam/install/templates/admin/layout.html.erb +0 -76
- data/lib/generators/loam/install/templates/admin/progress_jobs_controller.rb +0 -19
- data/lib/generators/loam/install/templates/migrations/create_loam_api_tokens.rb +0 -15
- data/lib/generators/loam/install/templates/migrations/create_loam_audit_records.rb +0 -14
- data/lib/generators/loam/install/templates/migrations/create_loam_comments.rb +0 -13
- data/lib/generators/loam/install/templates/migrations/create_loam_custom_field_values.rb +0 -19
- data/lib/generators/loam/install/templates/migrations/create_loam_dashboard_widgets.rb +0 -12
- data/lib/generators/loam/install/templates/migrations/create_loam_dictionaries.rb +0 -11
- data/lib/generators/loam/install/templates/migrations/create_loam_memberships.rb +0 -11
- data/lib/generators/loam/install/templates/migrations/create_loam_notifications.rb +0 -15
- data/lib/generators/loam/install/templates/migrations/create_loam_record_locks.rb +0 -16
- data/lib/generators/loam/install/templates/migrations/create_loam_search_tokens.rb +0 -18
- data/lib/generators/loam/install/templates/migrations/create_loam_tenants.rb +0 -9
- data/lib/generators/loam/install/templates/migrations/create_loam_translations.rb +0 -15
- data/lib/generators/loam/install/templates/migrations/create_loam_webhook_endpoints.rb +0 -13
- data/lib/loam/engine.rb +0 -57
- data/lib/loam/version.rb +0 -3
- data/lib/loam.rb +0 -122
- data/lib/tasks/loam_mcp.rake +0 -20
- /data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/features_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_preview.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_summary.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_activated.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_show.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/pagination.rb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/search_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_mfa_challenge.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_new.html.erb +0 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
class
|
|
1
|
+
class CreateOpenLoamPendingActions < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
2
|
def change
|
|
3
|
-
create_table :
|
|
4
|
-
t.references :tenant, null: false, foreign_key: { to_table: :
|
|
5
|
-
t.<%=
|
|
3
|
+
create_table :open_loam_pending_actions<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
|
+
t.<%= open_loam_key_column_type %> :actor_id<%= open_loam_key_limit_option %> # who staged it (the proposer)
|
|
6
6
|
t.string :status, null: false, default: "pending"
|
|
7
7
|
t.string :action_type, null: false # create / update / destroy
|
|
8
8
|
t.string :target_type # the model being mutated
|
|
9
|
-
t.<%=
|
|
9
|
+
t.<%= open_loam_key_column_type %> :target_id<%= open_loam_key_limit_option %> # null for a staged create
|
|
10
10
|
t.text :changeset # encrypted JSON of the proposed changes
|
|
11
11
|
t.text :summary, null: false # human-readable description
|
|
12
12
|
t.string :idempotency_key, null: false # keyed HMAC; same proposal collapses to one row
|
|
13
|
-
t.<%=
|
|
13
|
+
t.<%= open_loam_key_column_type %> :reviewed_by_id<%= open_loam_key_limit_option %> # the approving/rejecting manager
|
|
14
14
|
t.datetime :reviewed_at
|
|
15
15
|
t.text :result # execution outcome, or "rejected: <reason>"
|
|
16
16
|
t.text :error # execution failure message
|
|
@@ -19,8 +19,8 @@ class CreateLoamPendingActions < ActiveRecord::Migration[<%= ActiveRecord::VERSI
|
|
|
19
19
|
# One row per distinct proposal per tenant.
|
|
20
20
|
# Only ONE PENDING row per proposal per tenant — a rejected/executed row with
|
|
21
21
|
# the same key may coexist, so a rejected proposal can be re-staged later.
|
|
22
|
-
add_index :
|
|
23
|
-
where: "status = 'pending'", name: "
|
|
24
|
-
add_index :
|
|
22
|
+
add_index :open_loam_pending_actions, %i[tenant_id idempotency_key], unique: true,
|
|
23
|
+
where: "status = 'pending'", name: "index_open_loam_pending_actions_pending_key"
|
|
24
|
+
add_index :open_loam_pending_actions, %i[tenant_id status]
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
class
|
|
1
|
+
class CreateOpenLoamPerspectives < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
2
|
def change
|
|
3
|
-
create_table :
|
|
4
|
-
t.references :tenant, null: false, foreign_key: { to_table: :
|
|
3
|
+
create_table :open_loam_perspectives<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
5
|
t.string :entity_type, null: false # which entity this view is for
|
|
6
6
|
t.string :name, null: false # human label
|
|
7
|
-
t.<%=
|
|
7
|
+
t.<%= open_loam_key_column_type %> :owner_id<%= open_loam_key_limit_option %> # the user who owns a private view
|
|
8
8
|
t.string :visibility, null: false, default: "private" # private / role / tenant
|
|
9
9
|
t.string :role # set when visibility is "role"
|
|
10
10
|
t.boolean :is_default, null: false, default: false
|
|
@@ -12,6 +12,6 @@ class CreateLoamPerspectives < ActiveRecord::Migration[<%= ActiveRecord::VERSION
|
|
|
12
12
|
t.integer :lock_version, null: false, default: 0 # optimistic locking
|
|
13
13
|
t.timestamps
|
|
14
14
|
end
|
|
15
|
-
add_index :
|
|
15
|
+
add_index :open_loam_perspectives, %i[tenant_id entity_type]
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
class
|
|
1
|
+
class CreateOpenLoamProgressJobs < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
2
|
def change
|
|
3
|
-
create_table :
|
|
4
|
-
t.references :tenant, null: false, foreign_key: { to_table: :
|
|
3
|
+
create_table :open_loam_progress_jobs<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
5
|
t.string :key # optional caller-chosen id (e.g. "reindex:Gadget")
|
|
6
6
|
t.string :name, null: false # human label
|
|
7
7
|
t.string :status, null: false, default: "running" # running / completed / failed / cancelled
|
|
@@ -9,12 +9,12 @@ class CreateLoamProgressJobs < ActiveRecord::Migration[<%= ActiveRecord::VERSION
|
|
|
9
9
|
t.integer :completed, null: false, default: 0
|
|
10
10
|
t.text :message # latest status line
|
|
11
11
|
t.text :error # set when failed
|
|
12
|
-
t.<%=
|
|
12
|
+
t.<%= open_loam_key_column_type %> :actor_id<%= open_loam_key_limit_option %> # who started it
|
|
13
13
|
t.datetime :started_at
|
|
14
14
|
t.datetime :finished_at
|
|
15
15
|
t.json :result # structured result for a finished job (e.g. an import summary)
|
|
16
16
|
t.timestamps # updated_at is the heartbeat
|
|
17
17
|
end
|
|
18
|
-
add_index :
|
|
18
|
+
add_index :open_loam_progress_jobs, %i[tenant_id status]
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateOpenLoamRecordLocks < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :open_loam_record_locks<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
|
+
t.string :lockable_type, null: false
|
|
6
|
+
t.<%= open_loam_key_column_type %> :lockable_id<%= open_loam_key_limit_option %>, null: false
|
|
7
|
+
t.references :locked_by, null: false, foreign_key: { to_table: :users }<%= open_loam_type_option %>
|
|
8
|
+
t.string :token, null: false
|
|
9
|
+
t.datetime :expires_at, null: false # TTL; an expired lock is treated as free
|
|
10
|
+
t.timestamps # updated_at is the heartbeat
|
|
11
|
+
end
|
|
12
|
+
# One advisory lock per record.
|
|
13
|
+
add_index :open_loam_record_locks, %i[tenant_id lockable_type lockable_id], unique: true,
|
|
14
|
+
name: "index_open_loam_record_locks_on_lockable"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
class
|
|
1
|
+
class CreateOpenLoamScheduledJobs < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
2
|
def change
|
|
3
|
-
create_table :
|
|
4
|
-
t.references :tenant, null: false, foreign_key: { to_table: :
|
|
3
|
+
create_table :open_loam_scheduled_jobs<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
5
|
t.string :key, null: false # unique per tenant
|
|
6
6
|
t.string :name, null: false
|
|
7
7
|
t.string :job_class, null: false # a whitelisted ActiveJob class name
|
|
@@ -14,7 +14,7 @@ class CreateLoamScheduledJobs < ActiveRecord::Migration[<%= ActiveRecord::VERSIO
|
|
|
14
14
|
t.boolean :active, null: false, default: true
|
|
15
15
|
t.timestamps
|
|
16
16
|
end
|
|
17
|
-
add_index :
|
|
18
|
-
add_index :
|
|
17
|
+
add_index :open_loam_scheduled_jobs, %i[tenant_id key], unique: true
|
|
18
|
+
add_index :open_loam_scheduled_jobs, %i[active next_run_at locked_until]
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class CreateOpenLoamSearchTokens < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :open_loam_search_tokens<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
|
+
t.string :searchable_type, null: false # the entity class name (e.g. "Gadget")
|
|
6
|
+
t.<%= open_loam_key_column_type %> :searchable_id<%= open_loam_key_limit_option %>, null: false # the record it indexes
|
|
7
|
+
t.string :token, null: false # one normalized word
|
|
8
|
+
end
|
|
9
|
+
# The match subquery filters by (tenant, type, token); index maintenance
|
|
10
|
+
# deletes by (tenant, type, record). One index each — no timestamps: these
|
|
11
|
+
# are plumbing rows written with insert_all/delete_all.
|
|
12
|
+
#
|
|
13
|
+
# Shipped even though the default LikeDriver never uses it, so switching to
|
|
14
|
+
# OpenLoam::Search::TokenDriver is a one-line initializer change, not a migration.
|
|
15
|
+
add_index :open_loam_search_tokens, %i[tenant_id searchable_type token]
|
|
16
|
+
add_index :open_loam_search_tokens, %i[tenant_id searchable_type searchable_id]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
class
|
|
1
|
+
class CreateOpenLoamSsoProviders < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
2
|
def change
|
|
3
|
-
create_table :
|
|
4
|
-
t.references :tenant, null: false, foreign_key: { to_table: :
|
|
3
|
+
create_table :open_loam_sso_providers<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
5
|
t.string :name, null: false
|
|
6
6
|
t.string :protocol, null: false, default: "oidc" # "oidc" shipped; "saml" is a seam
|
|
7
7
|
t.string :issuer # OIDC discovery base (.well-known)
|
|
@@ -9,26 +9,26 @@ class CreateLoamSsoProviders < ActiveRecord::Migration[<%= ActiveRecord::VERSION
|
|
|
9
9
|
t.text :client_secret # ENCRYPTED at rest (tenant-scoped key)
|
|
10
10
|
t.string :domain, null: false # email domain for home-realm discovery
|
|
11
11
|
t.string :jit_role, null: false, default: "employee" # role for JIT-provisioned users
|
|
12
|
-
t.json :group_role_map, null: false, default: {} # IdP group ->
|
|
12
|
+
t.json :group_role_map, null: false, default: {} # IdP group -> OpenLoam role (first match wins)
|
|
13
13
|
t.boolean :active, null: false, default: true
|
|
14
14
|
t.timestamps
|
|
15
15
|
end
|
|
16
16
|
# HRD resolves a provider by email domain, cross-tenant, before login — so the
|
|
17
17
|
# domain is globally unique (one owning IdP per domain).
|
|
18
|
-
add_index :
|
|
19
|
-
add_index :
|
|
18
|
+
add_index :open_loam_sso_providers, :domain, unique: true
|
|
19
|
+
add_index :open_loam_sso_providers, %i[tenant_id active]
|
|
20
20
|
|
|
21
21
|
# The durable link between an external identity (provider + subject) and a
|
|
22
22
|
# local User — the primary lookup on callback (email can change at the IdP,
|
|
23
23
|
# `sub` does not).
|
|
24
|
-
create_table :
|
|
25
|
-
t.references :tenant, null: false, foreign_key: { to_table: :
|
|
26
|
-
t.references :sso_provider, null: false, foreign_key: { to_table: :
|
|
27
|
-
t.<%=
|
|
24
|
+
create_table :open_loam_sso_identities<%= open_loam_id_option %> do |t|
|
|
25
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
26
|
+
t.references :sso_provider, null: false, foreign_key: { to_table: :open_loam_sso_providers }<%= open_loam_type_option %>
|
|
27
|
+
t.<%= open_loam_key_column_type %> :user_id<%= open_loam_key_limit_option %>, null: false # the local User this identity belongs to
|
|
28
28
|
t.string :sub, null: false # the IdP's stable subject identifier
|
|
29
29
|
t.timestamps
|
|
30
30
|
end
|
|
31
|
-
add_index :
|
|
32
|
-
add_index :
|
|
31
|
+
add_index :open_loam_sso_identities, %i[sso_provider_id sub], unique: true
|
|
32
|
+
add_index :open_loam_sso_identities, :user_id
|
|
33
33
|
end
|
|
34
34
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class CreateOpenLoamTenants < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :open_loam_tenants<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :slug, null: false, index: { unique: true }
|
|
6
|
+
t.timestamps
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateOpenLoamTranslations < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :open_loam_translations<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
|
+
t.string :translatable_type, null: false
|
|
6
|
+
t.<%= open_loam_key_column_type %> :translatable_id<%= open_loam_key_limit_option %>, null: false
|
|
7
|
+
t.string :locale, null: false # "en", "de", "pl"
|
|
8
|
+
t.string :field, null: false # the translated attribute
|
|
9
|
+
t.text :value # the per-locale content
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
add_index :open_loam_translations, %i[translatable_type translatable_id locale field],
|
|
13
|
+
unique: true, name: "index_open_loam_translations_unique"
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/generators/open_loam/install/templates/migrations/create_open_loam_webhook_endpoints.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateOpenLoamWebhookEndpoints < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :open_loam_webhook_endpoints<%= open_loam_id_option %> do |t|
|
|
4
|
+
t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
|
|
5
|
+
t.string :url, null: false
|
|
6
|
+
t.string :event_pattern, null: false
|
|
7
|
+
t.string :secret, null: false
|
|
8
|
+
t.boolean :active, null: false, default: true
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
add_index :open_loam_webhook_endpoints, %i[tenant_id active]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class CreateUsers < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
2
2
|
def change
|
|
3
|
-
create_table :users<%=
|
|
3
|
+
create_table :users<%= open_loam_id_option %> do |t|
|
|
4
4
|
t.string :name, null: false
|
|
5
5
|
t.string :email, null: false
|
|
6
6
|
t.string :password_digest, null: false
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Minimal actor model. Deliberately NOT tenant-scoped: a user can belong to
|
|
2
|
-
# many tenants via
|
|
3
|
-
# auth (Devise, OmniAuth, SSO) later —
|
|
2
|
+
# many tenants via OpenLoam::Membership (role is per tenant). Swap in your real
|
|
3
|
+
# auth (Devise, OmniAuth, SSO) later — OpenLoam only needs an `id` and, for the
|
|
4
4
|
# bundled admin login, something that answers `authenticate_by`.
|
|
5
5
|
class User < ApplicationRecord
|
|
6
|
-
#
|
|
6
|
+
# OpenLoam's foreign keys point at this table, so its key has to be generated
|
|
7
7
|
# the same way theirs are when the app does not use integer keys.
|
|
8
|
-
include
|
|
8
|
+
include OpenLoam::GeneratedKey
|
|
9
9
|
|
|
10
10
|
has_secure_password
|
|
11
11
|
|
|
12
|
-
has_many :
|
|
12
|
+
has_many :open_loam_memberships, class_name: "OpenLoam::Membership"
|
|
13
13
|
|
|
14
14
|
validates :name, presence: true
|
|
15
15
|
validates :email, presence: true, uniqueness: true
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
module Generators
|
|
3
|
-
# Shared primary-key shaping for `
|
|
3
|
+
# Shared primary-key shaping for `open_loam:install` and `open_loam:entity`.
|
|
4
4
|
#
|
|
5
|
-
#
|
|
5
|
+
# OpenLoam's migrations used to spell every key `bigint` — implicitly in
|
|
6
6
|
# `create_table`, and explicitly in `t.references` and the polymorphic
|
|
7
7
|
# `*_id` columns. In an app whose own tables use string or uuid keys the
|
|
8
8
|
# foreign keys then do not line up with the tables they point at, and every
|
|
@@ -10,7 +10,7 @@ module Loam
|
|
|
10
10
|
#
|
|
11
11
|
# The key type is taken from, in order: an explicit --primary-key-type, the
|
|
12
12
|
# host app's own `config.generators` setting (so an app that already told
|
|
13
|
-
# Rails what its keys look like does not have to tell
|
|
13
|
+
# Rails what its keys look like does not have to tell OpenLoam separately), and
|
|
14
14
|
# bigint as the Rails default.
|
|
15
15
|
#
|
|
16
16
|
# The templates ask for the *rendered fragment* rather than the type, so
|
|
@@ -34,15 +34,15 @@ module Loam
|
|
|
34
34
|
|
|
35
35
|
private
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
@
|
|
37
|
+
def open_loam_key_type
|
|
38
|
+
@open_loam_key_type ||= begin
|
|
39
39
|
requested = (options[:primary_key_type] || host_app_primary_key_type).to_s
|
|
40
40
|
requested = "bigint" if requested.empty?
|
|
41
41
|
|
|
42
42
|
unless SUPPORTED.include?(requested)
|
|
43
43
|
raise Thor::Error,
|
|
44
|
-
"Unsupported --primary-key-type #{requested.inspect}.
|
|
45
|
-
"A key type
|
|
44
|
+
"Unsupported --primary-key-type #{requested.inspect}. OpenLoam generates #{SUPPORTED.join(', ')}. " \
|
|
45
|
+
"A key type OpenLoam does not know how to size would produce migrations that look right and " \
|
|
46
46
|
"do not line up with your tables."
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -60,40 +60,40 @@ module Loam
|
|
|
60
60
|
nil
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def open_loam_key_limit
|
|
64
64
|
return options[:key_limit].to_i if options[:key_limit]
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
open_loam_key_type == :string ? DEFAULT_STRING_LIMIT : nil
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# Appended to `create_table :x` — "" for bigint, so the default path is
|
|
70
70
|
# byte-for-byte what it was.
|
|
71
|
-
def
|
|
72
|
-
return "" if
|
|
71
|
+
def open_loam_id_option
|
|
72
|
+
return "" if open_loam_key_type == :bigint
|
|
73
73
|
|
|
74
|
-
"#{
|
|
74
|
+
"#{open_loam_key_fragment.sub('type:', 'id:')}"
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# Appended inside `t.references :x, ...`.
|
|
78
|
-
def
|
|
79
|
-
return "" if
|
|
78
|
+
def open_loam_type_option
|
|
79
|
+
return "" if open_loam_key_type == :bigint
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
open_loam_key_fragment
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
# The bare column type for a polymorphic or un-constrained `*_id`, which
|
|
85
85
|
# cannot use t.references.
|
|
86
|
-
def
|
|
87
|
-
|
|
86
|
+
def open_loam_key_column_type
|
|
87
|
+
open_loam_key_type
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
def
|
|
91
|
-
|
|
90
|
+
def open_loam_key_limit_option
|
|
91
|
+
open_loam_key_limit ? ", limit: #{open_loam_key_limit}" : ""
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
def
|
|
95
|
-
fragment = ", type: :#{
|
|
96
|
-
fragment += ", limit: #{
|
|
94
|
+
def open_loam_key_fragment
|
|
95
|
+
fragment = ", type: :#{open_loam_key_type}"
|
|
96
|
+
fragment += ", limit: #{open_loam_key_limit}" if open_loam_key_limit
|
|
97
97
|
fragment
|
|
98
98
|
end
|
|
99
99
|
end
|
data/lib/open-loam.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Bundler auto-requires the gem's name ("open-loam") on load. The library's
|
|
2
|
-
# entry point and whole namespace are `
|
|
2
|
+
# entry point and whole namespace are `open_loam` / `OpenLoam::` — the gem is only
|
|
3
3
|
# *distributed* as open-loam (the plain `loam` name is taken on RubyGems). This
|
|
4
4
|
# shim bridges the two so `gem "open-loam"` works with the default require.
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "open_loam"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# Files on a record, via ActiveStorage:
|
|
3
3
|
#
|
|
4
4
|
# equipment.files.attach(io: File.open(path), filename: "manual.pdf")
|
|
5
5
|
# equipment.files.each { |file| url_for(file) }
|
|
6
6
|
#
|
|
7
|
-
# KNOWN BOUNDARY: ActiveStorage blobs live in global tables that
|
|
7
|
+
# KNOWN BOUNDARY: ActiveStorage blobs live in global tables that OpenLoam does
|
|
8
8
|
# NOT tenant-scope. The association is scoped (a record only ever lists its
|
|
9
9
|
# own files, and the record is tenant-scoped), but a signed blob URL is a
|
|
10
10
|
# bearer capability: whoever holds the link can fetch the file, with no
|
|
@@ -15,8 +15,8 @@ module Loam
|
|
|
15
15
|
|
|
16
16
|
included do
|
|
17
17
|
unless respond_to?(:has_many_attached)
|
|
18
|
-
raise
|
|
19
|
-
"#{name} includes
|
|
18
|
+
raise OpenLoam::Error,
|
|
19
|
+
"#{name} includes OpenLoam::Attachable but ActiveStorage is not available. " \
|
|
20
20
|
"Run `bin/rails active_storage:install && bin/rails db:migrate`, or remove the include " \
|
|
21
21
|
"(this app was probably generated with --skip-active-storage)."
|
|
22
22
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# Audit-by-default. Included in every generated entity: create/update/destroy
|
|
3
|
-
# writes a
|
|
3
|
+
# writes a OpenLoam::AuditRecord tagged with tenant + actor + changeset. "Who
|
|
4
4
|
# changed the excavator's price, and when" is answered structurally, not by
|
|
5
5
|
# remembering to log.
|
|
6
6
|
module Auditable
|
|
@@ -9,54 +9,54 @@ module Loam
|
|
|
9
9
|
IGNORED_ATTRIBUTES = %w[updated_at created_at].freeze
|
|
10
10
|
|
|
11
11
|
included do
|
|
12
|
-
after_create {
|
|
13
|
-
after_update {
|
|
14
|
-
after_destroy {
|
|
12
|
+
after_create { open_loam_audit("create") }
|
|
13
|
+
after_update { open_loam_audit(open_loam_audit_action) if open_loam_audit_changes.any? }
|
|
14
|
+
after_destroy { open_loam_audit("destroy") }
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
|
-
# Relabel the audit that the save inside the block writes.
|
|
19
|
+
# Relabel the audit that the save inside the block writes. OpenLoam::SoftDeletable
|
|
20
20
|
# uses this to record a soft-delete (an UPDATE) as "soft_delete"/"restore"
|
|
21
21
|
# rather than the "update" it would otherwise be — one audit path, reused,
|
|
22
22
|
# not a second one to keep in sync.
|
|
23
|
-
def
|
|
24
|
-
previous = @
|
|
25
|
-
@
|
|
23
|
+
def open_loam_audit_as(action)
|
|
24
|
+
previous = @open_loam_audit_action
|
|
25
|
+
@open_loam_audit_action = action
|
|
26
26
|
yield
|
|
27
27
|
ensure
|
|
28
|
-
@
|
|
28
|
+
@open_loam_audit_action = previous
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def
|
|
32
|
-
@
|
|
31
|
+
def open_loam_audit_action
|
|
32
|
+
@open_loam_audit_action || "update"
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def
|
|
36
|
-
|
|
35
|
+
def open_loam_audit(action)
|
|
36
|
+
OpenLoam::AuditRecord.create!(
|
|
37
37
|
auditable_type: self.class.name,
|
|
38
38
|
auditable_id: id,
|
|
39
39
|
action: action,
|
|
40
|
-
actor_id:
|
|
41
|
-
changeset: action == "destroy" ? {} :
|
|
40
|
+
actor_id: OpenLoam::Current.actor&.id,
|
|
41
|
+
changeset: action == "destroy" ? {} : open_loam_audit_changes
|
|
42
42
|
)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def
|
|
45
|
+
def open_loam_audit_changes
|
|
46
46
|
changes = saved_changes.except(*IGNORED_ATTRIBUTES)
|
|
47
|
-
|
|
47
|
+
open_loam_redact_encrypted(changes)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# The audit trail must record the FACT that an encrypted field changed, never
|
|
51
51
|
# its value — neither the plaintext nor the ciphertext (a ciphertext still
|
|
52
52
|
# leaks length and, over time, correlations). So an encrypted column's change
|
|
53
53
|
# becomes "[encrypted]", and its blind-index sibling is dropped entirely.
|
|
54
|
-
# A no-op for models without
|
|
55
|
-
def
|
|
56
|
-
return changes unless self.class.respond_to?(:
|
|
54
|
+
# A no-op for models without OpenLoam::Encryptable.
|
|
55
|
+
def open_loam_redact_encrypted(changes)
|
|
56
|
+
return changes unless self.class.respond_to?(:open_loam_encrypted_attributes)
|
|
57
57
|
|
|
58
|
-
encrypted = self.class.
|
|
59
|
-
hash_columns = self.class.
|
|
58
|
+
encrypted = self.class.open_loam_encrypted_attributes.map(&:to_s)
|
|
59
|
+
hash_columns = self.class.open_loam_searchable_encrypted_attributes.map { |name| "#{name}_hash" }
|
|
60
60
|
|
|
61
61
|
changes.each_with_object({}) do |(column, values), redacted|
|
|
62
62
|
next if hash_columns.include?(column)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# Rate-limiting + lockout for authentication, to blunt online brute-force of
|
|
3
3
|
# passwords and (especially) 6-digit TOTP codes. A DB-backed counter keyed by
|
|
4
4
|
# the submitted identifier: after N failures within a window, the identifier is
|
|
5
5
|
# locked; a success clears the counter.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# OpenLoam::AuthThrottle.locked?(email) # refuse if true
|
|
8
|
+
# OpenLoam::AuthThrottle.record_failure(email, kind: "password", ip: request.ip)
|
|
9
|
+
# OpenLoam::AuthThrottle.clear(email) # on success
|
|
10
10
|
#
|
|
11
11
|
# PER-IDENTIFIER is the primary defense (an attacker targets one account /
|
|
12
12
|
# code). A per-ip throttle to blunt spraying across accounts is a clean
|
|
@@ -15,7 +15,7 @@ module Loam
|
|
|
15
15
|
# (needs a cache store + a gem); this DB counter is the portable,
|
|
16
16
|
# single-process-correct prototype.
|
|
17
17
|
#
|
|
18
|
-
# Thresholds come from
|
|
18
|
+
# Thresholds come from OpenLoam::Configs (per-tenant-overridable, but read globally
|
|
19
19
|
# here at the auth layer with sane defaults):
|
|
20
20
|
# security.max_auth_attempts (default 10)
|
|
21
21
|
# security.auth_window_minutes (default 15) — failures counted in this window
|
|
@@ -24,15 +24,15 @@ module Loam
|
|
|
24
24
|
module_function
|
|
25
25
|
|
|
26
26
|
def max_attempts
|
|
27
|
-
|
|
27
|
+
OpenLoam::Configs.get("security.max_auth_attempts", default: 10).to_i
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def window
|
|
31
|
-
|
|
31
|
+
OpenLoam::Configs.get("security.auth_window_minutes", default: 15).to_i.minutes
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def lockout
|
|
35
|
-
|
|
35
|
+
OpenLoam::Configs.get("security.auth_lockout_minutes", default: 15).to_i.minutes
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Log a failed attempt. Recorded for ANY submitted identifier — existing or
|
|
@@ -41,7 +41,7 @@ module Loam
|
|
|
41
41
|
identifier = normalize(identifier)
|
|
42
42
|
return if identifier.blank?
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
OpenLoam::AuthAttempt.create!(identifier: identifier, kind: kind.to_s, ip: ip)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# Locked if there are >= max failures within the window. Old attempts age out
|
|
@@ -73,7 +73,7 @@ module Loam
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def attempts(identifier)
|
|
76
|
-
|
|
76
|
+
OpenLoam::AuthAttempt.where(identifier: normalize(identifier))
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def normalize(identifier)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# Datatable bulk actions over selected records — policy-checked PER record and
|
|
3
3
|
# tenant-scoped. The ids are resolved THROUGH the tenant scope
|
|
4
4
|
# (`model.where(id: ids)`), so a forged cross-tenant id is simply not found —
|
|
5
5
|
# a bulk op can never reach another tenant's rows.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# OpenLoam::Bulk.soft_delete(Equipment, params[:ids])
|
|
8
|
+
# OpenLoam::Bulk.set_field(Equipment, params[:ids], field: "status", value: "retired")
|
|
9
9
|
#
|
|
10
10
|
# Returns the number of records actually affected (a record the actor may not
|
|
11
11
|
# touch, or an id from another tenant, is silently skipped).
|
|
@@ -32,7 +32,7 @@ module Loam
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# A relation for "export selected" — tenant-scoped, so it composes with
|
|
35
|
-
#
|
|
35
|
+
# OpenLoam::Export.csv and can't leak another tenant's rows.
|
|
36
36
|
def selected(model, ids)
|
|
37
37
|
model.where(id: Array(ids))
|
|
38
38
|
end
|
|
@@ -40,20 +40,20 @@ module Loam
|
|
|
40
40
|
def each_permitted(model, ids)
|
|
41
41
|
count = 0
|
|
42
42
|
selected(model, ids).find_each do |record|
|
|
43
|
-
count += 1 if yield(record,
|
|
43
|
+
count += 1 if yield(record, OpenLoam::Policy.for(record))
|
|
44
44
|
end
|
|
45
45
|
count
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def writable_column?(model, field)
|
|
49
|
-
model.column_names.include?(field) &&
|
|
49
|
+
model.column_names.include?(field) && OpenLoam::Import::PLUMBING.exclude?(field) && !workflow_column?(model, field)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# A workflow status column may ONLY change through a transition (role-gated).
|
|
53
53
|
# Skip it here so a bulk set-field can't self-"approve" (the model guard is
|
|
54
54
|
# the backstop; this keeps the bulk op a clean no-op rather than an error).
|
|
55
55
|
def workflow_column?(model, field)
|
|
56
|
-
model.respond_to?(:
|
|
56
|
+
model.respond_to?(:open_loam_workflow) && model.open_loam_workflow&.column == field
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
end
|