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,69 @@
|
|
|
1
|
+
module PermissionForge
|
|
2
|
+
# Column-level read, edit and mask access to one field of a resource.
|
|
3
|
+
#
|
|
4
|
+
# @api public
|
|
5
|
+
class ResourceField < ActiveRecord::Base
|
|
6
|
+
# The table name, with the configured prefix (read on every call, so a prefix set at runtime applies).
|
|
7
|
+
def self.table_name = "#{PermissionForge.table_name_prefix}resource_fields"
|
|
8
|
+
|
|
9
|
+
include PermissionForge::Concerns::HasActiveScope
|
|
10
|
+
include PermissionForge::Concerns::HasUuid
|
|
11
|
+
include PermissionForge::Concerns::ValidatesColumnLengths
|
|
12
|
+
include PermissionForge::Concerns::HasAuditTrail
|
|
13
|
+
include PermissionForge::Concerns::HasTenantScoping
|
|
14
|
+
include PermissionForge::Concerns::HasPermissionSetUsers
|
|
15
|
+
include PermissionForge::Concerns::ValidatesDependency
|
|
16
|
+
include PermissionForge::Concerns::PermissionsRegenerator
|
|
17
|
+
include PermissionForge::Concerns::RecordsAuditEvents
|
|
18
|
+
|
|
19
|
+
permission_set_user_lookup :via_resource
|
|
20
|
+
|
|
21
|
+
# optional: as declared since 0.13 (the foreign key holds); stated, so the
|
|
22
|
+
# host's belongs_to_required_by_default decides nothing here.
|
|
23
|
+
belongs_to :resource, class_name: 'PermissionForge::Resource', optional: true
|
|
24
|
+
|
|
25
|
+
# Active rows of active resources of active Permission Sets — the rows the
|
|
26
|
+
# engine merges. A switched-off row counts as no row.
|
|
27
|
+
scope :in_effect, lambda {
|
|
28
|
+
active.joins(resource: :permission_set)
|
|
29
|
+
.merge(PermissionForge::Resource.active).merge(PermissionForge::PermissionSet.active)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# A duplicate (resource_id, name) row would let a second field entry with
|
|
33
|
+
# mask_access: false silently unmask the field via FieldPermissionMerger's
|
|
34
|
+
# AND logic. Enforced by a DB unique index (idx_pf_resource_fields_unique)
|
|
35
|
+
# as well; the validation surfaces a 422 instead of a RecordNotUnique 500.
|
|
36
|
+
# No case_sensitive option: the check then compares the way the column's
|
|
37
|
+
# collation does, and so the way the index does. On MySQL's default
|
|
38
|
+
# (case-insensitive) collation "Email" collides with "email" in the index;
|
|
39
|
+
# a forced case-sensitive check would let it through to a RecordNotUnique.
|
|
40
|
+
# Checked for a new row and on a rename only: a row whose name was
|
|
41
|
+
# blank before 0.14 stays editable in its other attributes.
|
|
42
|
+
validates :name, presence: { message: ->(*) { I18n.t('permission_forge.validations.name_blank') } },
|
|
43
|
+
if: -> { new_record? || will_save_change_to_name? }
|
|
44
|
+
validates :name, uniqueness: { scope: :resource_id }
|
|
45
|
+
|
|
46
|
+
validates_dependency :edit_access, requires: :read_access
|
|
47
|
+
validates_dependency :mask_access, requires: :read_access
|
|
48
|
+
|
|
49
|
+
validates :mask_strategy, presence: {
|
|
50
|
+
message: I18n.t('permission_forge.validations.resource_field.mask_strategy_required',
|
|
51
|
+
default: 'must be present when mask access is enabled')
|
|
52
|
+
},
|
|
53
|
+
if: :mask_access?
|
|
54
|
+
validates :mask_strategy, inclusion: {
|
|
55
|
+
in: ->(_) { PermissionForge::Services::MaskValue.strategies.keys },
|
|
56
|
+
message: I18n.t('permission_forge.validations.resource_field.invalid_mask_strategy',
|
|
57
|
+
default: 'is not a valid mask strategy')
|
|
58
|
+
},
|
|
59
|
+
if: :mask_access?
|
|
60
|
+
|
|
61
|
+
apply_tenant_scoping! # from the configuration, as the class loads (HasTenantScoping)
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def mask_access?
|
|
66
|
+
mask_access == true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="<%= I18n.locale %>" class="pf-root" data-theme="<%= pf_theme %>">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="robots" content="noindex, nofollow">
|
|
7
|
+
<title><%= [content_for(:pf_title).presence, t('permission_forge.layouts.application.title')].compact.join(' · ') %></title>
|
|
8
|
+
<%= csrf_meta_tags %>
|
|
9
|
+
<%= csp_meta_tag if respond_to?(:csp_meta_tag) %>
|
|
10
|
+
<link rel="icon" href="<%= pf_asset_url('favicon.ico') %>" sizes="any">
|
|
11
|
+
<link rel="icon" href="<%= pf_asset_url('favicon.svg') %>" type="image/svg+xml">
|
|
12
|
+
<link rel="apple-touch-icon" href="<%= pf_asset_url('apple-touch-icon.png') %>">
|
|
13
|
+
<link rel="stylesheet" href="<%= pf_asset_url('pf_admin.css') %>"<%= pf_nonce_attribute(:style) %>>
|
|
14
|
+
<script src="<%= pf_asset_url('pf_admin.js') %>" defer<%= pf_nonce_attribute(:script) %>></script>
|
|
15
|
+
<%= render 'permission_forge/shared/head_extra' %>
|
|
16
|
+
</head>
|
|
17
|
+
<body class="pf-body">
|
|
18
|
+
<a class="pf-skip" href="#pf-main"><%= t('permission_forge.layouts.application.skip_to_content') %></a>
|
|
19
|
+
<div class="pf-app">
|
|
20
|
+
<%= render 'permission_forge/shared/topbar' %>
|
|
21
|
+
<%= render 'permission_forge/shared/sidebar' %>
|
|
22
|
+
<main id="pf-main" class="pf-main" tabindex="-1">
|
|
23
|
+
<div class="pf-main__inner">
|
|
24
|
+
<%= yield %>
|
|
25
|
+
</div>
|
|
26
|
+
</main>
|
|
27
|
+
</div>
|
|
28
|
+
<%= render 'permission_forge/shared/toasts' %>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<%# Person picker rows. Locals: page (PeopleDirectory::Page), query, field, type ('radio'|'checkbox'),
|
|
2
|
+
selected (ids), invalid (error text). %>
|
|
3
|
+
<% selected = Array(local_assigns[:selected]).map(&:to_s) %>
|
|
4
|
+
<% invalid = local_assigns[:invalid] %>
|
|
5
|
+
<div class="pf-listbox" <%= pf_attrs_when(invalid, aria: { invalid: true }) %>>
|
|
6
|
+
<div class="pf-listbox__scroll" role="group" aria-label="<%= t('.label') %>">
|
|
7
|
+
<% if page.people.empty? %>
|
|
8
|
+
<p class="pf-option pf-text-3"><%= query.present? ? t('.no_match', query: query) : t('.nobody') %></p>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% page.people.each do |person| %>
|
|
11
|
+
<% label = pf_person(person) %>
|
|
12
|
+
<label class="pf-option">
|
|
13
|
+
<input type="<%= type %>" name="<%= type == 'checkbox' ? "#{field}[]" : field %>" value="<%= person.id %>" <%= 'checked' if selected.include?(person.id.to_s) %>>
|
|
14
|
+
<%= pf_avatar(person, size: 'sm') %>
|
|
15
|
+
<span class="pf-option__main">
|
|
16
|
+
<span class="pf-fw-600 pf-ellipsis"><%= label.name %></span>
|
|
17
|
+
<% if label.secondary %><span class="pf-fs-12 pf-text-3 pf-ellipsis"><%= label.secondary %></span><% end %>
|
|
18
|
+
</span>
|
|
19
|
+
</label>
|
|
20
|
+
<% end %>
|
|
21
|
+
</div>
|
|
22
|
+
<% if page.total > page.people.size %>
|
|
23
|
+
<div class="pf-listbox__footer"><%= t('.capped', shown: page.people.size, total: page.total) %></div>
|
|
24
|
+
<% end %>
|
|
25
|
+
</div>
|
|
26
|
+
<% if invalid %><p class="pf-error" role="alert"><%= invalid %></p><% end %>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<% person = pf_person_name(@assignment.user) %>
|
|
2
|
+
<% set = @assignment.permission_set %>
|
|
3
|
+
<% content_for :pf_title, t('.title', set: set.name, name: person) %>
|
|
4
|
+
<%= pf_dialog_frame({ title: t('.title', set: set.name, name: person), danger: true,
|
|
5
|
+
breadcrumb: pf_breadcrumb([t('permission_forge.nav.people'), permission_forge.people_path], [person, permission_forge.person_path(@assignment.user_id)], t('.crumb')) }) do %>
|
|
6
|
+
<p class="pf-text-2">
|
|
7
|
+
<% first = pf_person(@assignment.user).first_name %>
|
|
8
|
+
<% if set.is_muting %>
|
|
9
|
+
<%= t('.body_muting', name: first, set: set.name) %>
|
|
10
|
+
<% elsif @lost.any? %>
|
|
11
|
+
<%= t('.body', name: first, resources: @lost.to_sentence) %>
|
|
12
|
+
<% else %>
|
|
13
|
+
<%= t('.body_nothing', name: first) %>
|
|
14
|
+
<% end %>
|
|
15
|
+
</p>
|
|
16
|
+
<%= form_with url: permission_forge.assignment_path(@assignment), method: :delete, class: 'pf-confirm-form' do %>
|
|
17
|
+
<%= render 'permission_forge/shared/reason_field' %>
|
|
18
|
+
<div class="pf-actions pf-row--right">
|
|
19
|
+
<input type="hidden" name="return_to" value="<%= params[:return_to] %>">
|
|
20
|
+
<%= link_to t('permission_forge.ui.cancel'), pf_back_or(permission_forge.person_path(@assignment.user_id)), class: 'pf-btn', data: { pf_close: true } %>
|
|
21
|
+
<button type="submit" class="pf-btn pf-btn--danger-solid"><%= t('permission_forge.ui.remove') %></button>
|
|
22
|
+
</div>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<% content_for :pf_title, t('.title') %>
|
|
2
|
+
<% errors = @errors || {} %>
|
|
3
|
+
<% title = if @person then t('.title_person', name: pf_person_name(@person))
|
|
4
|
+
elsif @preset_set then t('.title_set', set: @preset_set.name)
|
|
5
|
+
else t('.title') end %>
|
|
6
|
+
<% close_path = pf_back_or(@person ? permission_forge.person_path(@person) : permission_forge.people_path) %>
|
|
7
|
+
<%= form_with url: permission_forge.assignments_path, method: :post, id: 'pf-assign-form' do %>
|
|
8
|
+
<input type="hidden" name="return_to" value="<%= params[:return_to] %>">
|
|
9
|
+
<% footer = capture do %>
|
|
10
|
+
<span class="pf-sheet__footer-note">
|
|
11
|
+
<% if errors.any? %>
|
|
12
|
+
<% errors.each_value do |msg| %><span class="pf-error" role="alert"><%= msg %></span> <% end %>
|
|
13
|
+
<% elsif @person && @chosen_sets.any? %>
|
|
14
|
+
<%= t('.summary', name: pf_person_name(@person), sets: @chosen_sets.map(&:name).to_sentence,
|
|
15
|
+
until: (@expiry == 'never' ? t('.forever') : t('.until', date: pf_expiry_preset_date(@expiry) || @custom_date))) %>
|
|
16
|
+
<% else %>
|
|
17
|
+
<%= t('.applies_now') %>
|
|
18
|
+
<% end %>
|
|
19
|
+
</span>
|
|
20
|
+
<%= link_to t('permission_forge.ui.cancel'), close_path, class: 'pf-btn', data: { pf_close: true } %>
|
|
21
|
+
<button type="submit" class="pf-btn pf-btn--primary">
|
|
22
|
+
<%= @chosen_sets.size > 1 ? t('.assign_many', count: @chosen_sets.size) : t('.assign') %>
|
|
23
|
+
</button>
|
|
24
|
+
<% end %>
|
|
25
|
+
|
|
26
|
+
<%= pf_sheet_frame({ title: title, close_path: close_path, footer: footer }) do %>
|
|
27
|
+
<%# 1. Person %>
|
|
28
|
+
<section class="pf-stack" aria-labelledby="pf-assign-person">
|
|
29
|
+
<%= pf_section_heading("1. #{t('.person')}", id: 'pf-assign-person') %>
|
|
30
|
+
<% if @person %>
|
|
31
|
+
<input type="hidden" name="user_id" value="<%= @person.id %>">
|
|
32
|
+
<div class="pf-picked">
|
|
33
|
+
<%= pf_person_cell(@person) %>
|
|
34
|
+
<button type="submit" class="pf-btn pf-btn--xs pf-ml-auto" formmethod="get" formaction="<%= permission_forge.new_assignment_path %>"
|
|
35
|
+
name="change_person" value="1"><%= t('.change') %></button>
|
|
36
|
+
</div>
|
|
37
|
+
<% else %>
|
|
38
|
+
<div class="pf-row pf-row--nowrap">
|
|
39
|
+
<label class="pf-sr-only" for="pf-assign-q"><%= t('permission_forge.ui.search_people') %></label>
|
|
40
|
+
<input id="pf-assign-q" class="pf-input" type="search" name="q" value="<%= @query %>" autocomplete="off" autofocus
|
|
41
|
+
placeholder="<%= t('permission_forge.ui.search_people') %>"
|
|
42
|
+
data-pf-live="pf-assign-people" data-pf-live-url="<%= permission_forge.new_assignment_path %>">
|
|
43
|
+
<button type="submit" class="pf-btn pf-btn--sm" formmethod="get" formaction="<%= permission_forge.new_assignment_path %>" name="search" value="1"><%= t('permission_forge.ui.search') %></button>
|
|
44
|
+
</div>
|
|
45
|
+
<div id="pf-assign-people">
|
|
46
|
+
<%= render 'person_options', page: @people, query: @query, field: 'user_id', type: 'radio', invalid: errors[:person] %>
|
|
47
|
+
</div>
|
|
48
|
+
<% end %>
|
|
49
|
+
</section>
|
|
50
|
+
|
|
51
|
+
<%# 2. Permission Sets %>
|
|
52
|
+
<section class="pf-stack" aria-labelledby="pf-assign-sets">
|
|
53
|
+
<%= pf_section_heading("2. #{t('permission_forge.nav.permission_sets')}", id: 'pf-assign-sets') %>
|
|
54
|
+
<div class="pf-row pf-row--nowrap">
|
|
55
|
+
<label class="pf-sr-only" for="pf-assign-set-q"><%= t('.filter_sets') %></label>
|
|
56
|
+
<input id="pf-assign-set-q" class="pf-input pf-input--sm" type="search" name="set_q" value="<%= @set_query %>"
|
|
57
|
+
placeholder="<%= t('.filter_sets') %>" data-pf-live="pf-assign-set-list" data-pf-live-url="<%= permission_forge.new_assignment_path %>">
|
|
58
|
+
</div>
|
|
59
|
+
<div id="pf-assign-set-list" class="pf-listbox">
|
|
60
|
+
<div class="pf-listbox__scroll" role="group" aria-label="<%= t('permission_forge.nav.permission_sets') %>">
|
|
61
|
+
<% if @sets.empty? %>
|
|
62
|
+
<p class="pf-option pf-text-3"><%= t('.no_sets') %></p>
|
|
63
|
+
<% end %>
|
|
64
|
+
<% @sets.each do |set| %>
|
|
65
|
+
<% has = @existing_ids.include?(set.id) %>
|
|
66
|
+
<label class="pf-option">
|
|
67
|
+
<input type="checkbox" name="set_ids[]" value="<%= set.id %>" <%= 'checked' if @selected_ids.include?(set.id) && !has %> <%= 'disabled' if has %>>
|
|
68
|
+
<span class="pf-option__main">
|
|
69
|
+
<span class="pf-row pf-row--tight">
|
|
70
|
+
<span class="pf-fw-600"><%= set.name %></span>
|
|
71
|
+
<% if has %><%= pf_badge(t('.already')) %><% end %>
|
|
72
|
+
<% if set.is_muting %><%= pf_badge("⊘ #{t('permission_forge.ui.badges.muting')}", variant: :muting) %><% end %>
|
|
73
|
+
<% unless set.is_active %><%= pf_off_badge %><% end %>
|
|
74
|
+
</span>
|
|
75
|
+
<% if set.description.present? %><span class="pf-fs-12 pf-text-3"><%= set.description %></span><% end %>
|
|
76
|
+
</span>
|
|
77
|
+
<span class="pf-option__aside"><%= pf_people_text(@people_counts.fetch(set.id, 0)) %></span>
|
|
78
|
+
</label>
|
|
79
|
+
<% end %>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<% muting = @chosen_sets.select(&:is_muting) %>
|
|
83
|
+
<% if muting.any? %>
|
|
84
|
+
<div class="pf-banner pf-banner--muting">
|
|
85
|
+
<span class="pf-banner__glyph" aria-hidden="true">⊘</span>
|
|
86
|
+
<div class="pf-banner__body"><span><%= t('.muting_notice', sets: muting.map(&:name).to_sentence, name: @person ? pf_person(@person).first_name : t('.this_person')) %></span></div>
|
|
87
|
+
</div>
|
|
88
|
+
<% end %>
|
|
89
|
+
</section>
|
|
90
|
+
|
|
91
|
+
<%# 3. Expiry %>
|
|
92
|
+
<section class="pf-stack" aria-labelledby="pf-assign-expiry">
|
|
93
|
+
<%= pf_section_heading("3. #{t('.expires')}", id: 'pf-assign-expiry') %>
|
|
94
|
+
<%= render 'permission_forge/shared/expiry_picker', selected: @expiry, custom_date: @custom_date, error: errors[:expiry] %>
|
|
95
|
+
</section>
|
|
96
|
+
|
|
97
|
+
<%= render 'permission_forge/shared/reason_field', value: params[:reason] %>
|
|
98
|
+
<% end %>
|
|
99
|
+
<% end %>
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<% content_for :pf_title, t('.title') %>
|
|
2
|
+
<% filters = @log.filters %>
|
|
3
|
+
<% value = ->(key) { pf_audit_filter_value(filters, key) } %>
|
|
4
|
+
<% invalid = ->(key) { @errors&.key?(key.to_sym) ? tag.attributes('aria-invalid': 'true') : '' } %>
|
|
5
|
+
<div class="pf-page-header">
|
|
6
|
+
<div class="pf-page-header__text">
|
|
7
|
+
<h1 class="pf-h1"><%= t('.title') %></h1>
|
|
8
|
+
<p class="pf-subtitle"><%= t('.subtitle') %></p>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="pf-actions">
|
|
11
|
+
<%= link_to t('.holdings'), permission_forge.holdings_audit_events_path(format: :csv), class: 'pf-btn', title: t('.holdings_title') %>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<% if (problem = PermissionForge::Admin::AuditLog.recording_problem) %>
|
|
16
|
+
<%= render 'permission_forge/shared/audit_off_note', problem: problem %>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<% if @errors %>
|
|
20
|
+
<div class="pf-banner pf-banner--block" role="alert" id="pf-audit-errors">
|
|
21
|
+
<span class="pf-banner__glyph" aria-hidden="true">⊘</span>
|
|
22
|
+
<div class="pf-banner__body">
|
|
23
|
+
<span class="pf-banner__title"><%= t('.invalid.title') %></span>
|
|
24
|
+
<ul class="pf-audit-errors">
|
|
25
|
+
<% @errors.each do |name, messages| %>
|
|
26
|
+
<li><%= pf_audit_filter_error(name, messages, value.(name.to_s)) %></li>
|
|
27
|
+
<% end %>
|
|
28
|
+
</ul>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<%= form_with url: permission_forge.audit_events_path, method: :get, class: 'pf-card pf-audit-filters', role: 'search',
|
|
34
|
+
'aria-label': t('.filters.label') do %>
|
|
35
|
+
<div class="pf-card__body pf-audit-filters__grid">
|
|
36
|
+
<p class="pf-hint pf-audit-filters__zone"><%= t('.filters.time_zone', zone: PermissionForge::Admin::AuditLogDays.zone_name) %></p>
|
|
37
|
+
<div class="pf-field">
|
|
38
|
+
<label class="pf-label" for="pf-audit-from"><%= t('.filters.from') %></label>
|
|
39
|
+
<input id="pf-audit-from" class="pf-input pf-input--sm" type="date" name="from" value="<%= value.('from') %>" <%= invalid.('from') %>>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="pf-field">
|
|
42
|
+
<label class="pf-label" for="pf-audit-to"><%= t('.filters.to') %></label>
|
|
43
|
+
<input id="pf-audit-to" class="pf-input pf-input--sm" type="date" name="to" value="<%= value.('to') %>" <%= invalid.('to') %>>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="pf-field">
|
|
46
|
+
<label class="pf-label" for="pf-audit-event"><%= t('.filters.event') %></label>
|
|
47
|
+
<select id="pf-audit-event" class="pf-select pf-select--sm" name="event" <%= invalid.('event') %>>
|
|
48
|
+
<option value=""><%= t('.filters.any_event') %></option>
|
|
49
|
+
<% PermissionForge::Admin::AuditLog.event_names_by_category.each do |category, names| %>
|
|
50
|
+
<optgroup label="<%= pf_audit_category_label(category) %>">
|
|
51
|
+
<% names.each do |name| %>
|
|
52
|
+
<option value="<%= name %>" <%= 'selected' if value.('event') == name %>><%= name %></option>
|
|
53
|
+
<% end %>
|
|
54
|
+
</optgroup>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% if value.('event').present? && PermissionForge::Admin::AuditLog.event_names_by_category.values.flatten.exclude?(value.('event')) %>
|
|
57
|
+
<option value="<%= value.('event') %>" selected><%= value.('event') %></option>
|
|
58
|
+
<% end %>
|
|
59
|
+
</select>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="pf-field">
|
|
62
|
+
<label class="pf-label" for="pf-audit-category"><%= t('.filters.category') %></label>
|
|
63
|
+
<select id="pf-audit-category" class="pf-select pf-select--sm" name="category" <%= invalid.('category') %>>
|
|
64
|
+
<option value=""><%= t('.filters.any_category') %></option>
|
|
65
|
+
<% PermissionForge::AuditEvent::CATEGORIES.each do |category| %>
|
|
66
|
+
<option value="<%= category %>" <%= 'selected' if value.('category') == category %>><%= pf_audit_category_label(category) %></option>
|
|
67
|
+
<% end %>
|
|
68
|
+
</select>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="pf-field">
|
|
71
|
+
<label class="pf-label" for="pf-audit-actor-type"><%= t('.filters.actor_type') %></label>
|
|
72
|
+
<select id="pf-audit-actor-type" class="pf-select pf-select--sm" name="actor_type" <%= invalid.('actor_type') %>>
|
|
73
|
+
<option value=""><%= t('.filters.any_actor_type') %></option>
|
|
74
|
+
<% PermissionForge::AuditEvent::ACTOR_TYPES.each do |type| %>
|
|
75
|
+
<option value="<%= type %>" <%= 'selected' if value.('actor_type') == type %>><%= pf_audit_actor_type_label(type) %></option>
|
|
76
|
+
<% end %>
|
|
77
|
+
</select>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="pf-field">
|
|
80
|
+
<label class="pf-label" for="pf-audit-actor"><%= t('.filters.actor_id') %></label>
|
|
81
|
+
<input id="pf-audit-actor" class="pf-input pf-input--sm" type="text" name="actor_id" value="<%= value.('actor_id') %>"
|
|
82
|
+
list="pf-audit-people" autocomplete="off" placeholder="<%= t('.filters.id_placeholder') %>" <%= invalid.('actor_id') %>>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="pf-field">
|
|
85
|
+
<label class="pf-label" for="pf-audit-person"><%= t('.filters.subject_user_id') %></label>
|
|
86
|
+
<input id="pf-audit-person" class="pf-input pf-input--sm" type="text" name="subject_user_id" value="<%= value.('subject_user_id') %>"
|
|
87
|
+
list="pf-audit-people" autocomplete="off" placeholder="<%= t('.filters.id_placeholder') %>" <%= invalid.('subject_user_id') %>>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="pf-field">
|
|
90
|
+
<label class="pf-label" for="pf-audit-set"><%= t('.filters.permission_set') %></label>
|
|
91
|
+
<% sets = PermissionForge::PermissionSet.order(:name).pluck(:id, :name) %>
|
|
92
|
+
<select id="pf-audit-set" class="pf-select pf-select--sm" name="permission_set" <%= invalid.('permission_set') %>>
|
|
93
|
+
<option value=""><%= t('.filters.any_set') %></option>
|
|
94
|
+
<% sets.each do |id, name| %>
|
|
95
|
+
<option value="<%= id %>" <%= 'selected' if value.('permission_set') == id.to_s %>><%= name %></option>
|
|
96
|
+
<% end %>
|
|
97
|
+
<% if value.('permission_set').present? && sets.none? { |id, _| id.to_s == value.('permission_set') } %>
|
|
98
|
+
<option value="<%= value.('permission_set') %>" selected><%= t('.filters.deleted_set', id: value.('permission_set')) %></option>
|
|
99
|
+
<% end %>
|
|
100
|
+
</select>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="pf-field">
|
|
103
|
+
<label class="pf-label" for="pf-audit-correlation"><%= t('.filters.correlation_id') %></label>
|
|
104
|
+
<input id="pf-audit-correlation" class="pf-input pf-input--sm" type="text" name="correlation_id" value="<%= value.('correlation_id') %>"
|
|
105
|
+
autocomplete="off" <%= invalid.('correlation_id') %>>
|
|
106
|
+
</div>
|
|
107
|
+
<% record_filter = %w[record_type record_id record_uuid].any? { |key| filters.key?(key) } %>
|
|
108
|
+
<% %w[record_type record_id record_uuid].each do |key| %>
|
|
109
|
+
<% if record_filter %>
|
|
110
|
+
<div class="pf-field">
|
|
111
|
+
<label class="pf-label" for="pf-audit-<%= key.dasherize %>"><%= t(".filters.#{key}") %></label>
|
|
112
|
+
<input id="pf-audit-<%= key.dasherize %>" class="pf-input pf-input--sm" type="text" name="<%= key %>" value="<%= value.(key) %>"
|
|
113
|
+
autocomplete="off" <%= invalid.(key) %>>
|
|
114
|
+
</div>
|
|
115
|
+
<% end %>
|
|
116
|
+
<% end %>
|
|
117
|
+
<% unless @errors %>
|
|
118
|
+
<datalist id="pf-audit-people">
|
|
119
|
+
<% PermissionForge::Admin::AuditLog.known_people.each do |id, label| %>
|
|
120
|
+
<option value="<%= id %>"><%= label.presence || t('permission_forge.audit_log.person_id', id: id) %></option>
|
|
121
|
+
<% end %>
|
|
122
|
+
</datalist>
|
|
123
|
+
<% end %>
|
|
124
|
+
<label class="pf-checkbox pf-audit-filters__views">
|
|
125
|
+
<input type="checkbox" name="views" value="1" <%= 'checked' if params[:views].to_s == '1' %>>
|
|
126
|
+
<span><%= t('.filters.views') %></span>
|
|
127
|
+
</label>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="pf-card__footer pf-card__footer--muted">
|
|
130
|
+
<button type="submit" class="pf-btn pf-btn--primary pf-btn--sm"><%= t('.filters.apply') %></button>
|
|
131
|
+
<% if @log.link_params.any? %>
|
|
132
|
+
<%= link_to t('.filters.clear'), permission_forge.audit_events_path, class: 'pf-btn pf-btn--sm' %>
|
|
133
|
+
<% end %>
|
|
134
|
+
<% unless @errors %>
|
|
135
|
+
<span class="pf-ml-auto pf-row pf-row--tight">
|
|
136
|
+
<span class="pf-fs-13 pf-text-2"><%= t('.export') %></span>
|
|
137
|
+
<%= link_to 'CSV', permission_forge.audit_events_path(filters.merge(format: :csv)), class: 'pf-btn pf-btn--sm', title: t('.export_title', kind: 'CSV') %>
|
|
138
|
+
<%= link_to 'JSON', permission_forge.audit_events_path(filters.merge(format: :json)), class: 'pf-btn pf-btn--sm', title: t('.export_title', kind: 'JSON') %>
|
|
139
|
+
</span>
|
|
140
|
+
<% end %>
|
|
141
|
+
</div>
|
|
142
|
+
<% end %>
|
|
143
|
+
|
|
144
|
+
<% if @page %>
|
|
145
|
+
<section class="pf-card" aria-labelledby="pf-audit-results">
|
|
146
|
+
<div class="pf-card__header">
|
|
147
|
+
<h2 class="pf-h2" id="pf-audit-results"><%= t('.results', count: @page.total) %></h2>
|
|
148
|
+
<span class="pf-card__desc"><%= t('.newest_first') %></span>
|
|
149
|
+
</div>
|
|
150
|
+
<% unless @log.include_views? %>
|
|
151
|
+
<p class="pf-card__row pf-fs-13 pf-text-2 pf-audit-views-note">
|
|
152
|
+
<%= t('.views_hidden') %>
|
|
153
|
+
<%= link_to t('.views_show'), permission_forge.audit_events_path(@log.link_params.merge('views' => '1')) %>
|
|
154
|
+
</p>
|
|
155
|
+
<% end %>
|
|
156
|
+
<%= render 'permission_forge/shared/audit_events', events: @page.events, lookups: @lookups,
|
|
157
|
+
empty: (filters.any? ? t('.empty_filtered') : t('permission_forge.audit_log.empty')) %>
|
|
158
|
+
<%= render 'permission_forge/shared/audit_pagination', page: @page,
|
|
159
|
+
path: ->(number) { permission_forge.audit_events_path(@log.link_params.merge(page: number)) } %>
|
|
160
|
+
</section>
|
|
161
|
+
<% end %>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<% d = @detail %>
|
|
2
|
+
<% event = d.event %>
|
|
3
|
+
<% lookups = d.lookups %>
|
|
4
|
+
<% content_for :pf_title, t('.title', event: event.event) %>
|
|
5
|
+
<%= pf_breadcrumb([t('permission_forge.audit_events.index.title'), permission_forge.audit_events_path], event.event) %>
|
|
6
|
+
|
|
7
|
+
<div class="pf-page-header">
|
|
8
|
+
<div class="pf-page-header__text">
|
|
9
|
+
<div class="pf-row pf-row--tight">
|
|
10
|
+
<%= pf_audit_category_badge(event.category) %>
|
|
11
|
+
<span class="pf-mono-key"><%= event.event %></span>
|
|
12
|
+
</div>
|
|
13
|
+
<h1 class="pf-h1"><%= pf_audit_sentence(event, lookups) %></h1>
|
|
14
|
+
<p class="pf-subtitle"><%= pf_datetime(event.occurred_at) %> · <time datetime="<%= event.occurred_at.utc.iso8601(6) %>"><%= event.occurred_at.utc.iso8601 %></time></p>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="pf-grid-2">
|
|
19
|
+
<section class="pf-card" aria-labelledby="pf-event-who">
|
|
20
|
+
<div class="pf-card__header"><h2 class="pf-h2" id="pf-event-who"><%= t('.who') %></h2></div>
|
|
21
|
+
<dl class="pf-card__body pf-kv">
|
|
22
|
+
<dt><%= t('.actor') %></dt>
|
|
23
|
+
<dd>
|
|
24
|
+
<span class="pf-fw-600"><%= pf_audit_actor_name(event) %></span>
|
|
25
|
+
<span class="pf-text-3">· <%= pf_audit_actor_type_label(event.actor_type) %><% if d.actor_id? %> · <%= t('.id', id: event.actor_id) %><% end %></span>
|
|
26
|
+
<div class="pf-row pf-row--tight pf-fs-12">
|
|
27
|
+
<%= link_to t('.all_by_actor'), pf_audit_log_path(actor_type: event.actor_type, actor_id: event.actor_id) %>
|
|
28
|
+
<% if d.actor_user %>· <%= link_to t('.open_person'), permission_forge.person_path(d.actor_user) %><% end %>
|
|
29
|
+
</div>
|
|
30
|
+
</dd>
|
|
31
|
+
<dt><%= t('.source') %></dt>
|
|
32
|
+
<dd><%= pf_audit_source_label(event.source) %> <span class="pf-mono-key"><%= event.source %></span></dd>
|
|
33
|
+
<dt><%= t('.reason') %></dt>
|
|
34
|
+
<dd><%= event.reason.presence || t('.no_reason') %></dd>
|
|
35
|
+
<dt><%= t('.request_id') %></dt>
|
|
36
|
+
<dd class="pf-audit-code"><%= event.request_id.presence || t('permission_forge.audit_log.no_value') %></dd>
|
|
37
|
+
<dt><%= t('.ip') %></dt>
|
|
38
|
+
<dd class="pf-audit-code"><%= event.ip.presence || t('permission_forge.audit_log.no_value') %></dd>
|
|
39
|
+
</dl>
|
|
40
|
+
</section>
|
|
41
|
+
|
|
42
|
+
<section class="pf-card" aria-labelledby="pf-event-what">
|
|
43
|
+
<div class="pf-card__header"><h2 class="pf-h2" id="pf-event-what"><%= t('.what') %></h2></div>
|
|
44
|
+
<dl class="pf-card__body pf-kv">
|
|
45
|
+
<dt><%= t('.record') %></dt>
|
|
46
|
+
<dd>
|
|
47
|
+
<% if d.record? %>
|
|
48
|
+
<span class="pf-fw-600"><%= pf_audit_record_name(event) %></span>
|
|
49
|
+
<div class="pf-fs-12 pf-text-3"><span class="pf-audit-code"><%= event.record_type %></span><% if d.record_id? %> · <%= t('.id', id: event.record_id) %><% end %></div>
|
|
50
|
+
<% if d.record_uuid? %><div class="pf-fs-12 pf-audit-code pf-text-3"><%= event.record_uuid %></div><% end %>
|
|
51
|
+
<% if d.record_id? %>
|
|
52
|
+
<div class="pf-fs-12"><%= link_to t('.all_for_record'), pf_audit_log_path(record_type: event.record_type, record_id: event.record_id) %></div>
|
|
53
|
+
<% end %>
|
|
54
|
+
<% else %>
|
|
55
|
+
<%= t('permission_forge.audit_log.no_value') %>
|
|
56
|
+
<% end %>
|
|
57
|
+
</dd>
|
|
58
|
+
<dt><%= t('.permission_set') %></dt>
|
|
59
|
+
<dd>
|
|
60
|
+
<% if d.permission_set? %>
|
|
61
|
+
<% if d.permission_set %><%= link_to pf_audit_set_name(event, lookups), permission_forge.permission_set_path(d.permission_set) %><% else %><%= pf_audit_set_name(event, lookups) %><% end %>
|
|
62
|
+
<div class="pf-fs-12"><%= link_to t('.all_for_set'), pf_audit_log_path(permission_set: d.permission_set_key) %></div>
|
|
63
|
+
<% else %>
|
|
64
|
+
<%= t('permission_forge.audit_log.no_value') %>
|
|
65
|
+
<% end %>
|
|
66
|
+
</dd>
|
|
67
|
+
<dt><%= t('.person') %></dt>
|
|
68
|
+
<dd>
|
|
69
|
+
<% if d.subject_user? %>
|
|
70
|
+
<% if d.person %><%= link_to pf_audit_person_name(event, lookups), permission_forge.person_path(d.person) %><% else %><%= pf_audit_person_name(event, lookups) %><% end %>
|
|
71
|
+
<span class="pf-text-3">· <%= t('.id', id: event.subject_user_id) %></span>
|
|
72
|
+
<div class="pf-fs-12"><%= link_to t('.all_for_person'), pf_audit_log_path(subject_user_id: event.subject_user_id) %></div>
|
|
73
|
+
<% else %>
|
|
74
|
+
<%= t('permission_forge.audit_log.no_value') %>
|
|
75
|
+
<% end %>
|
|
76
|
+
</dd>
|
|
77
|
+
<dt><%= t('.correlation') %></dt>
|
|
78
|
+
<dd>
|
|
79
|
+
<% if d.correlation? %>
|
|
80
|
+
<span class="pf-audit-code pf-fs-12"><%= event.correlation_id %></span>
|
|
81
|
+
<div class="pf-fs-12"><%= link_to t('.all_in_action'), pf_audit_log_path(correlation_id: event.correlation_id) %></div>
|
|
82
|
+
<% else %>
|
|
83
|
+
<%= t('permission_forge.audit_log.no_value') %>
|
|
84
|
+
<% end %>
|
|
85
|
+
</dd>
|
|
86
|
+
<dt><%= t('.uuid') %></dt>
|
|
87
|
+
<dd class="pf-audit-code pf-fs-12"><%= event.uuid %></dd>
|
|
88
|
+
</dl>
|
|
89
|
+
</section>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<% if d.changes? %>
|
|
93
|
+
<section class="pf-card pf-scroll-x" aria-labelledby="pf-event-changes">
|
|
94
|
+
<div class="pf-card__header">
|
|
95
|
+
<h2 class="pf-h2" id="pf-event-changes"><%= t('.changes') %></h2>
|
|
96
|
+
<span class="pf-card__desc"><%= t('.changes_desc', count: d.changes_count) %></span>
|
|
97
|
+
</div>
|
|
98
|
+
<table class="pf-audit-table">
|
|
99
|
+
<thead><tr><th scope="col"><%= t('.attribute') %></th><th scope="col"><%= t('.before') %></th><th scope="col"><%= t('.after') %></th></tr></thead>
|
|
100
|
+
<tbody>
|
|
101
|
+
<% d.change_rows.each do |attribute, from, to| %>
|
|
102
|
+
<tr>
|
|
103
|
+
<th scope="row" class="pf-audit-code"><%= attribute %></th>
|
|
104
|
+
<td class="pf-audit-table__before"><span class="pf-sr-only"><%= t('.before') %>: </span><%= pf_audit_value(from) %></td>
|
|
105
|
+
<td class="pf-audit-table__after"><span aria-hidden="true">→ </span><span class="pf-sr-only"><%= t('.after') %>: </span><%= pf_audit_value(to) %></td>
|
|
106
|
+
</tr>
|
|
107
|
+
<% end %>
|
|
108
|
+
</tbody>
|
|
109
|
+
</table>
|
|
110
|
+
</section>
|
|
111
|
+
<% end %>
|
|
112
|
+
|
|
113
|
+
<% if d.snapshot? %>
|
|
114
|
+
<section class="pf-card pf-scroll-x" aria-labelledby="pf-event-snapshot">
|
|
115
|
+
<div class="pf-card__header">
|
|
116
|
+
<h2 class="pf-h2" id="pf-event-snapshot"><%= t('.snapshot') %></h2>
|
|
117
|
+
<span class="pf-card__desc"><%= d.snapshot_of_removed? ? t('.snapshot_destroyed') : t('.snapshot_desc') %></span>
|
|
118
|
+
</div>
|
|
119
|
+
<table class="pf-audit-table">
|
|
120
|
+
<thead><tr><th scope="col"><%= t('.attribute') %></th><th scope="col"><%= t('.value') %></th></tr></thead>
|
|
121
|
+
<tbody>
|
|
122
|
+
<% d.snapshot_rows.each do |attribute, attribute_value| %>
|
|
123
|
+
<tr><th scope="row" class="pf-audit-code"><%= attribute %></th><td><%= pf_audit_value(attribute_value) %></td></tr>
|
|
124
|
+
<% end %>
|
|
125
|
+
</tbody>
|
|
126
|
+
</table>
|
|
127
|
+
</section>
|
|
128
|
+
<% end %>
|
|
129
|
+
|
|
130
|
+
<% if d.other? %>
|
|
131
|
+
<section class="pf-card pf-scroll-x" aria-labelledby="pf-event-data">
|
|
132
|
+
<div class="pf-card__header"><h2 class="pf-h2" id="pf-event-data"><%= t('.data') %></h2></div>
|
|
133
|
+
<table class="pf-audit-table">
|
|
134
|
+
<thead><tr><th scope="col"><%= t('.key') %></th><th scope="col"><%= t('.value') %></th></tr></thead>
|
|
135
|
+
<tbody>
|
|
136
|
+
<% d.other_rows.each do |key, data_value| %>
|
|
137
|
+
<tr><th scope="row" class="pf-audit-code"><%= key %></th><td class="pf-audit-code"><%= pf_audit_value(data_value) %></td></tr>
|
|
138
|
+
<% end %>
|
|
139
|
+
</tbody>
|
|
140
|
+
</table>
|
|
141
|
+
</section>
|
|
142
|
+
<% end %>
|
|
143
|
+
|
|
144
|
+
<% if d.no_data? %>
|
|
145
|
+
<p class="pf-fs-13 pf-text-3"><%= t('.no_data') %></p>
|
|
146
|
+
<% end %>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<% set = @permission_set %>
|
|
2
|
+
<% back = permission_forge.permission_set_path(set, tab: 'assignments') %>
|
|
3
|
+
<% content_for :pf_title, t('.title', count: @people.size, set: set.name) %>
|
|
4
|
+
<%= pf_dialog_frame({ title: t('.title', count: @people.size, set: set.name), danger: true,
|
|
5
|
+
breadcrumb: pf_breadcrumb([t('permission_forge.nav.permission_sets'), permission_forge.permission_sets_path], [set.name, back], t('.crumb')) }) do %>
|
|
6
|
+
<% if @people.empty? %>
|
|
7
|
+
<p class="pf-text-2"><%= t('.none') %></p>
|
|
8
|
+
<div class="pf-actions pf-row--right"><%= link_to t('permission_forge.ui.close'), back, class: 'pf-btn', data: { pf_close: true } %></div>
|
|
9
|
+
<% else %>
|
|
10
|
+
<p class="pf-text-2"><%= t('.body') %></p>
|
|
11
|
+
<ul class="pf-confirm__list"><% @people.each do |u| %><li><%= pf_person_name(u) %></li><% end %></ul>
|
|
12
|
+
<%= form_with url: permission_forge.revoke_permission_set_bulk_assignment_path(set), method: :delete, class: 'pf-confirm-form' do %>
|
|
13
|
+
<%= render 'permission_forge/shared/reason_field' %>
|
|
14
|
+
<div class="pf-actions pf-row--right">
|
|
15
|
+
<% @people.each do |u| %><input type="hidden" name="user_ids[]" value="<%= u.id %>"><% end %>
|
|
16
|
+
<%= link_to t('permission_forge.ui.cancel'), back, class: 'pf-btn', data: { pf_close: true } %>
|
|
17
|
+
<button type="submit" class="pf-btn pf-btn--danger-solid"><%= t('.confirm', count: @people.size) %></button>
|
|
18
|
+
</div>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|