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,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PermissionForge
|
|
4
|
+
module Concerns
|
|
5
|
+
module MaskedAttributes
|
|
6
|
+
# Model-level masking of virtual (non-column) readers: plain methods,
|
|
7
|
+
# store accessors and attribute-API attributes. Part of MaskedAttributes;
|
|
8
|
+
# opt-in through config.mask_virtual_attributes.
|
|
9
|
+
#
|
|
10
|
+
# Wrapping a reader is permanent for the class, the decision is not:
|
|
11
|
+
# every wrapper asks the configuration on each call, so a wrapped class
|
|
12
|
+
# behaves like an unwrapped one while the config does not select it.
|
|
13
|
+
#
|
|
14
|
+
# Not covered: write-back protection for virtual setters.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# Customer.pf_mask_virtual_readers # the engine does this at boot
|
|
18
|
+
# customer.full_address # hidden / masked / raw for current_user
|
|
19
|
+
# customer.pf_without_virtual_masking { customer.full_address } # always raw
|
|
20
|
+
#
|
|
21
|
+
# @api private
|
|
22
|
+
module VirtualReaders
|
|
23
|
+
extend ActiveSupport::Concern
|
|
24
|
+
|
|
25
|
+
# The module a class's wrappers live in, prepended to that class.
|
|
26
|
+
class ReaderModule < Module; end
|
|
27
|
+
|
|
28
|
+
# Values a mask strategy can be applied to. Anything else (arrays,
|
|
29
|
+
# hashes, records, booleans, dates) is hidden instead of masked: a mask
|
|
30
|
+
# of its to_s would be meaningless and could leak.
|
|
31
|
+
MASKABLE_TYPES = [String, Symbol, Numeric].freeze
|
|
32
|
+
|
|
33
|
+
# Stands in for an unmaskable value, so the guard decides without the
|
|
34
|
+
# real value ever being stringified. Not a String: MaskValue masks the
|
|
35
|
+
# to_s of what it is given, so no mask can be this object.
|
|
36
|
+
PROBE = Object.new.freeze
|
|
37
|
+
|
|
38
|
+
READER_NAME = /\A[a-z_][a-zA-Z0-9_]*\z/
|
|
39
|
+
REQUIRED_PARAMETERS = %i[req keyreq].freeze
|
|
40
|
+
|
|
41
|
+
# ExecutionState slot: set (by identity) of the records inside
|
|
42
|
+
# pf_without_virtual_masking.
|
|
43
|
+
UNMASKED_KEY = :pf_unmasked_virtual_records
|
|
44
|
+
|
|
45
|
+
# Whether reading `name` on a record of `klass` is masked right now:
|
|
46
|
+
# the call reaches a wrapper on that class and
|
|
47
|
+
# config.mask_virtual_attributes selects the model. False for a class
|
|
48
|
+
# without the concern.
|
|
49
|
+
def self.masked?(klass, name)
|
|
50
|
+
klass.respond_to?(:pf_virtual_reader_masked?) &&
|
|
51
|
+
PermissionForge.configuration.mask_virtual_attributes_for?(klass.name) &&
|
|
52
|
+
klass.pf_virtual_reader_masked?(name)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Extended into the model by ActiveSupport::Concern.
|
|
56
|
+
module ClassMethods
|
|
57
|
+
# Wraps virtual readers so they return what the current user may see.
|
|
58
|
+
# Idempotent. Never wraps the primary key, a column, a method
|
|
59
|
+
# ActiveRecord::Base defines, a method that does not exist or one
|
|
60
|
+
# that needs arguments.
|
|
61
|
+
#
|
|
62
|
+
# @param names [Array<String, Symbol>] defaults to the virtual fields
|
|
63
|
+
# the admin lists for the model (declared, plus discovered ones
|
|
64
|
+
# while config.virtual_field_discovery is on). When the model's
|
|
65
|
+
# extra_fields_method raises, its declared names are not wrapped
|
|
66
|
+
# and the log says so.
|
|
67
|
+
# @return [Array<String>] the names this call wrapped
|
|
68
|
+
def pf_mask_virtual_readers(names = pf_virtual_field_names(wrapping: true))
|
|
69
|
+
pf_virtual_readers(names).reject { |name| pf_virtual_reader_masked?(name) }
|
|
70
|
+
.each { |name| pf_define_virtual_reader(name) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @return [Array<String>] the names in `names` that have a reader this
|
|
74
|
+
# concern can mask, wrapped already or not
|
|
75
|
+
def pf_virtual_readers(names = pf_virtual_field_names)
|
|
76
|
+
names.map(&:to_s).uniq.select { |name| pf_virtual_reader?(name) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @return [Array<String>] every wrapped reader, inherited ones included
|
|
80
|
+
def pf_masked_virtual_readers
|
|
81
|
+
ancestors.grep(ReaderModule).flat_map { |readers| readers.instance_methods(false) }.map(&:to_s).uniq
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# True when calling `name` on this class reaches a wrapper: its own,
|
|
85
|
+
# or one inherited from a superclass it does not override.
|
|
86
|
+
def pf_virtual_reader_masked?(name)
|
|
87
|
+
method_defined?(name) && instance_method(name).owner.is_a?(ReaderModule)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# `wrapping` — the names are about to be wrapped, so an error from the
|
|
93
|
+
# model's extra_fields_method (logged by VirtualFields#extra, which
|
|
94
|
+
# then gives no declared names) leaves readers unmasked.
|
|
95
|
+
def pf_virtual_field_names(wrapping: false)
|
|
96
|
+
fields = PermissionForge::VirtualFields.new(self, PermissionForge.configuration)
|
|
97
|
+
names = fields.names
|
|
98
|
+
error = fields.extra_error
|
|
99
|
+
pf_warn_virtual_readers_unmasked(error) if wrapping && error
|
|
100
|
+
names
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def pf_warn_virtual_readers_unmasked(error)
|
|
104
|
+
method = PermissionForge.configuration.extra_fields_method
|
|
105
|
+
Rails.logger&.warn("[PermissionForge] SECURITY warning: the virtual readers #{name} declares are NOT " \
|
|
106
|
+
"masked on the model: #{name}.#{method} raised #{error.class}: #{error.message}. " \
|
|
107
|
+
'Fix it and restart, or call pf_mask_virtual_readers with explicit names.')
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def pf_virtual_reader?(name)
|
|
111
|
+
return false unless name.match?(READER_NAME)
|
|
112
|
+
return false if Array(primary_key).map(&:to_s).include?(name) || columns_hash.key?(name)
|
|
113
|
+
|
|
114
|
+
attribute_names.include?(name) || (!pf_framework_method?(name) && pf_argumentless_reader?(name))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# `id`, `attributes`, `save`…: wrapping these would break the record.
|
|
118
|
+
def pf_framework_method?(name)
|
|
119
|
+
ActiveRecord::Base.method_defined?(name) || ActiveRecord::Base.private_method_defined?(name)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def pf_argumentless_reader?(name)
|
|
123
|
+
public_method_defined?(name) &&
|
|
124
|
+
instance_method(name).parameters.none? { |type, _| REQUIRED_PARAMETERS.include?(type) }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def pf_define_virtual_reader(name)
|
|
128
|
+
if attribute_names.include?(name)
|
|
129
|
+
pf_define_virtual_attribute_reader(name)
|
|
130
|
+
else
|
|
131
|
+
pf_define_virtual_method_reader(name)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Attribute-API virtuals go through read_attribute, like columns.
|
|
136
|
+
def pf_define_virtual_attribute_reader(name)
|
|
137
|
+
pf_virtual_readers_module.define_method(name) do
|
|
138
|
+
pf_mask_virtual_reads? ? read_attribute(name) : super()
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def pf_define_virtual_method_reader(name)
|
|
143
|
+
pf_virtual_readers_module.define_method(name) do |*args, **options, &block|
|
|
144
|
+
pf_guard_virtual_read(name, super(*args, **options, &block))
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# This class's own wrapper module (a subclass gets its own, so a
|
|
149
|
+
# reader it overrides is wrapped where it is defined).
|
|
150
|
+
def pf_virtual_readers_module
|
|
151
|
+
return const_get(:MaskedVirtualReaders, false) if const_defined?(:MaskedVirtualReaders, false)
|
|
152
|
+
|
|
153
|
+
const_set(:MaskedVirtualReaders, ReaderModule.new).tap { |readers| prepend(readers) }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Runs the block with this record's virtual readers unmasked. For the
|
|
158
|
+
# model's own callbacks and validations, which otherwise compute with
|
|
159
|
+
# the masked value. Scoped to this instance and restored when the block
|
|
160
|
+
# ends, however it ends.
|
|
161
|
+
#
|
|
162
|
+
# The flag is kept beside the record (by identity, for the running
|
|
163
|
+
# fiber: ExecutionState), not on it: a dup, clone or serialized copy made inside the
|
|
164
|
+
# block is a different object and stays masked.
|
|
165
|
+
def pf_without_virtual_masking
|
|
166
|
+
unmasked = PermissionForge::ExecutionState.fetch(UNMASKED_KEY) { Set.new.compare_by_identity }
|
|
167
|
+
added = unmasked.add?(self)
|
|
168
|
+
yield
|
|
169
|
+
ensure
|
|
170
|
+
unmasked.delete(self) if added
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# `methods:` names the current user may not read are dropped, the way
|
|
174
|
+
# hidden columns are dropped from the attribute names, and listed as
|
|
175
|
+
# hidden in the read's field.access event (one read with the columns).
|
|
176
|
+
def serializable_hash(options = nil)
|
|
177
|
+
PermissionForge::Audit::FieldAccess.read(self, self.class.name, record: self) do
|
|
178
|
+
super(pf_without_hidden_virtual_methods(options))
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
def pf_mask_virtual_reads?
|
|
185
|
+
return false if PermissionForge::ExecutionState[UNMASKED_KEY]&.include?(self)
|
|
186
|
+
|
|
187
|
+
PermissionForge.configuration.mask_virtual_attributes_for?(self.class.name)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# What a wrapped reader returns for `raw`: the raw value, its mask, or
|
|
191
|
+
# the hidden value.
|
|
192
|
+
def pf_guard_virtual_read(name, raw)
|
|
193
|
+
return raw unless pf_mask_virtual_reads?
|
|
194
|
+
return pf_guard_unmaskable_read(name, raw) unless pf_maskable?(raw)
|
|
195
|
+
|
|
196
|
+
include_field, value = pf_guard(name, raw)
|
|
197
|
+
include_field ? value : PermissionForge.configuration.hidden_attribute_value
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Passed or hidden, never masked. The guard decides on a probe, so the
|
|
201
|
+
# value itself is never stringified, and the read passes only when the
|
|
202
|
+
# probe comes back untouched from a field that is not Masked. Masked
|
|
203
|
+
# is read from the field's entry, not from what the mask strategy
|
|
204
|
+
# returned: a custom strategy may hand its input back unchanged.
|
|
205
|
+
def pf_guard_unmaskable_read(name, raw)
|
|
206
|
+
include_field, value = pf_guard(name, PROBE)
|
|
207
|
+
passed = include_field && value.equal?(PROBE) && !pf_masked_field?(name)
|
|
208
|
+
passed ? raw : PermissionForge.configuration.hidden_attribute_value
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Truthy when the current user's entry for `name` is Masked, the test
|
|
212
|
+
# AttributeAccessGuard applies once it knows the field is readable.
|
|
213
|
+
def pf_masked_field?(name)
|
|
214
|
+
field = pf_current_field_permissions(name)
|
|
215
|
+
field && field[:mask_access]
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def pf_maskable?(raw)
|
|
219
|
+
raw.nil? || MASKABLE_TYPES.any? { |type| raw.is_a?(type) }
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def pf_without_hidden_virtual_methods(options)
|
|
223
|
+
methods = options && options[:methods]
|
|
224
|
+
return options unless methods && pf_omit_hidden_virtual_methods?
|
|
225
|
+
|
|
226
|
+
hidden, shown = Array(methods).partition { |name| pf_hidden_virtual_method?(name) }
|
|
227
|
+
PermissionForge::Audit::FieldAccess.omitted(self, self.class.name.to_sym, hidden, pf_guarded_user) unless hidden.empty?
|
|
228
|
+
options.merge(methods: shown)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def pf_hidden_virtual_method?(name)
|
|
232
|
+
self.class.pf_masked_virtual_readers.include?(name.to_s) && pf_hidden_attribute?(name)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def pf_omit_hidden_virtual_methods?
|
|
236
|
+
pf_mask_virtual_reads? && (pf_guarded_user || pf_hide_without_user?) &&
|
|
237
|
+
PermissionForge.configuration.omit_hidden_in_attributes_hash
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|