permission_forge 0.14.1
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 +1138 -0
- data/LICENSE.txt +21 -0
- data/README.md +356 -0
- data/app/controllers/concerns/permission_forge/acting_admin.rb +17 -0
- data/app/controllers/concerns/permission_forge/api/crud_actions.rb +84 -0
- data/app/controllers/concerns/permission_forge/api/nested_resource_lookup.rb +18 -0
- data/app/controllers/concerns/permission_forge/api/pagination.rb +50 -0
- data/app/controllers/concerns/permission_forge/api/response_renderer.rb +65 -0
- data/app/controllers/concerns/permission_forge/api/serializer_resolver.rb +30 -0
- data/app/controllers/concerns/permission_forge/api_authentication.rb +40 -0
- data/app/controllers/concerns/permission_forge/assignment_authorization.rb +97 -0
- data/app/controllers/concerns/permission_forge/audit_log_gate.rb +55 -0
- data/app/controllers/concerns/permission_forge/basic_authentication.rb +77 -0
- data/app/controllers/concerns/permission_forge/cache_confirmation.rb +32 -0
- data/app/controllers/concerns/permission_forge/forgery_protection.rb +29 -0
- data/app/controllers/concerns/permission_forge/html_format.rb +40 -0
- data/app/controllers/concerns/permission_forge/param_shapes.rb +87 -0
- data/app/controllers/concerns/permission_forge/tenant_required.rb +42 -0
- data/app/controllers/permission_forge/api/audit_events_controller.rb +33 -0
- data/app/controllers/permission_forge/api/base_controller.rb +220 -0
- data/app/controllers/permission_forge/api/dashboard_controller.rb +30 -0
- data/app/controllers/permission_forge/api/permission_set_group_users_controller.rb +51 -0
- data/app/controllers/permission_forge/api/permission_set_groups_controller.rb +71 -0
- data/app/controllers/permission_forge/api/permission_set_rules_controller.rb +37 -0
- data/app/controllers/permission_forge/api/permission_set_users_controller.rb +105 -0
- data/app/controllers/permission_forge/api/permission_sets_controller.rb +87 -0
- data/app/controllers/permission_forge/api/resource_actions_controller.rb +58 -0
- data/app/controllers/permission_forge/api/resource_fields_controller.rb +30 -0
- data/app/controllers/permission_forge/api/resources_controller.rb +56 -0
- data/app/controllers/permission_forge/application_controller.rb +198 -0
- data/app/controllers/permission_forge/assets_controller.rb +97 -0
- data/app/controllers/permission_forge/assignments_controller.rb +266 -0
- data/app/controllers/permission_forge/audit_events_controller.rb +94 -0
- data/app/controllers/permission_forge/bulk_assignments_controller.rb +108 -0
- data/app/controllers/permission_forge/field_lookups_controller.rb +38 -0
- data/app/controllers/permission_forge/home_controller.rb +24 -0
- data/app/controllers/permission_forge/maintenance_controller.rb +13 -0
- data/app/controllers/permission_forge/people_controller.rb +190 -0
- data/app/controllers/permission_forge/permission_set_editor.rb +71 -0
- data/app/controllers/permission_forge/permission_set_group_users_controller.rb +57 -0
- data/app/controllers/permission_forge/permission_set_groups_controller.rb +111 -0
- data/app/controllers/permission_forge/permission_set_rules_controller.rb +97 -0
- data/app/controllers/permission_forge/permission_sets_controller.rb +227 -0
- data/app/controllers/permission_forge/preferences_controller.rb +26 -0
- data/app/controllers/permission_forge/resource_actions_controller.rb +106 -0
- data/app/controllers/permission_forge/resource_fields_controller.rb +129 -0
- data/app/controllers/permission_forge/resources_controller.rb +189 -0
- data/app/controllers/permission_forge/settings_controller.rb +33 -0
- data/app/frontend/permission_forge/brand/apple-touch-icon.png +0 -0
- data/app/frontend/permission_forge/brand/chevron.svg +1 -0
- data/app/frontend/permission_forge/brand/favicon.ico +0 -0
- data/app/frontend/permission_forge/brand/favicon.svg +1 -0
- data/app/frontend/permission_forge/brand/logo-horizontal-dark.svg +1 -0
- data/app/frontend/permission_forge/brand/logo-horizontal-light.svg +1 -0
- data/app/frontend/permission_forge/brand/mark-small.svg +1 -0
- data/app/frontend/permission_forge/brand/mark.svg +1 -0
- data/app/frontend/permission_forge/pf_admin.css +766 -0
- data/app/frontend/permission_forge/pf_admin.js +482 -0
- data/app/helpers/permission_forge/application_helper.rb +167 -0
- data/app/helpers/permission_forge/audit_helper.rb +230 -0
- data/app/helpers/permission_forge/audit_sentences_helper.rb +84 -0
- data/app/helpers/permission_forge/badges_helper.rb +134 -0
- data/app/helpers/permission_forge/changes_helper.rb +117 -0
- data/app/helpers/permission_forge/people_helper.rb +118 -0
- data/app/helpers/permission_forge/sentences_helper.rb +175 -0
- data/app/helpers/permission_forge/settings_helper.rb +152 -0
- data/app/helpers/permission_forge/ui_helper.rb +184 -0
- data/app/models/permission_forge/audit_event.rb +159 -0
- data/app/models/permission_forge/permission_set.rb +100 -0
- data/app/models/permission_forge/permission_set_group.rb +83 -0
- data/app/models/permission_forge/permission_set_group_user.rb +43 -0
- data/app/models/permission_forge/permission_set_rule.rb +99 -0
- data/app/models/permission_forge/permission_set_user.rb +158 -0
- data/app/models/permission_forge/resource.rb +100 -0
- data/app/models/permission_forge/resource_action.rb +58 -0
- data/app/models/permission_forge/resource_field.rb +69 -0
- data/app/views/layouts/permission_forge/application.html.erb +30 -0
- data/app/views/layouts/permission_forge/fragment.html.erb +2 -0
- data/app/views/permission_forge/assignments/_person_options.html.erb +26 -0
- data/app/views/permission_forge/assignments/confirm_destroy.html.erb +24 -0
- data/app/views/permission_forge/assignments/new.html.erb +99 -0
- data/app/views/permission_forge/audit_events/index.html.erb +161 -0
- data/app/views/permission_forge/audit_events/show.html.erb +146 -0
- data/app/views/permission_forge/bulk_assignments/confirm_revoke.html.erb +21 -0
- data/app/views/permission_forge/bulk_assignments/new.html.erb +69 -0
- data/app/views/permission_forge/field_lookups/show.html.erb +32 -0
- data/app/views/permission_forge/home/_audit_card.html.erb +19 -0
- data/app/views/permission_forge/home/_ending_card.html.erb +32 -0
- data/app/views/permission_forge/home/_expired_card.html.erb +34 -0
- data/app/views/permission_forge/home/_privacy_card.html.erb +67 -0
- data/app/views/permission_forge/home/_rules_card.html.erb +23 -0
- data/app/views/permission_forge/home/_stale_card.html.erb +39 -0
- data/app/views/permission_forge/home/_tidy_card.html.erb +18 -0
- data/app/views/permission_forge/home/index.html.erb +69 -0
- data/app/views/permission_forge/people/_access_tab.html.erb +58 -0
- data/app/views/permission_forge/people/_audit_tab.html.erb +93 -0
- data/app/views/permission_forge/people/_effective_field_source.html.erb +11 -0
- data/app/views/permission_forge/people/_effective_tab.html.erb +118 -0
- data/app/views/permission_forge/people/_effective_why.html.erb +48 -0
- data/app/views/permission_forge/people/_why_item.html.erb +19 -0
- data/app/views/permission_forge/people/confirm_remove_all.html.erb +27 -0
- data/app/views/permission_forge/people/consent_preview.html.erb +39 -0
- data/app/views/permission_forge/people/erasure_preview.html.erb +28 -0
- data/app/views/permission_forge/people/index.html.erb +78 -0
- data/app/views/permission_forge/people/show.html.erb +54 -0
- data/app/views/permission_forge/permission_set_groups/confirm_destroy.html.erb +12 -0
- data/app/views/permission_forge/permission_set_groups/index.html.erb +55 -0
- data/app/views/permission_forge/permission_set_groups/show.html.erb +100 -0
- data/app/views/permission_forge/permission_set_rules/_builder.html.erb +145 -0
- data/app/views/permission_forge/permission_set_rules/_value_input.html.erb +42 -0
- data/app/views/permission_forge/permission_set_rules/confirm_destroy.html.erb +13 -0
- data/app/views/permission_forge/permission_set_rules/edit.html.erb +3 -0
- data/app/views/permission_forge/permission_set_rules/new.html.erb +3 -0
- data/app/views/permission_forge/permission_sets/_assignments_tab.html.erb +74 -0
- data/app/views/permission_forge/permission_sets/_audit_tab.html.erb +80 -0
- data/app/views/permission_forge/permission_sets/_conditions_tab.html.erb +83 -0
- data/app/views/permission_forge/permission_sets/_header.html.erb +36 -0
- data/app/views/permission_forge/permission_sets/_records_summary.html.erb +16 -0
- data/app/views/permission_forge/permission_sets/_resource_summary.html.erb +11 -0
- data/app/views/permission_forge/permission_sets/_resources_tab.html.erb +49 -0
- data/app/views/permission_forge/permission_sets/confirm_destroy.html.erb +23 -0
- data/app/views/permission_forge/permission_sets/confirm_switch_mode.html.erb +15 -0
- data/app/views/permission_forge/permission_sets/index.html.erb +83 -0
- data/app/views/permission_forge/permission_sets/new.html.erb +63 -0
- data/app/views/permission_forge/permission_sets/review.html.erb +26 -0
- data/app/views/permission_forge/permission_sets/show.html.erb +50 -0
- data/app/views/permission_forge/resource_actions/_across_sets.html.erb +24 -0
- data/app/views/permission_forge/resource_actions/_add_select.html.erb +12 -0
- data/app/views/permission_forge/resource_actions/_effect.html.erb +25 -0
- data/app/views/permission_forge/resource_actions/_endpoints.html.erb +16 -0
- data/app/views/permission_forge/resource_actions/_form.html.erb +44 -0
- data/app/views/permission_forge/resource_actions/_managed_note.html.erb +11 -0
- data/app/views/permission_forge/resource_actions/confirm_destroy.html.erb +13 -0
- data/app/views/permission_forge/resource_actions/new.html.erb +31 -0
- data/app/views/permission_forge/resource_actions/show.html.erb +39 -0
- data/app/views/permission_forge/resource_fields/_explain.html.erb +24 -0
- data/app/views/permission_forge/resource_fields/show.html.erb +186 -0
- data/app/views/permission_forge/resources/_actions_tab.html.erb +88 -0
- data/app/views/permission_forge/resources/_fields_tab.html.erb +89 -0
- data/app/views/permission_forge/resources/_rules_tab.html.erb +55 -0
- data/app/views/permission_forge/resources/_sync.html.erb +57 -0
- data/app/views/permission_forge/resources/confirm_destroy.html.erb +17 -0
- data/app/views/permission_forge/resources/new.html.erb +57 -0
- data/app/views/permission_forge/resources/show.html.erb +117 -0
- data/app/views/permission_forge/settings/_ladder.html.erb +15 -0
- data/app/views/permission_forge/settings/_row.html.erb +38 -0
- data/app/views/permission_forge/settings/show.html.erb +67 -0
- data/app/views/permission_forge/shared/_across_sets.html.erb +31 -0
- data/app/views/permission_forge/shared/_audit_events.html.erb +36 -0
- data/app/views/permission_forge/shared/_audit_footer.html.erb +16 -0
- data/app/views/permission_forge/shared/_audit_off_note.html.erb +15 -0
- data/app/views/permission_forge/shared/_audit_pagination.html.erb +19 -0
- data/app/views/permission_forge/shared/_audit_refused_note.html.erb +7 -0
- data/app/views/permission_forge/shared/_audit_timeline.html.erb +29 -0
- data/app/views/permission_forge/shared/_crud_cell.html.erb +18 -0
- data/app/views/permission_forge/shared/_dialog_frame.html.erb +11 -0
- data/app/views/permission_forge/shared/_expiry_picker.html.erb +27 -0
- data/app/views/permission_forge/shared/_head_extra.html.erb +3 -0
- data/app/views/permission_forge/shared/_help_banner.html.erb +10 -0
- data/app/views/permission_forge/shared/_level_control.html.erb +18 -0
- data/app/views/permission_forge/shared/_mode_banners.html.erb +22 -0
- data/app/views/permission_forge/shared/_nav.html.erb +12 -0
- data/app/views/permission_forge/shared/_preferences.html.erb +24 -0
- data/app/views/permission_forge/shared/_reason_field.html.erb +20 -0
- data/app/views/permission_forge/shared/_savebar.html.erb +13 -0
- data/app/views/permission_forge/shared/_sheet_frame.html.erb +22 -0
- data/app/views/permission_forge/shared/_sidebar.html.erb +18 -0
- data/app/views/permission_forge/shared/_tech_details.html.erb +20 -0
- data/app/views/permission_forge/shared/_toasts.html.erb +27 -0
- data/app/views/permission_forge/shared/_topbar.html.erb +15 -0
- data/app/views/permission_forge/shared/_user_select.html.erb +16 -0
- data/app/views/permission_forge/shared/audit_refused.html.erb +7 -0
- data/config/locales/permission_forge.en.yml +2115 -0
- data/config/locales/permission_forge.mk.yml +2166 -0
- data/config/locales/permission_forge.sq.yml +2164 -0
- data/config/routes.rb +143 -0
- data/lib/generators/permission_forge/install/install_generator.rb +105 -0
- data/lib/generators/permission_forge/install/templates/initializer.rb.tt +148 -0
- data/lib/generators/permission_forge/install/templates/migration.rb.tt +198 -0
- data/lib/generators/permission_forge/rls/rls_generator.rb +57 -0
- data/lib/generators/permission_forge/rls/templates/enable_permission_forge_rls.rb.tt +15 -0
- data/lib/generators/permission_forge/upgrade/templates/add_admin_ui_columns.rb.tt +8 -0
- data/lib/generators/permission_forge/upgrade/templates/add_audit_events.rb.tt +98 -0
- data/lib/generators/permission_forge/upgrade/templates/add_resource_fields_unique_index.rb.tt +69 -0
- data/lib/generators/permission_forge/upgrade/upgrade_generator.rb +66 -0
- data/lib/permission_forge/admin/action_across_sets.rb +76 -0
- data/lib/permission_forge/admin/action_rows.rb +156 -0
- data/lib/permission_forge/admin/attention_items.rb +207 -0
- data/lib/permission_forge/admin/audit_event_detail.rb +75 -0
- data/lib/permission_forge/admin/audit_log.rb +193 -0
- data/lib/permission_forge/admin/audit_log_days.rb +41 -0
- data/lib/permission_forge/admin/audit_lookups.rb +105 -0
- data/lib/permission_forge/admin/audit_name_history.rb +85 -0
- data/lib/permission_forge/admin/audit_trail.rb +169 -0
- data/lib/permission_forge/admin/bulk_people_matcher.rb +109 -0
- data/lib/permission_forge/admin/change_set.rb +123 -0
- data/lib/permission_forge/admin/change_set_parts/actions.rb +27 -0
- data/lib/permission_forge/admin/change_set_parts/base.rb +58 -0
- data/lib/permission_forge/admin/change_set_parts/context.rb +50 -0
- data/lib/permission_forge/admin/change_set_parts/field_edit.rb +67 -0
- data/lib/permission_forge/admin/change_set_parts/field_levels.rb +71 -0
- data/lib/permission_forge/admin/change_set_parts/new_actions.rb +91 -0
- data/lib/permission_forge/admin/change_set_parts/new_fields.rb +63 -0
- data/lib/permission_forge/admin/change_set_parts/resource_edit.rb +55 -0
- data/lib/permission_forge/admin/change_set_parts/resource_flags.rb +19 -0
- data/lib/permission_forge/admin/change_set_parts/set_attrs.rb +45 -0
- data/lib/permission_forge/admin/change_set_parts/values.rb +20 -0
- data/lib/permission_forge/admin/column_fields.rb +48 -0
- data/lib/permission_forge/admin/conditions_form.rb +78 -0
- data/lib/permission_forge/admin/controller_catalog.rb +166 -0
- data/lib/permission_forge/admin/csv.rb +41 -0
- data/lib/permission_forge/admin/editable_lock.rb +157 -0
- data/lib/permission_forge/admin/effective_access.rb +158 -0
- data/lib/permission_forge/admin/effective_fields.rb +176 -0
- data/lib/permission_forge/admin/expiry_presets.rb +51 -0
- data/lib/permission_forge/admin/exports/document.rb +89 -0
- data/lib/permission_forge/admin/exports/events.rb +51 -0
- data/lib/permission_forge/admin/exports/holdings.rb +118 -0
- data/lib/permission_forge/admin/exports.rb +37 -0
- data/lib/permission_forge/admin/field_across_sets.rb +51 -0
- data/lib/permission_forge/admin/field_detail.rb +112 -0
- data/lib/permission_forge/admin/field_level.rb +74 -0
- data/lib/permission_forge/admin/field_rows.rb +137 -0
- data/lib/permission_forge/admin/last_changes.rb +94 -0
- data/lib/permission_forge/admin/model_catalog.rb +216 -0
- data/lib/permission_forge/admin/people_candidates.rb +108 -0
- data/lib/permission_forge/admin/people_directory.rb +158 -0
- data/lib/permission_forge/admin/permission_set_factory.rb +48 -0
- data/lib/permission_forge/admin/person_label.rb +95 -0
- data/lib/permission_forge/admin/record_id.rb +19 -0
- data/lib/permission_forge/admin/rule_builder.rb +58 -0
- data/lib/permission_forge/admin/rule_form.rb +175 -0
- data/lib/permission_forge/admin/rule_form_input.rb +74 -0
- data/lib/permission_forge/admin/sensitive_columns.rb +68 -0
- data/lib/permission_forge/admin/settings_catalog.rb +261 -0
- data/lib/permission_forge/admin/settings_values.rb +130 -0
- data/lib/permission_forge/api/serializers/audit_event_serializer.rb +27 -0
- data/lib/permission_forge/api/serializers/base_serializer.rb +36 -0
- data/lib/permission_forge/api/serializers/permission_set_group_serializer.rb +12 -0
- data/lib/permission_forge/api/serializers/permission_set_group_user_serializer.rb +11 -0
- data/lib/permission_forge/api/serializers/permission_set_rule_serializer.rb +25 -0
- data/lib/permission_forge/api/serializers/permission_set_serializer.rb +13 -0
- data/lib/permission_forge/api/serializers/permission_set_user_serializer.rb +11 -0
- data/lib/permission_forge/api/serializers/resource_action_serializer.rb +13 -0
- data/lib/permission_forge/api/serializers/resource_field_serializer.rb +14 -0
- data/lib/permission_forge/api/serializers/resource_serializer.rb +15 -0
- data/lib/permission_forge/audit/actor.rb +144 -0
- data/lib/permission_forge/audit/actor_resolver.rb +103 -0
- data/lib/permission_forge/audit/audited_views.rb +208 -0
- data/lib/permission_forge/audit/boot_warnings.rb +58 -0
- data/lib/permission_forge/audit/column_values.rb +56 -0
- data/lib/permission_forge/audit/context.rb +98 -0
- data/lib/permission_forge/audit/detached.rb +62 -0
- data/lib/permission_forge/audit/event_catalog.rb +110 -0
- data/lib/permission_forge/audit/event_query.rb +192 -0
- data/lib/permission_forge/audit/failure.rb +47 -0
- data/lib/permission_forge/audit/field_access.rb +186 -0
- data/lib/permission_forge/audit/gate.rb +167 -0
- data/lib/permission_forge/audit/job_context.rb +32 -0
- data/lib/permission_forge/audit/log_access.rb +36 -0
- data/lib/permission_forge/audit/recorder.rb +168 -0
- data/lib/permission_forge/audit/refusals.rb +61 -0
- data/lib/permission_forge/audit/request_context.rb +152 -0
- data/lib/permission_forge/audit/runtime.rb +178 -0
- data/lib/permission_forge/audit/runtime_emission.rb +135 -0
- data/lib/permission_forge/audit/settings_observer.rb +70 -0
- data/lib/permission_forge/audit/source.rb +54 -0
- data/lib/permission_forge/audit/subscribers/base.rb +69 -0
- data/lib/permission_forge/audit/subscribers/legacy_hooks.rb +97 -0
- data/lib/permission_forge/audit/subscribers/log.rb +45 -0
- data/lib/permission_forge/audit/subscribers.rb +58 -0
- data/lib/permission_forge/audit.rb +302 -0
- data/lib/permission_forge/boot_checks.rb +184 -0
- data/lib/permission_forge/cache/backlog.rb +142 -0
- data/lib/permission_forge/cache/base.rb +200 -0
- data/lib/permission_forge/cache/bulk_invalidation.rb +131 -0
- data/lib/permission_forge/cache/failure.rb +171 -0
- data/lib/permission_forge/cache/handled_errors.rb +82 -0
- data/lib/permission_forge/cache/holder_invalidation.rb +85 -0
- data/lib/permission_forge/cache/invalidation.rb +180 -0
- data/lib/permission_forge/cache/memory_cache.rb +77 -0
- data/lib/permission_forge/cache/null_cache.rb +40 -0
- data/lib/permission_forge/cache/rails_cache.rb +231 -0
- data/lib/permission_forge/cache/rebuild.rb +149 -0
- data/lib/permission_forge/cache/settlement.rb +63 -0
- data/lib/permission_forge/cache/user_keys.rb +206 -0
- data/lib/permission_forge/cache/variant_removal.rb +55 -0
- data/lib/permission_forge/cache/withheld_notice.rb +60 -0
- data/lib/permission_forge/concerns/audited_collection.rb +34 -0
- data/lib/permission_forge/concerns/auto_permission_scoping.rb +158 -0
- data/lib/permission_forge/concerns/controller_action_guard.rb +106 -0
- data/lib/permission_forge/concerns/field_mask_checker.rb +45 -0
- data/lib/permission_forge/concerns/has_active_scope.rb +12 -0
- data/lib/permission_forge/concerns/has_audit_trail.rb +59 -0
- data/lib/permission_forge/concerns/has_permission_set_users.rb +115 -0
- data/lib/permission_forge/concerns/has_tenant_scoping.rb +86 -0
- data/lib/permission_forge/concerns/has_uuid.rb +24 -0
- data/lib/permission_forge/concerns/invalidates_in_batch.rb +207 -0
- data/lib/permission_forge/concerns/invalidates_managed_actions.rb +80 -0
- data/lib/permission_forge/concerns/masked_attributes/alias_access.rb +280 -0
- data/lib/permission_forge/concerns/masked_attributes/store_access.rb +100 -0
- data/lib/permission_forge/concerns/masked_attributes/validation_reads.rb +153 -0
- data/lib/permission_forge/concerns/masked_attributes/virtual_readers.rb +242 -0
- data/lib/permission_forge/concerns/masked_attributes.rb +576 -0
- data/lib/permission_forge/concerns/permission_cache_accessor.rb +416 -0
- data/lib/permission_forge/concerns/permission_scoped_findable.rb +337 -0
- data/lib/permission_forge/concerns/permissioned_user.rb +78 -0
- data/lib/permission_forge/concerns/permissions_memo.rb +160 -0
- data/lib/permission_forge/concerns/permissions_per_context.rb +288 -0
- data/lib/permission_forge/concerns/permissions_regenerator.rb +43 -0
- data/lib/permission_forge/concerns/records_audit_events.rb +286 -0
- data/lib/permission_forge/concerns/resource_permission_checker.rb +116 -0
- data/lib/permission_forge/concerns/validates_column_lengths.rb +69 -0
- data/lib/permission_forge/concerns/validates_dependency.rb +54 -0
- data/lib/permission_forge/conditional_sets.rb +361 -0
- data/lib/permission_forge/configuration/admin_ui_settings.rb +108 -0
- data/lib/permission_forge/configuration/audit_settings.rb +184 -0
- data/lib/permission_forge/configuration/cache_settings.rb +76 -0
- data/lib/permission_forge/configuration/controller_action_settings.rb +112 -0
- data/lib/permission_forge/configuration/gdpr_settings.rb +70 -0
- data/lib/permission_forge/configuration/masked_attributes_settings.rb +83 -0
- data/lib/permission_forge/configuration/permits_attributes_settings.rb +26 -0
- data/lib/permission_forge/configuration/presets.rb +139 -0
- data/lib/permission_forge/configuration/query_scoping_settings.rb +107 -0
- data/lib/permission_forge/configuration/strategy_values.rb +57 -0
- data/lib/permission_forge/configuration/virtual_attributes_settings.rb +65 -0
- data/lib/permission_forge/configuration.rb +624 -0
- data/lib/permission_forge/controller_actions/authorizer.rb +171 -0
- data/lib/permission_forge/controller_actions/managed_index.rb +213 -0
- data/lib/permission_forge/controller_actions/mapping.rb +167 -0
- data/lib/permission_forge/controller_actions.rb +122 -0
- data/lib/permission_forge/deprecation.rb +86 -0
- data/lib/permission_forge/engine.rb +113 -0
- data/lib/permission_forge/execution_state.rb +318 -0
- data/lib/permission_forge/invalidation_batch.rb +206 -0
- data/lib/permission_forge/masked_models.rb +226 -0
- data/lib/permission_forge/masking_sync.rb +271 -0
- data/lib/permission_forge/model_bindings.rb +90 -0
- data/lib/permission_forge/open_transaction.rb +303 -0
- data/lib/permission_forge/operators/constants.rb +41 -0
- data/lib/permission_forge/operators/registry.rb +300 -0
- data/lib/permission_forge/operators/rule_column.rb +120 -0
- data/lib/permission_forge/operators/validations.rb +111 -0
- data/lib/permission_forge/operators/value_casters/number.rb +68 -0
- data/lib/permission_forge/operators/value_casters/temporal.rb +69 -0
- data/lib/permission_forge/operators/value_casters.rb +80 -0
- data/lib/permission_forge/operators/value_typing.rb +115 -0
- data/lib/permission_forge/query_scoping_warnings.rb +68 -0
- data/lib/permission_forge/rake_audit.rb +65 -0
- data/lib/permission_forge/resource_chain.rb +190 -0
- data/lib/permission_forge/rls.rb +209 -0
- data/lib/permission_forge/services/attribute_access_guard.rb +138 -0
- data/lib/permission_forge/services/audit_event_purger.rb +150 -0
- data/lib/permission_forge/services/bulk_permission_set_assignment.rb +108 -0
- data/lib/permission_forge/services/concerns/across_tenants.rb +69 -0
- data/lib/permission_forge/services/concerns/audited_operation.rb +43 -0
- data/lib/permission_forge/services/concerns/clonable.rb +29 -0
- data/lib/permission_forge/services/consent_withdrawal_handler.rb +135 -0
- data/lib/permission_forge/services/dashboard_stats.rb +43 -0
- data/lib/permission_forge/services/data_subject_erasure.rb +306 -0
- data/lib/permission_forge/services/expired_assignment_cleaner.rb +152 -0
- data/lib/permission_forge/services/extra_field_defaults.rb +71 -0
- data/lib/permission_forge/services/field_permission_merger.rb +83 -0
- data/lib/permission_forge/services/filters_response_attributes.rb +144 -0
- data/lib/permission_forge/services/gdpr_field_auditor/text_report.rb +63 -0
- data/lib/permission_forge/services/gdpr_field_auditor.rb +223 -0
- data/lib/permission_forge/services/host_key_guard.rb +110 -0
- data/lib/permission_forge/services/mask_value.rb +93 -0
- data/lib/permission_forge/services/permission_cache_clearer.rb +174 -0
- data/lib/permission_forge/services/permission_hash_builder.rb +305 -0
- data/lib/permission_forge/services/permission_rule_applier.rb +192 -0
- data/lib/permission_forge/services/permission_set_cloner.rb +89 -0
- data/lib/permission_forge/services/permission_set_condition_evaluator.rb +102 -0
- data/lib/permission_forge/services/permission_set_group_cloner.rb +52 -0
- data/lib/permission_forge/services/permits_attributes/nested_records.rb +106 -0
- data/lib/permission_forge/services/permits_attributes.rb +270 -0
- data/lib/permission_forge/services/resource_field_generator.rb +93 -0
- data/lib/permission_forge/services/resource_with_fields_builder.rb +47 -0
- data/lib/permission_forge/services/rls_operator_sql.rb +131 -0
- data/lib/permission_forge/services/rls_policy_generator.rb +365 -0
- data/lib/permission_forge/services/security_audit/action_check.rb +157 -0
- data/lib/permission_forge/services/security_audit/assignment_check.rb +120 -0
- data/lib/permission_forge/services/security_audit/audit_trail_check.rb +166 -0
- data/lib/permission_forge/services/security_audit/base_check.rb +57 -0
- data/lib/permission_forge/services/security_audit/config_check.rb +212 -0
- data/lib/permission_forge/services/security_audit/integrity_check.rb +105 -0
- data/lib/permission_forge/services/security_audit/personal_data_check.rb +79 -0
- data/lib/permission_forge/services/security_audit/privilege_check.rb +67 -0
- data/lib/permission_forge/services/security_audit/rule_check.rb +141 -0
- data/lib/permission_forge/services/security_audit/text_report.rb +46 -0
- data/lib/permission_forge/services/security_audit/user_model_check.rb +67 -0
- data/lib/permission_forge/services/security_audit.rb +75 -0
- data/lib/permission_forge/services/stale_assignment_auditor.rb +68 -0
- data/lib/permission_forge/services/unscoped_query_detector.rb +132 -0
- data/lib/permission_forge/version.rb +3 -0
- data/lib/permission_forge/virtual_fields/discovery.rb +65 -0
- data/lib/permission_forge/virtual_fields.rb +102 -0
- data/lib/permission_forge.rb +464 -0
- data/lib/tasks/permission_forge.rake +31 -0
- data/lib/tasks/permission_forge_audit_events.rake +28 -0
- data/lib/tasks/permission_forge_cache.rake +26 -0
- data/lib/tasks/permission_forge_expiration.rake +30 -0
- data/lib/tasks/permission_forge_gdpr.rake +69 -0
- data/lib/tasks/permission_forge_rls.rake +38 -0
- metadata +515 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PermissionForge
|
|
4
|
+
# What a model took from the configuration as it loaded — the user class of
|
|
5
|
+
# an assignment (PermissionSetUser#user, PermissionSetGroupUser#user), the
|
|
6
|
+
# audit user class (created_by / updated_by) and the tenant model
|
|
7
|
+
# (HasTenantScoping) — against what the configuration says now. A setting
|
|
8
|
+
# changed after the class loaded (a PermissionForge.configure or a setter
|
|
9
|
+
# in after_initialize, a console) changes nothing in the class: a
|
|
10
|
+
# development reload picks it up, an eager-loaded production process never
|
|
11
|
+
# does. The engine logs each difference once the host's after_initialize
|
|
12
|
+
# blocks have run, and PermissionForge.configure logs it too. Nothing here
|
|
13
|
+
# loads a model. (table_name_prefix is not compared: table_name is a
|
|
14
|
+
# method that reads it on every call.)
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
module ModelBindings
|
|
18
|
+
# The settings a model declares as it loads, each with the association
|
|
19
|
+
# that carries it (the tenant is read from HasTenantScoping).
|
|
20
|
+
ASSOCIATIONS = { user_class: :user, audit_user_class: :created_by }.freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# One sentence per setting that loaded models declared differently.
|
|
24
|
+
#
|
|
25
|
+
# @param config [Configuration]
|
|
26
|
+
# @return [Array<String>]
|
|
27
|
+
def mismatches(config = PermissionForge.configuration)
|
|
28
|
+
found = Hash.new { |hash, key| hash[key] = [] }
|
|
29
|
+
loaded_models.each do |model|
|
|
30
|
+
declared_otherwise(model, config).each do |setting, declared|
|
|
31
|
+
found[[setting, config.public_send(setting), declared]] << model.name
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
found.map { |difference, models| sentence(difference, models) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Logs every mismatch.
|
|
38
|
+
#
|
|
39
|
+
# @param config [Configuration]
|
|
40
|
+
# @param logger [Logger, nil]
|
|
41
|
+
# @return [void]
|
|
42
|
+
def warn!(config = PermissionForge.configuration, logger: default_logger)
|
|
43
|
+
mismatches(config).each { |sentence| logger&.warn("[PermissionForge] #{sentence}") }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The model classes already loaded, without loading one.
|
|
47
|
+
#
|
|
48
|
+
# @return [Array<Class>]
|
|
49
|
+
def loaded_models
|
|
50
|
+
MODEL_NAMES.select { |name| PermissionForge.autoload?(name).nil? && PermissionForge.const_defined?(name, false) }
|
|
51
|
+
.map { |name| PermissionForge.const_get(name) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# [setting, what the model declared] for each setting the model
|
|
57
|
+
# declared and the configuration now says differently. A model without
|
|
58
|
+
# the association (a PermissionSet has no :user) declares nothing.
|
|
59
|
+
def declared_otherwise(model, config)
|
|
60
|
+
declared(model).reject { |setting, value| value == current(config, setting) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def declared(model)
|
|
64
|
+
pairs = ASSOCIATIONS.filter_map do |setting, association|
|
|
65
|
+
reflection = model.reflect_on_association(association)
|
|
66
|
+
[setting, reflection.class_name] if reflection
|
|
67
|
+
end
|
|
68
|
+
pairs << [:tenant_model, model.tenant_scoping_model]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def current(config, setting)
|
|
72
|
+
return (config.tenant_model.to_s if config.tenant_enabled?) if setting == :tenant_model
|
|
73
|
+
|
|
74
|
+
config.public_send(setting).to_s
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def sentence(difference, models)
|
|
78
|
+
setting, now, declared = difference
|
|
79
|
+
"config.#{setting} is #{now.inspect} but #{models.join(', ')} loaded with " \
|
|
80
|
+
"#{declared.inspect}: a loaded class keeps what it declared until it is loaded again (a code reload in " \
|
|
81
|
+
'development; never in an eager-loaded production process). Configure PermissionForge in ' \
|
|
82
|
+
'config/initializers.'
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def default_logger
|
|
86
|
+
Rails.logger if defined?(Rails) && Rails.respond_to?(:logger)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PermissionForge
|
|
4
|
+
# The database transaction the calling code runs in, on the connection of
|
|
5
|
+
# the permission models — if one is open whose rows nobody else can see yet
|
|
6
|
+
# and which may still be rolled back.
|
|
7
|
+
#
|
|
8
|
+
# Open means: a joinable transaction is on the connection's stack. A
|
|
9
|
+
# transaction that is not joinable does not count by itself — that is how
|
|
10
|
+
# Rails wraps a test (and how test-prof's `before_all` and DatabaseCleaner
|
|
11
|
+
# wrap theirs): everything a test does runs inside it, and Rails treats it
|
|
12
|
+
# as no transaction at all (commit callbacks run inside it). Whatever is
|
|
13
|
+
# opened inside one counts, and so does a non-joinable transaction inside
|
|
14
|
+
# an ordinary one. `rails console --sandbox` is one non-joinable
|
|
15
|
+
# transaction too, and there it does count: nothing done in a sandbox is
|
|
16
|
+
# ever committed.
|
|
17
|
+
#
|
|
18
|
+
# What is asked of it:
|
|
19
|
+
# - #withhold?: whether a permission hash built inside it has to stay
|
|
20
|
+
# out of the shared cache — the transaction reads from a snapshot
|
|
21
|
+
# (#snapshot?, `config.snapshot_transactions`), or it is the sandbox.
|
|
22
|
+
# - #finished?: whether it has ended since, committed or rolled back —
|
|
23
|
+
# the outermost transaction that counted, that is.
|
|
24
|
+
# - #rolled_back?: whether the transaction it was asked in, or one around
|
|
25
|
+
# it, was rolled back since. What was read inside it is void then, also
|
|
26
|
+
# while a transaction around it is still open.
|
|
27
|
+
# - #after_commit: runs a block once everybody can see what the
|
|
28
|
+
# transaction did — after the outermost one has committed — and never
|
|
29
|
+
# after this transaction, or one around it, was rolled back.
|
|
30
|
+
#
|
|
31
|
+
# All of it is asked of the adapter's own transaction objects, on every
|
|
32
|
+
# Rails version: the stack of the connection's transaction manager (Rails
|
|
33
|
+
# has no reader for it; it is `@stack` from 7.1 to 8.1), each transaction's
|
|
34
|
+
# `joinable?`, `state` and `isolation_level`, and `add_record` for the
|
|
35
|
+
# block, which is enlisted the way Rails enlists a record that has commit
|
|
36
|
+
# callbacks. The public API of Rails 7.2+ (ActiveRecord::Transaction) is not
|
|
37
|
+
# used: it does not see a transaction that is not joinable, it does not say
|
|
38
|
+
# how a transaction ended, and Rails skips a block registered through it
|
|
39
|
+
# when a commit callback of any record in the transaction raises.
|
|
40
|
+
#
|
|
41
|
+
# @api private
|
|
42
|
+
class OpenTransaction
|
|
43
|
+
# Isolation levels at which a transaction reads from one snapshot.
|
|
44
|
+
SNAPSHOT_LEVELS = %i[repeatable_read serializable].freeze
|
|
45
|
+
|
|
46
|
+
# Adapters whose transactions read committed rows unless they were opened
|
|
47
|
+
# with a stronger isolation level. Every other adapter is taken to read
|
|
48
|
+
# from a snapshot (MySQL at its default, SQLite) under `:auto`.
|
|
49
|
+
READ_COMMITTED_ADAPTERS = %w[PostgreSQL PostGIS].freeze
|
|
50
|
+
|
|
51
|
+
class << self
|
|
52
|
+
# @param model [Class] a model on the connection in question
|
|
53
|
+
# @return [OpenTransaction, nil] nil when no transaction is open
|
|
54
|
+
def current(model = PermissionForge::PermissionSet)
|
|
55
|
+
pool = model.connection_pool
|
|
56
|
+
# a thread that holds no connection has no transaction
|
|
57
|
+
return unless pool.active_connection?
|
|
58
|
+
|
|
59
|
+
pool.with_connection { |connection| on(connection) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The open transaction, when what is built inside it must stay out of
|
|
63
|
+
# the shared cache. The question a host asks before it writes a cache
|
|
64
|
+
# entry of its own that is derived from the permissions.
|
|
65
|
+
#
|
|
66
|
+
# @return [OpenTransaction, nil]
|
|
67
|
+
def withholding(model = PermissionForge::PermissionSet)
|
|
68
|
+
return if PermissionForge.configuration.snapshot_transactions == false && !sandbox?
|
|
69
|
+
|
|
70
|
+
transaction = current(model)
|
|
71
|
+
transaction if transaction&.withhold?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [Array] the adapter's open transactions, outermost first
|
|
75
|
+
# @raise [PermissionForge::ConfigurationError] when this Rails keeps
|
|
76
|
+
# them elsewhere. Looking at the innermost transaction alone would
|
|
77
|
+
# answer, wrongly and silently: a hash a rollback takes back would be
|
|
78
|
+
# cached.
|
|
79
|
+
def stack_of(connection)
|
|
80
|
+
manager = connection.transaction_manager
|
|
81
|
+
return manager.instance_variable_get(:@stack) if manager.instance_variable_defined?(:@stack)
|
|
82
|
+
|
|
83
|
+
raise PermissionForge::ConfigurationError,
|
|
84
|
+
"PermissionForge cannot read the open transactions of #{manager.class} (ActiveRecord " \
|
|
85
|
+
"#{ActiveRecord.version}): this version of the gem does not support that Rails version"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @return [Boolean] whether this process is `rails console --sandbox`
|
|
89
|
+
def sandbox?
|
|
90
|
+
return false unless defined?(Rails) && Rails.respond_to?(:application)
|
|
91
|
+
|
|
92
|
+
application = Rails.application
|
|
93
|
+
application.respond_to?(:sandbox?) && application.sandbox? == true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def on(connection)
|
|
99
|
+
stack = stack_of(connection)
|
|
100
|
+
root = sandbox? ? stack.first : stack.find(&:joinable?)
|
|
101
|
+
new(connection, root, stack.last) if root
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# What identifies the transaction the handle was asked for (the innermost
|
|
106
|
+
# one), for as long as it is referenced.
|
|
107
|
+
#
|
|
108
|
+
# @return [Object] the adapter's transaction
|
|
109
|
+
attr_reader :subject
|
|
110
|
+
|
|
111
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
112
|
+
# @param root [Object] the outermost transaction that counts
|
|
113
|
+
# @param subject [Object] the innermost transaction
|
|
114
|
+
def initialize(connection, root, subject)
|
|
115
|
+
@connection = connection
|
|
116
|
+
@root = root
|
|
117
|
+
@subject = subject
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @return [Boolean]
|
|
121
|
+
def withhold?
|
|
122
|
+
self.class.sandbox? || snapshot?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Whether the transaction is treated as reading from a snapshot:
|
|
126
|
+
# `config.snapshot_transactions`, and under `:auto` the adapter and the
|
|
127
|
+
# isolation level the transaction was opened with. The server is not
|
|
128
|
+
# asked.
|
|
129
|
+
#
|
|
130
|
+
# @return [Boolean]
|
|
131
|
+
def snapshot?
|
|
132
|
+
setting = PermissionForge.configuration.snapshot_transactions
|
|
133
|
+
return setting if [true, false].include?(setting)
|
|
134
|
+
|
|
135
|
+
!READ_COMMITTED_ADAPTERS.include?(@connection.adapter_name) || snapshot_isolation?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Also true when the transaction is not on the connection's stack any
|
|
139
|
+
# more without having ended (the connection was reset or closed), and for
|
|
140
|
+
# a copy that was marshalled.
|
|
141
|
+
#
|
|
142
|
+
# @return [Boolean]
|
|
143
|
+
def finished?
|
|
144
|
+
return true unless @root
|
|
145
|
+
|
|
146
|
+
!!@root.state.finalized? || self.class.stack_of(@connection).none? { |transaction| transaction.equal?(@root) }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# (A transaction that rolls back marks the ones inside it as rolled back,
|
|
150
|
+
# also those that had been released before.)
|
|
151
|
+
#
|
|
152
|
+
# @return [Boolean]
|
|
153
|
+
def rolled_back?
|
|
154
|
+
!!@subject&.state&.rolledback?
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Whether what was read inside it is still good: it has neither ended
|
|
158
|
+
# nor been rolled back to a savepoint.
|
|
159
|
+
#
|
|
160
|
+
# @return [Boolean]
|
|
161
|
+
def stands?
|
|
162
|
+
!(finished? || rolled_back?)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Whether what was read inside it can be used where it is asked: it
|
|
166
|
+
# stands, and the calling thread (fiber) works on its connection —
|
|
167
|
+
# another connection does not see its rows, which may still be rolled
|
|
168
|
+
# back.
|
|
169
|
+
#
|
|
170
|
+
# @return [Boolean]
|
|
171
|
+
def visible?
|
|
172
|
+
own_connection? && stands?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Whether the calling thread (fiber) works on its connection.
|
|
176
|
+
#
|
|
177
|
+
# @return [Boolean]
|
|
178
|
+
def own_connection?
|
|
179
|
+
pool = @connection&.pool
|
|
180
|
+
return false unless pool&.active_connection?
|
|
181
|
+
|
|
182
|
+
pool.with_connection { |connection| connection.equal?(@connection) }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Whether what is tied to it must not be replaced, from the calling
|
|
186
|
+
# thread, by what is tied to `other` (another transaction, or none): it
|
|
187
|
+
# is still open, on a connection the calling thread does not work on.
|
|
188
|
+
#
|
|
189
|
+
# @param other [OpenTransaction, nil]
|
|
190
|
+
# @return [Boolean]
|
|
191
|
+
def held_elsewhere?(other)
|
|
192
|
+
!(equal?(other) || finished? || own_connection?)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# @return [void]
|
|
196
|
+
def after_commit(&block)
|
|
197
|
+
@subject.add_record(CommitHook.new(after: block))
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Runs a block just before the outermost transaction commits — after the
|
|
201
|
+
# transaction it was registered in, and every one around it, has decided
|
|
202
|
+
# to commit. A block that raises makes the commit fail: Rails rolls the
|
|
203
|
+
# transaction back and raises the error to the code that opened it. That
|
|
204
|
+
# is how a change whose audit event could not be written is kept from
|
|
205
|
+
# being committed by a caller that rescued the error (Audit).
|
|
206
|
+
#
|
|
207
|
+
# Never runs after this transaction, or one around it, was rolled back.
|
|
208
|
+
#
|
|
209
|
+
# @return [void]
|
|
210
|
+
def before_commit(&block)
|
|
211
|
+
@subject.add_record(CommitHook.new(before: block))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Runs a block once, when what this transaction did is decided either
|
|
215
|
+
# way: after the outermost transaction has committed, or once it — or
|
|
216
|
+
# this one, or one in between — has rolled back and no transaction that
|
|
217
|
+
# counts is open any more. A savepoint that rolls back while
|
|
218
|
+
# transactions around it are still open hands the block to them; one
|
|
219
|
+
# that is released takes it along (Rails moves its records). For what
|
|
220
|
+
# must be written whichever way the caller's transaction ends: a
|
|
221
|
+
# refusal or a failed sign-in (Audit::Detached), a runtime decision
|
|
222
|
+
# (Audit.instrument).
|
|
223
|
+
#
|
|
224
|
+
# The block runs from Rails' commit or rollback, where raising would
|
|
225
|
+
# replace the caller's own exception or fail a transaction that has
|
|
226
|
+
# already committed: an error is reported (Rails.error) and logged,
|
|
227
|
+
# never raised — also when the connection cannot say whether a
|
|
228
|
+
# transaction is still open (the block then runs). Nothing is kept on a
|
|
229
|
+
# record.
|
|
230
|
+
#
|
|
231
|
+
# @return [void]
|
|
232
|
+
def after_end(&block)
|
|
233
|
+
hand_on = lambda do
|
|
234
|
+
enclosing = CommitHook.run_detached(-> { self.class.send(:on, @connection) if @connection&.active? })
|
|
235
|
+
enclosing ? CommitHook.run_detached(-> { enclosing.after_end(&block) }) : CommitHook.run_detached(block)
|
|
236
|
+
end
|
|
237
|
+
@subject.add_record(CommitHook.new(after: -> { CommitHook.run_detached(block) }, rollback: hand_on))
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# A user that still holds the hash it built inside a transaction can be
|
|
241
|
+
# marshalled (cached). Nothing of a transaction can be dumped, and the
|
|
242
|
+
# copy is in no transaction: for it, this one has finished.
|
|
243
|
+
def marshal_dump = nil
|
|
244
|
+
|
|
245
|
+
def marshal_load(_state)
|
|
246
|
+
@connection = @root = @subject = nil
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
private
|
|
250
|
+
|
|
251
|
+
def snapshot_isolation?
|
|
252
|
+
self.class.stack_of(@connection).any? { |transaction| SNAPSHOT_LEVELS.include?(transaction.isolation_level&.to_sym) }
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Blocks in the place of a record: what Rails calls on the records of a
|
|
256
|
+
# transaction when it commits or rolls back. The record moves to the
|
|
257
|
+
# enclosing transaction when a nested one commits. `before_committed!` is
|
|
258
|
+
# called by the transaction that runs the commit callbacks before its
|
|
259
|
+
# COMMIT — an error there makes Rails roll back instead. `committed!` is
|
|
260
|
+
# called after the COMMIT, for every record, also when an after_commit of
|
|
261
|
+
# another one raised, and before the blocks registered through
|
|
262
|
+
# ActiveRecord::Transaction#after_commit. `rolledback!` is called by the
|
|
263
|
+
# transaction that rolls back (a savepoint too), and again for records left
|
|
264
|
+
# when a callback of another one raised: each block runs once.
|
|
265
|
+
class CommitHook
|
|
266
|
+
# Runs a block where nothing may be raised (OpenTransaction#after_end).
|
|
267
|
+
def self.run_detached(block)
|
|
268
|
+
block.call
|
|
269
|
+
rescue StandardError => e
|
|
270
|
+
Rails.logger&.error("[PermissionForge] transaction end hook failed — #{e.class}: #{e.message}") if defined?(Rails)
|
|
271
|
+
Rails.error.report(e, handled: true, source: 'permission_forge.open_transaction') if defined?(Rails) && Rails.respond_to?(:error)
|
|
272
|
+
nil
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def initialize(after: nil, before: nil, rollback: nil)
|
|
276
|
+
@after = after
|
|
277
|
+
@before = before
|
|
278
|
+
@rollback = rollback
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def trigger_transactional_callbacks?
|
|
282
|
+
true
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def before_committed!
|
|
286
|
+
@before&.call
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def committed!(*, **)
|
|
290
|
+
@rollback = nil
|
|
291
|
+
block = @after
|
|
292
|
+
@after = nil
|
|
293
|
+
block&.call
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def rolledback!(*, **)
|
|
297
|
+
block = @rollback
|
|
298
|
+
@rollback = nil
|
|
299
|
+
block&.call
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PermissionForge
|
|
4
|
+
module Operators
|
|
5
|
+
# Operator and join-type constants used by PermissionSetRule and Registry.
|
|
6
|
+
# Extracted to keep PermissionSetRule's constant count manageable.
|
|
7
|
+
# Include this module to access constants directly (e.g., EQUAL, JOINS).
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
module Constants
|
|
11
|
+
# Join types
|
|
12
|
+
LEFT_JOINS = 'left_joins'
|
|
13
|
+
LEFT_OUTER_JOINS = 'left_outer_joins'
|
|
14
|
+
JOINS = 'joins'
|
|
15
|
+
|
|
16
|
+
JOIN_TYPES = [LEFT_JOINS, LEFT_OUTER_JOINS, JOINS].freeze
|
|
17
|
+
|
|
18
|
+
# Operators
|
|
19
|
+
EQUAL = 'equal'
|
|
20
|
+
NOT_EQUAL = 'not_equal'
|
|
21
|
+
LESS_THAN = 'less_than'
|
|
22
|
+
GREATER_THAN = 'greater_than'
|
|
23
|
+
LESS_THAN_OR_EQUAL = 'less_than_or_equal'
|
|
24
|
+
GREATER_THAN_OR_EQUAL = 'greater_than_or_equal'
|
|
25
|
+
IN = 'in'
|
|
26
|
+
NOT_IN = 'not_in'
|
|
27
|
+
CONTAINS = 'contains'
|
|
28
|
+
NOT_CONTAINS = 'not_contains'
|
|
29
|
+
STARTS_WITH = 'starts_with'
|
|
30
|
+
ENDS_WITH = 'ends_with'
|
|
31
|
+
BETWEEN = 'between'
|
|
32
|
+
IS_NULL = 'is_null'
|
|
33
|
+
IS_NOT_NULL = 'is_not_null'
|
|
34
|
+
|
|
35
|
+
OPERATORS = [
|
|
36
|
+
EQUAL, NOT_EQUAL, LESS_THAN, GREATER_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN_OR_EQUAL,
|
|
37
|
+
IN, NOT_IN, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH, BETWEEN, IS_NULL, IS_NOT_NULL
|
|
38
|
+
].freeze
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|