permission_forge 0.14.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2372 -0
- data/README.md +76 -25
- data/app/controllers/concerns/permission_forge/api/crud_actions.rb +3 -2
- data/app/controllers/concerns/permission_forge/api/input_shapes.rb +200 -0
- data/app/controllers/concerns/permission_forge/api/pagination.rb +12 -5
- data/app/controllers/concerns/permission_forge/api/response_renderer.rb +37 -1
- data/app/controllers/concerns/permission_forge/api_authentication.rb +13 -2
- data/app/controllers/concerns/permission_forge/cache_confirmation.rb +42 -11
- data/app/controllers/concerns/permission_forge/change_review.rb +42 -0
- data/app/controllers/concerns/permission_forge/copy_refusal.rb +39 -0
- data/app/controllers/concerns/permission_forge/param_shapes.rb +207 -7
- data/app/controllers/concerns/permission_forge/save_notice.rb +69 -0
- data/app/controllers/permission_forge/api/audit_events_controller.rb +1 -1
- data/app/controllers/permission_forge/api/base_controller.rb +163 -12
- data/app/controllers/permission_forge/api/dashboard_controller.rb +29 -4
- data/app/controllers/permission_forge/api/permission_set_group_users_controller.rb +5 -2
- data/app/controllers/permission_forge/api/permission_set_groups_controller.rb +5 -4
- data/app/controllers/permission_forge/api/permission_set_rules_controller.rb +7 -5
- data/app/controllers/permission_forge/api/permission_set_users_controller.rb +80 -19
- data/app/controllers/permission_forge/api/permission_sets_controller.rb +12 -6
- data/app/controllers/permission_forge/api/resource_actions_controller.rb +1 -1
- data/app/controllers/permission_forge/api/resource_fields_controller.rb +3 -4
- data/app/controllers/permission_forge/api/resources_controller.rb +26 -12
- data/app/controllers/permission_forge/api/unmatched_routes_controller.rb +20 -0
- data/app/controllers/permission_forge/application_controller.rb +39 -5
- data/app/controllers/permission_forge/assignments_controller.rb +36 -34
- data/app/controllers/permission_forge/bulk_assignments_controller.rb +8 -4
- data/app/controllers/permission_forge/field_lookups_controller.rb +1 -1
- data/app/controllers/permission_forge/maintenance_controller.rb +6 -2
- data/app/controllers/permission_forge/people_controller.rb +14 -14
- data/app/controllers/permission_forge/permission_set_editor.rb +20 -6
- data/app/controllers/permission_forge/permission_set_group_users_controller.rb +3 -1
- data/app/controllers/permission_forge/permission_set_groups_controller.rb +23 -9
- data/app/controllers/permission_forge/permission_set_rules_controller.rb +20 -10
- data/app/controllers/permission_forge/permission_sets_controller.rb +23 -24
- data/app/controllers/permission_forge/resource_actions_controller.rb +11 -6
- data/app/controllers/permission_forge/resource_fields_controller.rb +37 -19
- data/app/controllers/permission_forge/resources_controller.rb +12 -11
- data/app/frontend/permission_forge/pf_admin.css +99 -17
- data/app/frontend/permission_forge/pf_admin.js +36 -0
- data/app/helpers/permission_forge/application_helper.rb +22 -72
- data/app/helpers/permission_forge/audit_attribution_helper.rb +77 -0
- data/app/helpers/permission_forge/audit_helper.rb +57 -119
- data/app/helpers/permission_forge/audit_records_helper.rb +103 -0
- data/app/helpers/permission_forge/audit_sentences_helper.rb +56 -2
- data/app/helpers/permission_forge/badges_helper.rb +74 -17
- data/app/helpers/permission_forge/changes_helper.rb +24 -6
- data/app/helpers/permission_forge/conditions_helper.rb +119 -0
- data/app/helpers/permission_forge/labels_helper.rb +134 -0
- data/app/helpers/permission_forge/people_helper.rb +11 -19
- data/app/helpers/permission_forge/rule_problems_helper.rb +73 -0
- data/app/helpers/permission_forge/sentences_helper.rb +119 -98
- data/app/helpers/permission_forge/settings_helper.rb +2 -2
- data/app/helpers/permission_forge/ui_helper.rb +65 -4
- data/app/models/permission_forge/audit_event.rb +5 -1
- data/app/models/permission_forge/permission_set.rb +208 -18
- data/app/models/permission_forge/permission_set_group.rb +10 -4
- data/app/models/permission_forge/permission_set_group_user.rb +3 -1
- data/app/models/permission_forge/permission_set_rule.rb +254 -17
- data/app/models/permission_forge/permission_set_user.rb +38 -3
- data/app/models/permission_forge/resource.rb +18 -8
- data/app/models/permission_forge/resource_action.rb +15 -6
- data/app/models/permission_forge/resource_field.rb +97 -11
- data/app/views/permission_forge/assignments/confirm_destroy.html.erb +4 -4
- data/app/views/permission_forge/assignments/new.html.erb +10 -6
- data/app/views/permission_forge/audit_events/index.html.erb +1 -1
- data/app/views/permission_forge/audit_events/show.html.erb +16 -9
- data/app/views/permission_forge/bulk_assignments/confirm_revoke.html.erb +3 -3
- data/app/views/permission_forge/bulk_assignments/new.html.erb +3 -3
- data/app/views/permission_forge/field_lookups/show.html.erb +2 -2
- data/app/views/permission_forge/home/_broken_rules_card.html.erb +35 -0
- data/app/views/permission_forge/home/_conditions_card.html.erb +28 -0
- data/app/views/permission_forge/home/_ending_card.html.erb +11 -5
- data/app/views/permission_forge/home/_expired_card.html.erb +1 -1
- data/app/views/permission_forge/home/_mask_strategies_card.html.erb +27 -0
- data/app/views/permission_forge/home/_privacy_card.html.erb +21 -7
- data/app/views/permission_forge/home/_rules_card.html.erb +1 -1
- data/app/views/permission_forge/home/_secret_rules_card.html.erb +33 -0
- data/app/views/permission_forge/home/_stale_card.html.erb +7 -5
- data/app/views/permission_forge/home/_tidy_card.html.erb +1 -1
- data/app/views/permission_forge/home/index.html.erb +5 -6
- data/app/views/permission_forge/people/_access_tab.html.erb +26 -19
- data/app/views/permission_forge/people/_audit_tab.html.erb +8 -8
- data/app/views/permission_forge/people/_effective_field_source.html.erb +13 -7
- data/app/views/permission_forge/people/_effective_tab.html.erb +16 -15
- data/app/views/permission_forge/people/_effective_why.html.erb +20 -16
- data/app/views/permission_forge/people/_why_item.html.erb +1 -1
- data/app/views/permission_forge/people/confirm_remove_all.html.erb +3 -3
- data/app/views/permission_forge/people/erasure_preview.html.erb +1 -1
- data/app/views/permission_forge/permission_set_groups/confirm_destroy.html.erb +3 -3
- data/app/views/permission_forge/permission_set_groups/index.html.erb +3 -3
- data/app/views/permission_forge/permission_set_groups/show.html.erb +28 -19
- data/app/views/permission_forge/permission_set_rules/_builder.html.erb +35 -15
- data/app/views/permission_forge/permission_set_rules/_preview.html.erb +17 -0
- data/app/views/permission_forge/permission_set_rules/confirm_destroy.html.erb +3 -3
- data/app/views/permission_forge/permission_set_rules/preview.html.erb +12 -0
- data/app/views/permission_forge/permission_sets/_assignments_tab.html.erb +37 -22
- data/app/views/permission_forge/permission_sets/_audit_tab.html.erb +4 -4
- data/app/views/permission_forge/permission_sets/_condition_problems.html.erb +15 -0
- data/app/views/permission_forge/permission_sets/_conditions_tab.html.erb +14 -8
- data/app/views/permission_forge/permission_sets/_header.html.erb +6 -5
- data/app/views/permission_forge/permission_sets/_records_summary.html.erb +2 -2
- data/app/views/permission_forge/permission_sets/_resources_tab.html.erb +4 -4
- data/app/views/permission_forge/permission_sets/confirm_destroy.html.erb +4 -4
- data/app/views/permission_forge/permission_sets/confirm_switch_mode.html.erb +3 -3
- data/app/views/permission_forge/permission_sets/index.html.erb +6 -6
- data/app/views/permission_forge/permission_sets/new.html.erb +2 -2
- data/app/views/permission_forge/permission_sets/review.html.erb +13 -3
- data/app/views/permission_forge/permission_sets/show.html.erb +7 -5
- data/app/views/permission_forge/resource_actions/_across_sets.html.erb +7 -10
- data/app/views/permission_forge/resource_actions/_effect.html.erb +5 -5
- data/app/views/permission_forge/resource_actions/_form.html.erb +1 -1
- data/app/views/permission_forge/resource_actions/_managed_note.html.erb +5 -3
- data/app/views/permission_forge/resource_actions/confirm_destroy.html.erb +1 -1
- data/app/views/permission_forge/resource_actions/new.html.erb +1 -1
- data/app/views/permission_forge/resource_actions/show.html.erb +2 -2
- data/app/views/permission_forge/resource_fields/_explain.html.erb +19 -15
- data/app/views/permission_forge/resource_fields/show.html.erb +21 -12
- data/app/views/permission_forge/resources/_actions_tab.html.erb +4 -4
- data/app/views/permission_forge/resources/_fields_tab.html.erb +17 -9
- data/app/views/permission_forge/resources/_rules_tab.html.erb +9 -6
- data/app/views/permission_forge/resources/_sync.html.erb +8 -5
- data/app/views/permission_forge/resources/confirm_destroy.html.erb +3 -3
- data/app/views/permission_forge/resources/new.html.erb +3 -2
- data/app/views/permission_forge/resources/show.html.erb +6 -6
- data/app/views/permission_forge/settings/_row.html.erb +2 -2
- data/app/views/permission_forge/settings/show.html.erb +10 -1
- data/app/views/permission_forge/shared/_across_sets.html.erb +8 -9
- data/app/views/permission_forge/shared/_audit_events.html.erb +2 -2
- data/app/views/permission_forge/shared/_audit_footer.html.erb +13 -2
- data/app/views/permission_forge/shared/_crud_cell.html.erb +6 -6
- data/app/views/permission_forge/shared/_mode_banners.html.erb +1 -1
- data/app/views/permission_forge/shared/_savebar.html.erb +4 -2
- data/config/locales/permission_forge.en.yml +480 -69
- data/config/locales/permission_forge.mk.yml +499 -79
- data/config/locales/permission_forge.sq.yml +509 -80
- data/config/routes.rb +38 -23
- data/lib/generators/permission_forge/install/install_generator.rb +21 -0
- data/lib/generators/permission_forge/install/templates/initializer.rb.tt +35 -34
- data/lib/generators/permission_forge/install/templates/migration.rb.tt +5 -5
- data/lib/permission_forge/activation_conditions.rb +200 -0
- data/lib/permission_forge/admin/action_label.rb +33 -0
- data/lib/permission_forge/admin/action_rows.rb +3 -1
- data/lib/permission_forge/admin/actor_label.rb +52 -0
- data/lib/permission_forge/admin/attention_items.rb +152 -31
- data/lib/permission_forge/admin/audit_log.rb +4 -7
- data/lib/permission_forge/admin/audit_trail.rb +33 -17
- data/lib/permission_forge/admin/bulk_people_matcher.rb +32 -14
- data/lib/permission_forge/admin/chain_entries.rb +42 -0
- data/lib/permission_forge/admin/change_set.rb +12 -1
- data/lib/permission_forge/admin/change_set_parts/base.rb +5 -4
- data/lib/permission_forge/admin/change_set_parts/context.rb +3 -1
- data/lib/permission_forge/admin/change_set_parts/field_edit.rb +4 -1
- data/lib/permission_forge/admin/change_set_parts/field_levels.rb +1 -1
- data/lib/permission_forge/admin/change_set_parts/new_actions.rb +1 -1
- data/lib/permission_forge/admin/change_set_parts/new_fields.rb +38 -11
- data/lib/permission_forge/admin/change_set_parts/resource_edit.rb +17 -1
- data/lib/permission_forge/admin/change_set_parts/resource_flags.rb +1 -1
- data/lib/permission_forge/admin/change_set_parts/set_attrs.rb +4 -0
- data/lib/permission_forge/admin/change_set_parts/values.rb +10 -4
- data/lib/permission_forge/admin/column_fields.rb +22 -21
- data/lib/permission_forge/admin/conditions_form.rb +47 -15
- data/lib/permission_forge/admin/controller_catalog.rb +42 -14
- data/lib/permission_forge/admin/editable_lock.rb +83 -3
- data/lib/permission_forge/admin/effective_access.rb +147 -48
- data/lib/permission_forge/admin/effective_fields.rb +75 -67
- data/lib/permission_forge/admin/error_sentence.rb +47 -0
- data/lib/permission_forge/admin/expiry_presets.rb +17 -0
- data/lib/permission_forge/admin/exports/holdings.rb +6 -3
- data/lib/permission_forge/admin/field_across_sets.rb +7 -4
- data/lib/permission_forge/admin/field_chain.rb +109 -0
- data/lib/permission_forge/admin/field_detail.rb +24 -11
- data/lib/permission_forge/admin/field_label.rb +109 -0
- data/lib/permission_forge/admin/field_level.rb +4 -68
- data/lib/permission_forge/admin/field_provenance.rb +132 -0
- data/lib/permission_forge/admin/field_rows.rb +26 -11
- data/lib/permission_forge/admin/last_changes.rb +14 -0
- data/lib/permission_forge/admin/list_sentence.rb +32 -0
- data/lib/permission_forge/admin/mask_strategy_choices.rb +57 -0
- data/lib/permission_forge/admin/model_catalog.rb +83 -39
- data/lib/permission_forge/admin/model_label.rb +66 -0
- data/lib/permission_forge/admin/people_candidates.rb +21 -9
- data/lib/permission_forge/admin/people_directory.rb +23 -5
- data/lib/permission_forge/admin/permission_set_factory.rb +6 -15
- data/lib/permission_forge/admin/person_label.rb +105 -16
- data/lib/permission_forge/admin/prefill.rb +92 -0
- data/lib/permission_forge/admin/record_name.rb +45 -0
- data/lib/permission_forge/admin/rule_builder.rb +77 -3
- data/lib/permission_forge/admin/rule_form.rb +44 -10
- data/lib/permission_forge/admin/rule_form_input.rb +27 -11
- data/lib/permission_forge/admin/rule_notes.rb +43 -0
- data/lib/permission_forge/admin/rule_preview.rb +96 -0
- data/lib/permission_forge/admin/sensitive_columns.rb +10 -62
- data/lib/permission_forge/admin/settings_catalog.rb +64 -12
- data/lib/permission_forge/admin/state_label.rb +97 -0
- data/lib/permission_forge/admin_method_override.rb +44 -0
- data/lib/permission_forge/api/schema.rb +322 -0
- data/lib/permission_forge/api/serializers/base_serializer.rb +87 -1
- data/lib/permission_forge/api/serializers/permission_set_group_user_serializer.rb +1 -1
- data/lib/permission_forge/api/serializers/permission_set_rule_serializer.rb +1 -1
- data/lib/permission_forge/api/serializers/permission_set_serializer.rb +5 -2
- data/lib/permission_forge/api/serializers/permission_set_user_serializer.rb +1 -1
- data/lib/permission_forge/api/serializers/resource_action_serializer.rb +1 -1
- data/lib/permission_forge/api/serializers/resource_field_serializer.rb +1 -1
- data/lib/permission_forge/api/serializers/resource_serializer.rb +1 -1
- data/lib/permission_forge/api_authorizations.rb +59 -0
- data/lib/permission_forge/api_only_admin.rb +81 -0
- data/lib/permission_forge/audit/actor_codes.rb +32 -0
- data/lib/permission_forge/audit/actor_resolver.rb +1 -2
- data/lib/permission_forge/audit/boot_warnings.rb +29 -9
- data/lib/permission_forge/audit/event_catalog.rb +16 -6
- data/lib/permission_forge/audit/event_query.rb +3 -3
- data/lib/permission_forge/audit/reason_codes.rb +25 -0
- data/lib/permission_forge/audit/runtime.rb +9 -2
- data/lib/permission_forge/audit/settings_observer.rb +2 -2
- data/lib/permission_forge/audit/unmasked_without_user.rb +169 -0
- data/lib/permission_forge/audit.rb +24 -10
- data/lib/permission_forge/boot_checks.rb +180 -63
- data/lib/permission_forge/cache/base.rb +10 -0
- data/lib/permission_forge/cache/null_cache.rb +5 -0
- data/lib/permission_forge/cache/outcome.rb +119 -0
- data/lib/permission_forge/cache/rails_cache.rb +6 -0
- data/lib/permission_forge/cache/report.rb +150 -0
- data/lib/permission_forge/cache/user_keys.rb +8 -4
- data/lib/permission_forge/cache_warnings.rb +140 -0
- data/lib/permission_forge/concerns/auto_permission_scoping.rb +19 -2
- data/lib/permission_forge/concerns/has_tenant_scoping.rb +1 -1
- data/lib/permission_forge/concerns/has_uuid.rb +1 -1
- data/lib/permission_forge/concerns/keeps_copied_values.rb +48 -0
- data/lib/permission_forge/concerns/masked_attributes/alias_access.rb +12 -2
- data/lib/permission_forge/concerns/masked_attributes/virtual_readers.rb +6 -4
- data/lib/permission_forge/concerns/masked_attributes.rb +150 -34
- data/lib/permission_forge/concerns/normalizes_label.rb +78 -0
- data/lib/permission_forge/concerns/permission_cache_accessor.rb +3 -3
- data/lib/permission_forge/concerns/permission_scoped_findable.rb +121 -29
- data/lib/permission_forge/concerns/permissioned_user.rb +1 -1
- data/lib/permission_forge/concerns/records_audit_events.rb +13 -6
- data/lib/permission_forge/concerns/requires_name.rb +37 -0
- data/lib/permission_forge/concerns/validates_column_lengths.rb +24 -0
- data/lib/permission_forge/concerns/validates_dependency.rb +5 -2
- data/lib/permission_forge/concerns/validates_required_columns.rb +74 -0
- data/lib/permission_forge/conditional_sets.rb +13 -6
- data/lib/permission_forge/configuration/admin_ui_settings.rb +18 -4
- data/lib/permission_forge/configuration/audit_settings.rb +2 -1
- data/lib/permission_forge/configuration/controller_action_settings.rb +5 -4
- data/lib/permission_forge/configuration/masked_attributes_settings.rb +73 -19
- data/lib/permission_forge/configuration/presets.rb +20 -11
- data/lib/permission_forge/configuration/query_scoping_settings.rb +55 -0
- data/lib/permission_forge/configuration/strategy_values.rb +2 -1
- data/lib/permission_forge/configuration.rb +41 -18
- data/lib/permission_forge/controller_actions/managed_index.rb +8 -7
- data/lib/permission_forge/database_reach.rb +93 -0
- data/lib/permission_forge/deprecation.rb +1 -1
- data/lib/permission_forge/docs/api_samples.rb +250 -0
- data/lib/permission_forge/docs/configuration_reference.rb +172 -0
- data/lib/permission_forge/encrypted_attributes.rb +150 -0
- data/lib/permission_forge/engine.rb +46 -14
- data/lib/permission_forge/execution_state/restore.rb +74 -0
- data/lib/permission_forge/execution_state.rb +8 -1
- data/lib/permission_forge/field_level.rb +123 -0
- data/lib/permission_forge/field_names.rb +145 -0
- data/lib/permission_forge/field_variants.rb +96 -0
- data/lib/permission_forge/generated_text.rb +95 -0
- data/lib/permission_forge/inspection_filters.rb +83 -0
- data/lib/permission_forge/managed_fields.rb +66 -0
- data/lib/permission_forge/masked_models.rb +25 -19
- data/lib/permission_forge/masking_sync.rb +100 -25
- data/lib/permission_forge/model_bindings.rb +30 -19
- data/lib/permission_forge/model_lookup.rb +42 -0
- data/lib/permission_forge/operators/encrypted_alias.rb +67 -0
- data/lib/permission_forge/operators/registry.rb +6 -0
- data/lib/permission_forge/operators/rule_column.rb +242 -27
- data/lib/permission_forge/operators/rule_path.rb +179 -0
- data/lib/permission_forge/operators/rule_problem.rb +309 -0
- data/lib/permission_forge/operators/rule_sources.rb +96 -0
- data/lib/permission_forge/operators/value_casters/postgresql_types.rb +237 -0
- data/lib/permission_forge/operators/value_casters/temporal.rb +9 -20
- data/lib/permission_forge/operators/value_casters.rb +61 -13
- data/lib/permission_forge/operators/value_typing.rb +83 -7
- data/lib/permission_forge/query_scoping_warnings.rb +22 -17
- data/lib/permission_forge/rake_audit.rb +5 -4
- data/lib/permission_forge/rake_dry_run.rb +32 -0
- data/lib/permission_forge/rake_output.rb +189 -0
- data/lib/permission_forge/record_read_access.rb +51 -0
- data/lib/permission_forge/resource_chain.rb +14 -2
- data/lib/permission_forge/schema_read/ordered_types.rb +72 -0
- data/lib/permission_forge/schema_read.rb +335 -0
- data/lib/permission_forge/secret_columns.rb +49 -0
- data/lib/permission_forge/sensitive_columns.rb +114 -0
- data/lib/permission_forge/services/attribute_access_guard.rb +22 -14
- data/lib/permission_forge/services/bulk_permission_set_assignment.rb +16 -8
- data/lib/permission_forge/services/concerns/clonable.rb +9 -0
- data/lib/permission_forge/services/concerns/settles_cache.rb +58 -0
- data/lib/permission_forge/services/consent_withdrawal_handler.rb +37 -19
- data/lib/permission_forge/services/data_subject_erasure.rb +83 -45
- data/lib/permission_forge/services/expired_assignment_cleaner.rb +62 -25
- data/lib/permission_forge/services/extra_field_defaults.rb +1 -1
- data/lib/permission_forge/services/field_permission_merger.rb +44 -17
- data/lib/permission_forge/services/filters_response_attributes.rb +20 -5
- data/lib/permission_forge/services/gdpr_field_auditor/text_report.rb +33 -5
- data/lib/permission_forge/services/gdpr_field_auditor.rb +68 -48
- data/lib/permission_forge/services/host_key_guard.rb +8 -5
- data/lib/permission_forge/services/mask_value.rb +72 -11
- data/lib/permission_forge/services/permission_cache_clearer.rb +36 -15
- data/lib/permission_forge/services/permission_hash_builder.rb +48 -19
- data/lib/permission_forge/services/permission_rule_applier.rb +131 -68
- data/lib/permission_forge/services/permission_set_cloner.rb +32 -8
- data/lib/permission_forge/services/permission_set_condition_evaluator.rb +38 -36
- data/lib/permission_forge/services/permission_set_group_cloner.rb +2 -1
- data/lib/permission_forge/services/permits_attributes/result.rb +31 -0
- data/lib/permission_forge/services/permits_attributes.rb +48 -27
- data/lib/permission_forge/services/resource_field_generator/row_builder.rb +96 -0
- data/lib/permission_forge/services/resource_field_generator.rb +71 -42
- data/lib/permission_forge/services/resource_with_fields_builder.rb +2 -1
- data/lib/permission_forge/services/rls_operator_sql.rb +83 -24
- data/lib/permission_forge/services/rls_policy_generator.rb +83 -39
- data/lib/permission_forge/services/rls_read_access_sql.rb +131 -0
- data/lib/permission_forge/services/security_audit/action_check.rb +49 -5
- data/lib/permission_forge/services/security_audit/assignment_check.rb +2 -1
- data/lib/permission_forge/services/security_audit/audit_trail_check.rb +1 -3
- data/lib/permission_forge/services/security_audit/conditions_check.rb +92 -0
- data/lib/permission_forge/services/security_audit/config_check.rb +41 -18
- data/lib/permission_forge/services/security_audit/integrity_check.rb +71 -4
- data/lib/permission_forge/services/security_audit/masking_without_user.rb +66 -0
- data/lib/permission_forge/services/security_audit/personal_data_check.rb +7 -11
- data/lib/permission_forge/services/security_audit/rule_check.rb +180 -50
- data/lib/permission_forge/services/security_audit/text_report.rb +14 -2
- data/lib/permission_forge/services/security_audit/unmanaged_actions.rb +145 -0
- data/lib/permission_forge/services/security_audit/user_model_check.rb +4 -3
- data/lib/permission_forge/services/security_audit.rb +16 -4
- data/lib/permission_forge/services/stale_assignment_auditor.rb +8 -5
- data/lib/permission_forge/timestamp_fields.rb +60 -0
- data/lib/permission_forge/unique_conflict.rb +117 -0
- data/lib/permission_forge/version.rb +1 -1
- data/lib/permission_forge/virtual_fields/discovery.rb +2 -1
- data/lib/permission_forge/virtual_fields.rb +15 -5
- data/lib/permission_forge/warnings.rb +263 -0
- data/lib/permission_forge/web_request.rb +248 -0
- data/lib/permission_forge.rb +54 -0
- data/lib/tasks/permission_forge.rake +26 -6
- data/lib/tasks/permission_forge_audit_events.rake +8 -4
- data/lib/tasks/permission_forge_cache.rake +12 -12
- data/lib/tasks/permission_forge_docs.rake +48 -0
- data/lib/tasks/permission_forge_expiration.rake +11 -5
- data/lib/tasks/permission_forge_gdpr.rake +36 -20
- metadata +84 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module PermissionForge
|
|
4
4
|
module Api
|
|
5
5
|
class DashboardController < BaseController
|
|
6
|
-
before_action :
|
|
6
|
+
before_action :authorize_stats!, only: :show
|
|
7
7
|
|
|
8
8
|
def show
|
|
9
9
|
render json: {
|
|
@@ -20,10 +20,35 @@ module PermissionForge
|
|
|
20
20
|
|
|
21
21
|
private
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
# While config.api_stats_require_admin is on (the default), only a
|
|
24
|
+
# caller config.api_stats_authorization allows reads the stats; a
|
|
25
|
+
# refusal is a 403 recorded as api.auth_failed (stats_not_authorized).
|
|
26
|
+
# Without the lambda the 403 names it, as for the other lambdas.
|
|
27
|
+
def authorize_stats!
|
|
28
|
+
configuration = PermissionForge.configuration
|
|
29
|
+
return unless configuration.api_stats_require_admin
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
authorizer = configuration.api_stats_authorization
|
|
32
|
+
return if authorizer ? authorizer.call(self) : legacy_admin_flag?
|
|
33
|
+
|
|
34
|
+
pf_audit_refused('api.auth_failed', data: { 'reason' => 'stats_not_authorized',
|
|
35
|
+
'hook' => authorizer ? 'api_stats_authorization' : 'api_stats_require_admin' })
|
|
36
|
+
render_not_authorized(authorizer ? nil : I18n.t('permission_forge.api.errors.stats_authorization_missing'))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Through 0.15 the stats endpoint read an @admin = true that api_auth
|
|
40
|
+
# set on the controller (documented in 0.14). Still honoured when
|
|
41
|
+
# api_stats_authorization is not set, with a deprecation warning;
|
|
42
|
+
# removed in 0.16.
|
|
43
|
+
def legacy_admin_flag?
|
|
44
|
+
return false unless @admin
|
|
45
|
+
|
|
46
|
+
PermissionForge::Deprecation.warn(
|
|
47
|
+
'Setting @admin on the API controller (in api_auth) to allow GET /api/stats is deprecated; ' \
|
|
48
|
+
'set config.api_stats_authorization = ->(controller) { ... } instead',
|
|
49
|
+
key: :api_stats_admin_ivar
|
|
50
|
+
)
|
|
51
|
+
true
|
|
27
52
|
end
|
|
28
53
|
end
|
|
29
54
|
end
|
|
@@ -15,14 +15,17 @@ module PermissionForge
|
|
|
15
15
|
render_collection(scope, serializer_for(:permission_set_group_user))
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# A user_id that is missing, not an id, or names nobody
|
|
19
|
+
# config.user_list_resolver lists is a 422 keyed by user_id
|
|
20
|
+
# (BaseController#requested_user), after the authorization hooks.
|
|
18
21
|
def create
|
|
19
22
|
user_id = params[:user_id]
|
|
20
23
|
authorize_assignment!(@group, [user_id])
|
|
21
24
|
enforce_editable!(@group) unless performed?
|
|
22
25
|
return if performed?
|
|
23
26
|
|
|
24
|
-
user =
|
|
25
|
-
return if
|
|
27
|
+
user = requested_user
|
|
28
|
+
return if pf_assignment_refused_api?(@group, user.id, :add_member)
|
|
26
29
|
|
|
27
30
|
gu = @group.permission_set_group_users.build(user: user)
|
|
28
31
|
save_and_render(gu, :permission_set_group_user)
|
|
@@ -49,10 +49,11 @@ module PermissionForge
|
|
|
49
49
|
authorize_action!(:clone, @group)
|
|
50
50
|
return if performed?
|
|
51
51
|
|
|
52
|
-
# As the admin's Duplicate: the copy of the group and of its sets is
|
|
52
|
+
# As the admin's Duplicate: the copy of the group and of its sets is
|
|
53
|
+
# editable, and named "<name> (copy)" in the request's locale.
|
|
54
|
+
name = pf_copy_name.presence || I18n.t('permission_forge.permission_sets.copy_name', name: @group.name)
|
|
53
55
|
cloned = PermissionForge::Services::PermissionSetGroupCloner.call(
|
|
54
|
-
permission_set_group: @group, attrs: { name:
|
|
55
|
-
unlock: true
|
|
56
|
+
permission_set_group: @group, attrs: { name: name, is_editable: true }, unlock: true
|
|
56
57
|
)
|
|
57
58
|
render_created(cloned, serializer_for(:permission_set_group))
|
|
58
59
|
end
|
|
@@ -64,7 +65,7 @@ module PermissionForge
|
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
def group_params
|
|
67
|
-
|
|
68
|
+
pf_record_attributes(:permission_set_group, PermissionForge::PermissionSetGroup, :name, :description, :is_active, :is_editable)
|
|
68
69
|
end
|
|
69
70
|
end
|
|
70
71
|
end
|
|
@@ -25,12 +25,14 @@ module PermissionForge
|
|
|
25
25
|
@rule = find_nested_record(@resource.permission_set_rules)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# A list for field_value under in / not_in / between is joined with
|
|
29
|
+
# commas; the stored rule's operator decides on an update that sends
|
|
30
|
+
# none (Api::InputShapes).
|
|
28
31
|
def record_params
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
32
|
+
pf_record_attributes(:permission_set_rule, PermissionForge::PermissionSetRule,
|
|
33
|
+
:name, :field_name, :field_value, :operator,
|
|
34
|
+
:join_type, :field_accessor_path, :include_nulls,
|
|
35
|
+
:is_active, :is_editable, :failure_message, record: current_record)
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
end
|
|
@@ -19,13 +19,16 @@ module PermissionForge
|
|
|
19
19
|
render_collection(scope, serializer_for(:permission_set_user))
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# A user_id that is missing, not an id, or names nobody
|
|
23
|
+
# config.user_list_resolver lists is a 422 keyed by user_id
|
|
24
|
+
# (BaseController#requested_user), after the authorization hooks.
|
|
22
25
|
def create
|
|
23
26
|
authorize_assignment!(@permission_set, [params[:user_id]])
|
|
24
27
|
reject_widening!(:grant) unless performed?
|
|
25
28
|
return if performed?
|
|
26
29
|
|
|
27
|
-
user =
|
|
28
|
-
return if
|
|
30
|
+
user = requested_user
|
|
31
|
+
return if pf_assignment_refused_api?(@permission_set, user.id, :grant)
|
|
29
32
|
|
|
30
33
|
create_assignment(user)
|
|
31
34
|
end
|
|
@@ -41,20 +44,20 @@ module PermissionForge
|
|
|
41
44
|
reject_widening!(:revoke) unless performed?
|
|
42
45
|
return if performed? || pf_assignment_refused_api?(@permission_set, psu.user_id, :revoke)
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
# The revoked assignment and what the cache store confirmed
|
|
48
|
+
# (meta.cache, as the bulk endpoint answers it); through 0.14.x a 204.
|
|
49
|
+
outcome = PermissionForge::Cache::Report.of { psu.destroy! }
|
|
50
|
+
render_record_with_cache(psu, outcome, status: :ok)
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
def bulk
|
|
49
54
|
authorize_assignment!(@permission_set, params[:user_ids])
|
|
50
55
|
# On a locked set only what narrows access passes (an unknown
|
|
51
56
|
# bulk_action is judged as a grant, then refused as a 422).
|
|
52
|
-
reject_widening!(BULK_ACTIONS.fetch(
|
|
57
|
+
reject_widening!(BULK_ACTIONS.fetch(bulk_action_param, :grant)) unless performed?
|
|
53
58
|
return if performed? || bulk_refused?
|
|
54
59
|
|
|
55
60
|
render json: { data: bulk_assignment }, status: :ok
|
|
56
|
-
rescue ArgumentError => e
|
|
57
|
-
render json: { error: { status: 422, messages: e.message } }, status: :unprocessable_entity
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
private
|
|
@@ -65,34 +68,92 @@ module PermissionForge
|
|
|
65
68
|
reject_locked!(pf_widening_lock(@permission_set, change))
|
|
66
69
|
end
|
|
67
70
|
|
|
71
|
+
# The new assignment and what the cache store confirmed (meta.cache).
|
|
68
72
|
def create_assignment(user)
|
|
69
|
-
psu = @permission_set.permission_set_users.build(user: user, expires_at:
|
|
70
|
-
|
|
73
|
+
psu = @permission_set.permission_set_users.build(user: user, expires_at: future_expires_at)
|
|
74
|
+
saved = false
|
|
75
|
+
outcome = PermissionForge::Cache::Report.of { saved = psu.save }
|
|
76
|
+
return render_validation_errors(psu) unless saved
|
|
77
|
+
|
|
78
|
+
render_record_with_cache(psu, outcome, status: :created)
|
|
71
79
|
end
|
|
72
80
|
|
|
73
|
-
#
|
|
74
|
-
#
|
|
81
|
+
# params[:expires_at] (ParamShapes#pf_time_param) for a new
|
|
82
|
+
# assignment. An end that is not after now would create an assignment
|
|
83
|
+
# that grants nothing: refused (422) before anybody is assigned. The
|
|
84
|
+
# admin's Undo restores a removed assignment with the end it had, also
|
|
85
|
+
# one that has passed, and does not come through here.
|
|
86
|
+
def future_expires_at
|
|
87
|
+
expires_at = pf_time_param(:expires_at)
|
|
88
|
+
return expires_at if expires_at.nil? || expires_at > Time.current
|
|
89
|
+
|
|
90
|
+
raise ParamShapes::InvalidParam.new(:expires_at, I18n.t('permission_forge.api.errors.expires_at_past'))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# An unknown bulk_action, and a request that names nobody, are a 422
|
|
94
|
+
# ({ "bulk_action": [...] }, { "user_ids": [...] }) before anything is
|
|
95
|
+
# changed: a 200 that did nothing would hide a broken payload.
|
|
75
96
|
def bulk_refused?
|
|
76
|
-
action = BULK_ACTIONS.fetch(
|
|
77
|
-
|
|
78
|
-
raise ArgumentError, "Invalid action: #{name}" # a service action this controller does not map
|
|
97
|
+
action = BULK_ACTIONS.fetch(bulk_action_param) do
|
|
98
|
+
raise ParamShapes::InvalidParam.new(:bulk_action, I18n.t('permission_forge.api.errors.bulk_action_invalid'))
|
|
79
99
|
end
|
|
100
|
+
require_user_ids!
|
|
80
101
|
pf_assignment_refused_api?(@permission_set, params[:user_ids], action)
|
|
81
102
|
end
|
|
82
103
|
|
|
104
|
+
# params[:bulk_action] when it is text ('' otherwise: a list or an
|
|
105
|
+
# object is no action, never its #to_s).
|
|
106
|
+
def bulk_action_param
|
|
107
|
+
action = params[:bulk_action]
|
|
108
|
+
action.is_a?(String) ? action : ''
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# user_ids missing, [], "" or only blanks: "must name at least one
|
|
112
|
+
# user". Anything else that is not a list of ids — one string such as
|
|
113
|
+
# "1,3", a number, an object, a list holding an object, a list, a
|
|
114
|
+
# fraction or a boolean —: "must be a list" — through 0.14.1 "1,3" was
|
|
115
|
+
# read as one id and answered 200 with nobody assigned, and an element
|
|
116
|
+
# that was no id was left out without a word. A blank string beside an
|
|
117
|
+
# id is ignored, as Rails' own `collection_singular_ids=` ignores it
|
|
118
|
+
# (`collection_check_boxes` sends one with every form); Ids.cast leaves
|
|
119
|
+
# it out. Ids that name nobody are not refused: they are reported as
|
|
120
|
+
# `skipped`. (A JSON null among them never gets here: Rails' deep_munge
|
|
121
|
+
# removes it from the parameters.)
|
|
122
|
+
def require_user_ids!
|
|
123
|
+
user_ids = params[:user_ids]
|
|
124
|
+
message = if Array.wrap(user_ids).all?(&:blank?) then I18n.t('permission_forge.api.errors.user_ids_missing')
|
|
125
|
+
elsif !user_id_list?(user_ids) then I18n.t('permission_forge.api.errors.user_ids_not_list')
|
|
126
|
+
end
|
|
127
|
+
raise ParamShapes::InvalidParam.new(:user_ids, message) if message
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def user_id_list?(user_ids)
|
|
131
|
+
user_ids.is_a?(Array) && user_ids.all? { |id| id.is_a?(String) || id.is_a?(Integer) }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# The service's counts and its cache outcome (`cache`: `confirmed`,
|
|
135
|
+
# `process_local`, `unconfirmed` — Cache::Outcome, as every
|
|
136
|
+
# cache-touching operation answers it) plus `skipped`: the ids of the
|
|
137
|
+
# request that name nobody config.user_list_resolver lists. A revoke
|
|
138
|
+
# ignores expires_at (it removes the assignments whatever their end),
|
|
139
|
+
# also one that cannot be read.
|
|
83
140
|
def bulk_assignment
|
|
84
|
-
|
|
141
|
+
user_ids = bulk_user_ids
|
|
142
|
+
result = PermissionForge::Services::BulkPermissionSetAssignment.call(
|
|
85
143
|
permission_set: @permission_set,
|
|
86
|
-
user_ids:
|
|
87
|
-
action:
|
|
144
|
+
user_ids: user_ids,
|
|
145
|
+
action: bulk_action_param,
|
|
88
146
|
created_by: PermissionForge.current_user,
|
|
89
|
-
expires_at:
|
|
147
|
+
expires_at: (future_expires_at if bulk_action_param == 'assign')
|
|
90
148
|
)
|
|
149
|
+
# Someone outside user_list_resolver and an id that names no user
|
|
150
|
+
# read the same: the answer says nothing about who exists.
|
|
151
|
+
result.merge(skipped: PermissionForge::Admin::PeopleDirectory::Ids.unmatched(params[:user_ids], user_ids))
|
|
91
152
|
end
|
|
92
153
|
|
|
93
154
|
# The people of the request within config.user_list_resolver
|
|
94
155
|
# (Admin::PeopleDirectory#find_all); an id naming nobody there is
|
|
95
|
-
# skipped
|
|
156
|
+
# skipped (and reported).
|
|
96
157
|
def bulk_user_ids
|
|
97
158
|
PermissionForge::Admin::PeopleDirectory.new.find_all(params[:user_ids]).map(&:id)
|
|
98
159
|
end
|
|
@@ -45,8 +45,10 @@ module PermissionForge
|
|
|
45
45
|
return if performed?
|
|
46
46
|
|
|
47
47
|
# As the admin's Duplicate: an editable copy, outside a locked group.
|
|
48
|
+
# Names are unique per tenant: the default one is the admin's
|
|
49
|
+
# "<name> (copy)" in the request's locale, numbered when taken.
|
|
48
50
|
cloned = PermissionForge::Admin::PermissionSetFactory.duplicate(
|
|
49
|
-
@permission_set, name:
|
|
51
|
+
@permission_set, name: pf_copy_name.presence || PermissionForge::PermissionSet.copy_name(@permission_set.name)
|
|
50
52
|
)
|
|
51
53
|
render_created(cloned, serializer_for(:permission_set))
|
|
52
54
|
end
|
|
@@ -75,12 +77,16 @@ module PermissionForge
|
|
|
75
77
|
@permission_set = find_record(PermissionForge::PermissionSet)
|
|
76
78
|
end
|
|
77
79
|
|
|
80
|
+
# The activation conditions take their shapes from Api::InputShapes
|
|
81
|
+
# (allowed_ips a String or a list of Strings, an hour digits, a flag
|
|
82
|
+
# text; null or "" clears); permission_set_group_id takes the group's
|
|
83
|
+
# id or, under api_record_identifier = :uuid, its uuid
|
|
84
|
+
# (BaseController#with_reference_id).
|
|
78
85
|
def permission_set_params
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
86
|
+
attrs = pf_record_attributes(:permission_set, PermissionForge::PermissionSet,
|
|
87
|
+
:name, :description, :is_active, :is_editable, :is_muting, :permission_set_group_id,
|
|
88
|
+
:requires_session_flag, :allowed_hours_start, :allowed_hours_end, :allowed_ips)
|
|
89
|
+
with_reference_id(attrs, :permission_set_group_id, PermissionForge::PermissionSetGroup)
|
|
84
90
|
end
|
|
85
91
|
end
|
|
86
92
|
end
|
|
@@ -20,7 +20,7 @@ module PermissionForge
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def record_params
|
|
23
|
-
|
|
23
|
+
pf_record_attributes(:resource_action, PermissionForge::ResourceAction, :name, :label, :description, :is_permitted, :is_active)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# A create or a rename must also name a valid action key. Checked after
|
|
@@ -20,10 +20,9 @@ module PermissionForge
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def record_params
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
23
|
+
pf_record_attributes(:resource_field, PermissionForge::ResourceField,
|
|
24
|
+
:name, :label, :description, :is_active, :is_editable,
|
|
25
|
+
:read_access, :edit_access, :mask_access, :mask_strategy)
|
|
27
26
|
end
|
|
28
27
|
end
|
|
29
28
|
end
|
|
@@ -8,26 +8,34 @@ module PermissionForge
|
|
|
8
8
|
before_action :set_permission_set
|
|
9
9
|
before_action :set_resource, only: %i[show update destroy generate_fields]
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
11
|
+
# Creates a field row for every unconfigured column: with grant_all,
|
|
12
|
+
# readable and editable, unmasked; without it, no access (under
|
|
13
|
+
# gdpr_defaults, personal-data columns readable and masked). It passes
|
|
14
|
+
# the same gates as the admin UI's "Sync fields": the authorization
|
|
15
|
+
# hook, then the lock on the resource and its set. grant_all is read
|
|
16
|
+
# by ParamShapes#pf_boolean_param (true, "true", 1, "1"; absent, false,
|
|
17
|
+
# "false", 0, "0"): anything else is a 422 keyed by grant_all, never
|
|
18
|
+
# fields without edit. A Resource named after no model this app can
|
|
19
|
+
# load (PermissionForge::ModelLookup: `Kernel`, a model without its
|
|
20
|
+
# table) has no columns: a 422 keyed by name.
|
|
14
21
|
def generate_fields
|
|
15
22
|
guard_mutation!(:generate_fields, @resource)
|
|
23
|
+
refuse_no_model unless performed?
|
|
16
24
|
return if performed?
|
|
17
25
|
|
|
18
26
|
fields = PermissionForge::Services::ResourceFieldGenerator.call(
|
|
19
27
|
resource: @resource,
|
|
20
|
-
grant_all:
|
|
28
|
+
grant_all: pf_boolean_param(:grant_all)
|
|
21
29
|
)
|
|
22
30
|
data = PermissionForge::Api::Serializers::ResourceFieldSerializer.collection(fields)
|
|
23
31
|
render json: { data: data }, status: :created
|
|
32
|
+
rescue ParamShapes::InvalidParam
|
|
33
|
+
raise # rendered by BaseController like every input error
|
|
24
34
|
rescue StandardError => e
|
|
25
35
|
# Log the internal detail; return a generic message so model/column names
|
|
26
36
|
# and adapter errors are not leaked to API clients.
|
|
27
37
|
Rails.logger.error("[PermissionForge] generate_fields failed for #{@resource&.name}: #{e.class}: #{e.message}")
|
|
28
|
-
|
|
29
|
-
error: { status: 422, messages: I18n.t('permission_forge.api.errors.field_generation_failed') }
|
|
30
|
-
}, status: :unprocessable_entity
|
|
38
|
+
render_unprocessable('base' => [I18n.t('permission_forge.api.errors.field_generation_failed')])
|
|
31
39
|
end
|
|
32
40
|
|
|
33
41
|
private
|
|
@@ -36,6 +44,13 @@ module PermissionForge
|
|
|
36
44
|
def current_record = @resource
|
|
37
45
|
def serializer_type = :resource
|
|
38
46
|
|
|
47
|
+
# 422 on name for a Resource named after no model this app can load.
|
|
48
|
+
def refuse_no_model
|
|
49
|
+
return if PermissionForge::ModelLookup.model?(@resource.name)
|
|
50
|
+
|
|
51
|
+
render_unprocessable('name' => [I18n.t('permission_forge.api.errors.resource_names_no_model')])
|
|
52
|
+
end
|
|
53
|
+
|
|
39
54
|
def set_permission_set
|
|
40
55
|
@permission_set = find_record(PermissionForge::PermissionSet, :permission_set_identifier)
|
|
41
56
|
end
|
|
@@ -45,11 +60,10 @@ module PermissionForge
|
|
|
45
60
|
end
|
|
46
61
|
|
|
47
62
|
def record_params
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
63
|
+
pf_record_attributes(:resource, PermissionForge::Resource,
|
|
64
|
+
:name, :description, :is_active, :is_editable,
|
|
65
|
+
:read_access, :create_access, :edit_access, :delete_access,
|
|
66
|
+
:view_all, :modify_all)
|
|
53
67
|
end
|
|
54
68
|
end
|
|
55
69
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PermissionForge
|
|
4
|
+
module Api
|
|
5
|
+
# Any path and method under the API mount that no endpoint answers
|
|
6
|
+
# (`GET /api/unknown`, `POST /api/audit_events`): a 404 in the API's JSON
|
|
7
|
+
# envelope instead of Rails' HTML page. Drawn after every API route, and
|
|
8
|
+
# only under /api. It runs the API's guards first: while api_enabled is
|
|
9
|
+
# off it is the same bodiless 404 as every API route; a caller api_auth
|
|
10
|
+
# refuses gets the 401 and a request with no tenant the 403, each
|
|
11
|
+
# recorded as api.auth_failed like on any endpoint.
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
class UnmatchedRoutesController < BaseController
|
|
15
|
+
def show
|
|
16
|
+
render_not_found(I18n.t('permission_forge.api.errors.route_not_found'))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -26,6 +26,7 @@ module PermissionForge
|
|
|
26
26
|
include PermissionForge::TenantRequired
|
|
27
27
|
include PermissionForge::ParamShapes
|
|
28
28
|
include PermissionForge::HtmlFormat
|
|
29
|
+
include PermissionForge::SaveNotice
|
|
29
30
|
|
|
30
31
|
THEMES = %w[auto light dark].freeze
|
|
31
32
|
FRAGMENT_HEADER = 'X-PF-Fragment'.freeze
|
|
@@ -38,19 +39,56 @@ module PermissionForge
|
|
|
38
39
|
# inside the scope suspension (an actor resolver may query).
|
|
39
40
|
around_action :pf_audit_request
|
|
40
41
|
|
|
42
|
+
# The uniqueness race (two requests passed the uniqueness validation
|
|
43
|
+
# together; the unique index refused this one's INSERT): back to the
|
|
44
|
+
# page the form was on (the admin's home without a Referer, never
|
|
45
|
+
# another host) with the validation's message for the index the error
|
|
46
|
+
# names (UniqueConflict), or the generic one. 303: the browser follows
|
|
47
|
+
# with a GET whatever the form's method was. Through 0.14.1 a 500.
|
|
48
|
+
rescue_from ActiveRecord::RecordNotUnique do |error|
|
|
49
|
+
detail = PermissionForge::UniqueConflict.sentence(error)
|
|
50
|
+
alert = detail ? t('permission_forge.flash.record_not_unique_detail', detail: detail) : t('permission_forge.flash.record_not_unique')
|
|
51
|
+
redirect_back_or_to permission_forge.root_path, alert: alert, allow_other_host: false, status: :see_other
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# A NOT NULL column no validation foresaw (ValidatesRequiredColumns
|
|
55
|
+
# covers the gem's own columns): back to the page like the uniqueness
|
|
56
|
+
# race, never a 500.
|
|
57
|
+
rescue_from ActiveRecord::NotNullViolation do
|
|
58
|
+
redirect_back_or_to permission_forge.root_path, alert: t('permission_forge.flash.not_null'), allow_other_host: false,
|
|
59
|
+
status: :see_other
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A flag the admin cannot read (ParamShapes::InvalidFlag: not true or
|
|
63
|
+
# false, where the forms post "0" and "1"): back to the page like the
|
|
64
|
+
# uniqueness race, saying which flag, nothing changed. ActiveModel's
|
|
65
|
+
# cast read any other word as true; this is never that, nor a 500. (Any
|
|
66
|
+
# other InvalidParam, a date the admin cannot read, stays a 400.)
|
|
67
|
+
rescue_from PermissionForge::ParamShapes::InvalidFlag do |error|
|
|
68
|
+
redirect_back_or_to permission_forge.root_path,
|
|
69
|
+
alert: t('permission_forge.flash.invalid_param', param: error.param, detail: error.detail),
|
|
70
|
+
allow_other_host: false, status: :see_other
|
|
71
|
+
end
|
|
72
|
+
|
|
41
73
|
helper PermissionForge::ApplicationHelper unless self < PermissionForge::ApplicationHelper
|
|
42
74
|
helper_method :pf_fragment?, :pf_theme, :pf_help_visible?, :pf_current_admin,
|
|
43
75
|
:pf_attention, :pf_settings_catalog, :pf_back_or
|
|
44
76
|
|
|
45
77
|
private
|
|
46
78
|
|
|
79
|
+
# A Permission Set's, group's or rule's name for a flash (Admin::RecordName:
|
|
80
|
+
# an install-default English name in the request's language).
|
|
81
|
+
def pf_record_name(record)
|
|
82
|
+
PermissionForge::Admin::RecordName.for(record)
|
|
83
|
+
end
|
|
84
|
+
|
|
47
85
|
# Memoized per request: drives the Home badge and the home page.
|
|
48
86
|
def pf_attention
|
|
49
87
|
@pf_attention ||= PermissionForge::Admin::AttentionItems.new
|
|
50
88
|
end
|
|
51
89
|
|
|
52
90
|
def pf_settings_catalog
|
|
53
|
-
@pf_settings_catalog ||= PermissionForge::Admin::SettingsCatalog.new
|
|
91
|
+
@pf_settings_catalog ||= PermissionForge::Admin::SettingsCatalog.new(attention: pf_attention)
|
|
54
92
|
end
|
|
55
93
|
|
|
56
94
|
def set_locale
|
|
@@ -178,10 +216,6 @@ module PermissionForge
|
|
|
178
216
|
raise ActionController::RoutingError, 'Not Found'
|
|
179
217
|
end
|
|
180
218
|
|
|
181
|
-
def pf_locked?(record)
|
|
182
|
-
PermissionForge::Admin::EditableLock.locked?(record)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
219
|
# Redirects with an alert when the record (or any of `records`), or what
|
|
186
220
|
# it belongs to (Admin::EditableLock: its Resource, Permission Set, the
|
|
187
221
|
# Group of a membership), is locked, and records the refusal
|