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
|
@@ -9,6 +9,7 @@ module PermissionForge
|
|
|
9
9
|
class AssignmentsController < ApplicationController
|
|
10
10
|
include PermissionForge::AssignmentAuthorization
|
|
11
11
|
include PermissionForge::ActingAdmin
|
|
12
|
+
include PermissionForge::CacheConfirmation
|
|
12
13
|
|
|
13
14
|
# What config.admin_assignment_authorization is asked for each member action.
|
|
14
15
|
CHANGE_ACTIONS = { update: :change_expiry, destroy: :revoke, keep: :review }.freeze
|
|
@@ -30,21 +31,24 @@ module PermissionForge
|
|
|
30
31
|
back = pf_back_or(permission_forge.person_path(@person))
|
|
31
32
|
return if pf_assignment_refused?(@chosen_sets, @person.id, :grant, back)
|
|
32
33
|
|
|
33
|
-
created =
|
|
34
|
-
|
|
34
|
+
created = nil
|
|
35
|
+
outcome = cache_outcome { created = assign_chosen_sets }
|
|
36
|
+
pf_redirect back, undo: assign_undo(created, back), **cache_flash_text(outcome, assigned_notice(created))
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# Change the end date (presets) or extend by 30 days. On a locked
|
|
38
40
|
# Permission Set only a change that narrows access passes
|
|
39
41
|
# (EditableLock.widening_lock): for a granting set an end date set or
|
|
40
42
|
# moved earlier, for a Muting Permission Set one cleared or moved later.
|
|
43
|
+
# Nothing chosen (the select's own first entry), or the end date the
|
|
44
|
+
# assignment has, changes nothing and says so (SaveNotice).
|
|
41
45
|
def update
|
|
42
46
|
expires_at = new_expiry(params[:expiry].to_s)
|
|
43
|
-
return redirect_to(return_path) if expires_at == :invalid
|
|
47
|
+
return redirect_to(return_path, **pf_save_flash(false)) if expires_at == :invalid
|
|
44
48
|
return if widening_refused?(expires_at)
|
|
45
49
|
|
|
46
|
-
@assignment
|
|
47
|
-
redirect_to return_path,
|
|
50
|
+
outcome = cache_outcome { pf_save_changed(@assignment, { expires_at: expires_at }, &:save!) }
|
|
51
|
+
redirect_to return_path, **pf_save_flash(@assignment) { cache_flash_text(outcome, end_changed_notice(expires_at)) }
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
def confirm_destroy
|
|
@@ -56,19 +60,19 @@ module PermissionForge
|
|
|
56
60
|
# is allowed (EditableLock.widening_lock).
|
|
57
61
|
def destroy
|
|
58
62
|
name = person_name(@assignment.user)
|
|
59
|
-
set_name = @assignment.permission_set
|
|
63
|
+
set_name = pf_record_name(@assignment.permission_set)
|
|
60
64
|
undo = restore_undo
|
|
61
|
-
@assignment.destroy!
|
|
62
|
-
pf_redirect return_path,
|
|
65
|
+
outcome = cache_outcome { @assignment.destroy! }
|
|
66
|
+
pf_redirect return_path, undo: undo, **cache_flash_text(outcome, t('permission_forge.flash.assignments.removed', name: name, set: set_name))
|
|
63
67
|
end
|
|
64
68
|
|
|
65
69
|
# Access review "Keep": records the reviewer and the date
|
|
66
70
|
# (PermissionSetUser#mark_reviewed!, one assignment.reviewed event);
|
|
67
71
|
# staleness is measured from the last review.
|
|
68
72
|
def keep
|
|
69
|
-
@assignment.mark_reviewed!(reviewer: pf_acting_admin_record)
|
|
70
|
-
redirect_to return_path,
|
|
71
|
-
|
|
73
|
+
outcome = cache_outcome { @assignment.mark_reviewed!(reviewer: pf_acting_admin_record) }
|
|
74
|
+
redirect_to return_path, **cache_flash_text(outcome, t('permission_forge.flash.assignments.kept',
|
|
75
|
+
name: person_name(@assignment.user), set: pf_record_name(@assignment.permission_set)))
|
|
72
76
|
end
|
|
73
77
|
|
|
74
78
|
# Undo for "Remove": recreates the assignment.
|
|
@@ -77,8 +81,9 @@ module PermissionForge
|
|
|
77
81
|
set, user = restore_target
|
|
78
82
|
return if pf_widening_refused?(pf_widening_lock(set, :grant), return_path) || pf_assignment_refused?(set, user.id, :grant, return_path)
|
|
79
83
|
|
|
80
|
-
restore_assignment(set, user, expires_at)
|
|
81
|
-
redirect_to return_path,
|
|
84
|
+
outcome = cache_outcome { restore_assignment(set, user, expires_at) }
|
|
85
|
+
redirect_to return_path,
|
|
86
|
+
**cache_flash_text(outcome, t('permission_forge.flash.assignments.restored', name: person_name(user), set: pf_record_name(set)))
|
|
82
87
|
end
|
|
83
88
|
|
|
84
89
|
# Undo for "Assign": removes the assignments just created (narrows
|
|
@@ -88,8 +93,8 @@ module PermissionForge
|
|
|
88
93
|
assignments = undo_assignments
|
|
89
94
|
return if undo_refused?(assignments)
|
|
90
95
|
|
|
91
|
-
PermissionForge::Audit.correlate { assignments.each(&:destroy!) }
|
|
92
|
-
redirect_to return_path,
|
|
96
|
+
outcome = cache_outcome { PermissionForge::Audit.correlate { assignments.each(&:destroy!) } }
|
|
97
|
+
redirect_to return_path, **cache_flash_text(outcome, t('permission_forge.flash.assignments.undone'))
|
|
93
98
|
end
|
|
94
99
|
|
|
95
100
|
private
|
|
@@ -104,8 +109,9 @@ module PermissionForge
|
|
|
104
109
|
def restore_undo
|
|
105
110
|
return if pf_widening_lock(@assignment.permission_set, :grant)
|
|
106
111
|
|
|
112
|
+
# Microseconds: end_of_day is 23:59:59.999999, and IsoTime reads up to 9 fraction digits.
|
|
107
113
|
restore = { 'permission_set_id' => @assignment.permission_set_id, 'user_id' => @assignment.user_id,
|
|
108
|
-
'expires_at' => @assignment.expires_at&.iso8601, 'return_to' => return_path }
|
|
114
|
+
'expires_at' => @assignment.expires_at&.iso8601(6), 'return_to' => return_path }
|
|
109
115
|
{ path: permission_forge.restore_assignments_path, method: 'post', params: restore }
|
|
110
116
|
end
|
|
111
117
|
|
|
@@ -173,7 +179,9 @@ module PermissionForge
|
|
|
173
179
|
load_sets
|
|
174
180
|
@people_counts = PermissionForge::PermissionSetUser.active_assignments.group(:permission_set_id).count
|
|
175
181
|
pf_load_expiry
|
|
176
|
-
@chosen_sets = @sets.select
|
|
182
|
+
@chosen_sets = @sets.select do |set|
|
|
183
|
+
@selected_ids.include?(set.id) && !@existing_ids.include?(set.id) && !@locked_ids.include?(set.id)
|
|
184
|
+
end
|
|
177
185
|
end
|
|
178
186
|
|
|
179
187
|
def load_person
|
|
@@ -192,22 +200,24 @@ module PermissionForge
|
|
|
192
200
|
@set_query = pf_text_param(:set_q)
|
|
193
201
|
@preset_set = PermissionForge::PermissionSet.find_by(id: params[:permission_set_id])
|
|
194
202
|
@selected_ids = (pf_id_list(:set_ids) + [@preset_set&.id]).compact.uniq
|
|
195
|
-
@sets =
|
|
203
|
+
@sets = listed_sets
|
|
204
|
+
@locked_ids = @sets.select { |set| pf_widening_lock(set, :grant) }.to_set(&:id)
|
|
196
205
|
end
|
|
197
206
|
|
|
198
207
|
def picker_limit
|
|
199
208
|
@person ? 0 : 8
|
|
200
209
|
end
|
|
201
210
|
|
|
202
|
-
#
|
|
203
|
-
# on a locked set
|
|
204
|
-
|
|
211
|
+
# Every set matching the filter. Those a holder cannot be added to (that
|
|
212
|
+
# widens access on a locked set; a locked Muting Permission Set takes
|
|
213
|
+
# holders) are listed as locked (@locked_ids) and never chosen.
|
|
214
|
+
def listed_sets
|
|
205
215
|
scope = PermissionForge::PermissionSet.order(:is_muting, :name)
|
|
206
216
|
unless @set_query.empty?
|
|
207
217
|
needle = "%#{ActiveRecord::Base.sanitize_sql_like(@set_query.downcase)}%"
|
|
208
218
|
scope = scope.where('LOWER(name) LIKE ?', needle).or(scope.where(id: @selected_ids))
|
|
209
219
|
end
|
|
210
|
-
scope.
|
|
220
|
+
scope.to_a
|
|
211
221
|
end
|
|
212
222
|
|
|
213
223
|
def validate_form
|
|
@@ -226,7 +236,7 @@ module PermissionForge
|
|
|
226
236
|
|
|
227
237
|
def end_changed_notice(expires_at)
|
|
228
238
|
t('permission_forge.flash.assignments.end_changed', name: person_name(@assignment.user),
|
|
229
|
-
set: @assignment.permission_set
|
|
239
|
+
set: pf_record_name(@assignment.permission_set),
|
|
230
240
|
when: helpers.pf_expiry(expires_at).first)
|
|
231
241
|
end
|
|
232
242
|
|
|
@@ -235,7 +245,7 @@ module PermissionForge
|
|
|
235
245
|
def restored_expiry = pf_time_param(:expires_at)
|
|
236
246
|
|
|
237
247
|
def assigned_notice(created)
|
|
238
|
-
sets = created.map { |assignment| assignment.permission_set
|
|
248
|
+
sets = helpers.pf_to_sentence(created.map { |assignment| pf_record_name(assignment.permission_set) })
|
|
239
249
|
return t('permission_forge.flash.assignments.assigned', name: person_name(@person), sets: sets) if @expires_at.nil?
|
|
240
250
|
|
|
241
251
|
t('permission_forge.flash.assignments.assigned_until', name: person_name(@person), sets: sets,
|
|
@@ -244,15 +254,7 @@ module PermissionForge
|
|
|
244
254
|
|
|
245
255
|
# :invalid for a blank or unknown key (CHANGE_KEYS has no blank entry).
|
|
246
256
|
def new_expiry(key)
|
|
247
|
-
|
|
248
|
-
return :invalid unless PermissionForge::Admin::ExpiryPresets::CHANGE_KEYS.include?(key)
|
|
249
|
-
|
|
250
|
-
PermissionForge::Admin::ExpiryPresets.expires_at_for(key)
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def extended_expiry
|
|
254
|
-
base = [@assignment.expires_at&.to_date, Time.zone.today].compact.max
|
|
255
|
-
(base + 30).in_time_zone.end_of_day
|
|
257
|
+
PermissionForge::Admin::ExpiryPresets.change_to(key, from: @assignment.expires_at)
|
|
256
258
|
end
|
|
257
259
|
|
|
258
260
|
# What the person stops seeing when this (granting) assignment is removed.
|
|
@@ -260,7 +262,7 @@ module PermissionForge
|
|
|
260
262
|
permission_set = @assignment.permission_set
|
|
261
263
|
return [] if permission_set.is_muting
|
|
262
264
|
|
|
263
|
-
permission_set.resources.
|
|
265
|
+
permission_set.resources.in_effect.map { |r| helpers.pf_resource_plural(r.name) }
|
|
264
266
|
end
|
|
265
267
|
end
|
|
266
268
|
end
|
|
@@ -8,6 +8,7 @@ module PermissionForge
|
|
|
8
8
|
class BulkAssignmentsController < ApplicationController
|
|
9
9
|
include PermissionForge::AssignmentAuthorization
|
|
10
10
|
include PermissionForge::ActingAdmin
|
|
11
|
+
include PermissionForge::CacheConfirmation
|
|
11
12
|
|
|
12
13
|
before_action :set_permission_set
|
|
13
14
|
# A locked set refuses what widens access (EditableLock.widening_lock):
|
|
@@ -34,9 +35,11 @@ module PermissionForge
|
|
|
34
35
|
return render(:new, status: :unprocessable_entity) if @expiry_error || @nobody
|
|
35
36
|
return if grant_refused?
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
result = assign(@result.to_assign)
|
|
39
|
+
# the service's one cache outcome: what the store confirmed, and its scope (CacheConfirmation)
|
|
38
40
|
redirect_to assignments_path,
|
|
39
|
-
|
|
41
|
+
**cache_flash(result[:cache], { notice: :assigned, alert: :assigned_cache_kept },
|
|
42
|
+
count: result[:assigned], set: pf_record_name(@permission_set))
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
def confirm_revoke
|
|
@@ -47,11 +50,12 @@ module PermissionForge
|
|
|
47
50
|
ids = selected_people.map(&:id)
|
|
48
51
|
return if pf_assignment_refused?(@permission_set, ids, :revoke, assignments_path)
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
result = PermissionForge::Services::BulkPermissionSetAssignment.call(
|
|
51
54
|
permission_set: @permission_set, user_ids: ids, action: :revoke
|
|
52
55
|
)
|
|
53
56
|
redirect_to assignments_path,
|
|
54
|
-
|
|
57
|
+
**cache_flash(result[:cache], { notice: :revoked, alert: :revoked_cache_kept },
|
|
58
|
+
count: result[:revoked], set: pf_record_name(@permission_set))
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
private
|
|
@@ -3,11 +3,15 @@ module PermissionForge
|
|
|
3
3
|
#
|
|
4
4
|
# @api private
|
|
5
5
|
class MaintenanceController < ApplicationController
|
|
6
|
-
|
|
6
|
+
include PermissionForge::CacheConfirmation
|
|
7
|
+
|
|
8
|
+
# Removes expired assignments (the dry run is shown on Home first). The
|
|
9
|
+
# flash says what the cache store confirmed for the affected people, and
|
|
10
|
+
# when the store lives in this process only (CacheConfirmation).
|
|
7
11
|
def cleanup
|
|
8
12
|
result = PermissionForge::Services::ExpiredAssignmentCleaner.call
|
|
9
13
|
redirect_to permission_forge.root_path,
|
|
10
|
-
notice:
|
|
14
|
+
**cache_flash(result[:cache], { notice: :cleaned, alert: :cleaned_cache_kept }, count: result[:removed])
|
|
11
15
|
end
|
|
12
16
|
end
|
|
13
17
|
end
|
|
@@ -55,15 +55,15 @@ module PermissionForge
|
|
|
55
55
|
return if pf_assignment_refused?(sets, @person.id, :revoke, path)
|
|
56
56
|
|
|
57
57
|
count = @removable.size
|
|
58
|
-
|
|
58
|
+
outcome = cache_outcome { PermissionForge::Audit.correlate { @removable.each(&:destroy!) } }
|
|
59
59
|
redirect_to permission_forge.person_path(@person),
|
|
60
|
-
**cache_flash(
|
|
60
|
+
**cache_flash(outcome, { notice: :removed_all, alert: :removed_all_cache_kept }, count: count, name: person_name)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def refresh_cache
|
|
64
|
-
|
|
64
|
+
outcome = cache_outcome { @person.invalidate_permissions if @person.respond_to?(:invalidate_permissions) }
|
|
65
65
|
redirect_to permission_forge.person_path(@person, tab: pf_tab_param(TABS)),
|
|
66
|
-
**cache_flash(
|
|
66
|
+
**cache_flash(outcome, { notice: :cache_refreshed, alert: :cache_refresh_failed }, name: person_name)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# --- consent withdrawal -------------------------------------------------------
|
|
@@ -78,11 +78,11 @@ module PermissionForge
|
|
|
78
78
|
flag = params[:flag].to_s
|
|
79
79
|
return redirect_to(permission_forge.person_path(@person)) unless consent_flags.include?(flag)
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
# nothing revoked
|
|
81
|
+
result = consent_service(flag, dry_run: false)
|
|
82
|
+
# the service's one cache outcome (nothing revoked: nothing to clear, confirmed)
|
|
83
83
|
redirect_to permission_forge.person_path(@person),
|
|
84
|
-
**cache_flash(
|
|
85
|
-
count:
|
|
84
|
+
**cache_flash(result[:cache], { notice: :consent_withdrawn, alert: :consent_withdrawn_cache_kept },
|
|
85
|
+
count: result[:revoked_sets], flag: flag)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
# --- erasure ----------------------------------------------------------------------
|
|
@@ -99,10 +99,9 @@ module PermissionForge
|
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
result = erasure_service(dry_run: false)
|
|
102
|
-
# nil: erased inside a transaction of the host's, the cache follows at its commit
|
|
103
|
-
confirmed = result[:cache_cleared] != false
|
|
102
|
+
# the service's one cache outcome (confirmed nil: erased inside a transaction of the host's, the cache follows at its commit)
|
|
104
103
|
redirect_to permission_forge.people_path,
|
|
105
|
-
**cache_flash(
|
|
104
|
+
**cache_flash(result[:cache], { notice: :erased, alert: :erased_cache_kept }, name: person_name)
|
|
106
105
|
end
|
|
107
106
|
|
|
108
107
|
private
|
|
@@ -151,7 +150,8 @@ module PermissionForge
|
|
|
151
150
|
def load_audit_tab
|
|
152
151
|
@changes = audit.changes_by(@person.id, limit: CHANGES_SHOWN)
|
|
153
152
|
@changes_capped = audit.capped?
|
|
154
|
-
@
|
|
153
|
+
@views = pf_text_param(:views).presence # a Hash or a list is no toggle (it would reach url_for)
|
|
154
|
+
@timeline_log = PermissionForge::Admin::AuditLog.new({ views: @views },
|
|
155
155
|
scope: PermissionForge::Admin::AuditLog.for_person(@person.id))
|
|
156
156
|
@timeline = @timeline_log.page(params[:page], per_page: TIMELINE_PER_PAGE)
|
|
157
157
|
@timeline_lookups = PermissionForge::Admin::AuditLookups.new(@timeline.events)
|
|
@@ -163,14 +163,14 @@ module PermissionForge
|
|
|
163
163
|
|
|
164
164
|
def send_changes_csv
|
|
165
165
|
entries = audit.changes_by(@person.id).values.flatten
|
|
166
|
-
csv = audit.to_csv(entries, describe: helpers.method(:
|
|
166
|
+
csv = audit.to_csv(entries, describe: helpers.method(:pf_audit_csv_describe), person: helpers.method(:pf_audit_people))
|
|
167
167
|
response.headers['Cache-Control'] = 'no-store' # names of people, like the log's exports
|
|
168
168
|
send_data csv, filename: "permission-forge-changes-#{@person.id}.csv", type: 'text/csv'
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
def lost_resource_labels(assignments)
|
|
172
172
|
assignments.map(&:permission_set).reject(&:is_muting)
|
|
173
|
-
.flat_map { |ps| ps.resources.
|
|
173
|
+
.flat_map { |ps| ps.resources.in_effect.map { |r| helpers.pf_resource_plural(r.name) } }.uniq
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
# Flags gating sets this person has, plus configured flags.
|
|
@@ -20,7 +20,7 @@ module PermissionForge
|
|
|
20
20
|
live = @permission_set.permission_set_users.active_assignments.includes(:user).order(:created_at)
|
|
21
21
|
@people_count = live.count
|
|
22
22
|
@impact_people = live.limit(3).map(&:user).compact
|
|
23
|
-
@lost = @permission_set.is_muting ? [] : @permission_set.resources.
|
|
23
|
+
@lost = @permission_set.is_muting ? [] : @permission_set.resources.in_effect.map { |r| helpers.pf_resource_plural(r.name) }
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def load_resources_tab
|
|
@@ -31,6 +31,15 @@ module PermissionForge
|
|
|
31
31
|
@assignments = @permission_set.permission_set_users.includes(:user).order(:created_at).to_a
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# The Conditions tab again, with the refused form's values and errors.
|
|
35
|
+
def render_conditions_refused
|
|
36
|
+
@tab = 'conditions'
|
|
37
|
+
@ip_text = params[:allowed_ips].to_s
|
|
38
|
+
load_editor
|
|
39
|
+
load_conditions_tab
|
|
40
|
+
render :show, status: :unprocessable_entity
|
|
41
|
+
end
|
|
42
|
+
|
|
34
43
|
def load_conditions_tab
|
|
35
44
|
@ip_text ||= helpers.pf_ip_list(@permission_set.allowed_ips).join("\n")
|
|
36
45
|
configured = (PermissionForge.configuration.admin_session_flags || {}).keys.map(&:to_s)
|
|
@@ -48,22 +57,27 @@ module PermissionForge
|
|
|
48
57
|
|
|
49
58
|
# The set's events, newest first (the log filtered by this set).
|
|
50
59
|
def load_audit_timeline
|
|
51
|
-
@timeline_log = PermissionForge::Admin::AuditLog.new({ views:
|
|
60
|
+
@timeline_log = PermissionForge::Admin::AuditLog.new({ views: @views },
|
|
52
61
|
scope: PermissionForge::Admin::AuditLog.for_permission_set(@permission_set))
|
|
53
62
|
@timeline = @timeline_log.page(params[:page], per_page: AUDIT_TIMELINE_PER_PAGE)
|
|
54
63
|
@timeline_lookups = PermissionForge::Admin::AuditLookups.new(@timeline.events)
|
|
55
64
|
end
|
|
56
65
|
|
|
66
|
+
# Typed like every admin parameter (ParamShapes): a Hash or a list in
|
|
67
|
+
# one of them is no filter, never a 500. Days are one of the offered
|
|
68
|
+
# counts (AuditTrail::DAY_FILTERS), else no filter.
|
|
57
69
|
def load_audit_filters
|
|
58
|
-
@who =
|
|
59
|
-
@type =
|
|
60
|
-
|
|
70
|
+
@who = pf_text_param(:who).presence
|
|
71
|
+
@type = pf_text_param(:type).presence
|
|
72
|
+
offered = PermissionForge::Admin::AuditTrail::DAY_FILTERS
|
|
73
|
+
@days = pf_integer_param(:days, default: nil, range: 1..offered.max).then { |days| days if offered.include?(days) }
|
|
61
74
|
@sort = params[:sort] == 'asc' ? 'asc' : 'desc'
|
|
75
|
+
@views = pf_text_param(:views).presence
|
|
62
76
|
end
|
|
63
77
|
|
|
64
78
|
def send_audit_csv
|
|
65
79
|
audit = PermissionForge::Admin::AuditTrail.new
|
|
66
|
-
csv = audit.to_csv(@entries, describe: helpers.method(:
|
|
80
|
+
csv = audit.to_csv(@entries, describe: helpers.method(:pf_audit_csv_describe), person: helpers.method(:pf_audit_people))
|
|
67
81
|
response.headers['Cache-Control'] = 'no-store' # names of people, like the log's exports
|
|
68
82
|
send_data csv, filename: "permission-forge-#{@permission_set.name.parameterize}-audit.csv", type: 'text/csv'
|
|
69
83
|
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
module PermissionForge
|
|
2
2
|
# Group membership. Organisational only: it never gives Permission Sets.
|
|
3
3
|
# Adding and removing go through the model callbacks (group_membership.*
|
|
4
|
-
# audit events) after config.admin_assignment_authorization.
|
|
4
|
+
# audit events) after config.admin_assignment_authorization. No cached
|
|
5
|
+
# permissions change, so the toast reports no cache outcome (unlike an
|
|
6
|
+
# assignment: CacheConfirmation).
|
|
5
7
|
#
|
|
6
8
|
# @api private
|
|
7
9
|
class PermissionSetGroupUsersController < ApplicationController
|
|
@@ -4,6 +4,8 @@ module PermissionForge
|
|
|
4
4
|
#
|
|
5
5
|
# @api private
|
|
6
6
|
class PermissionSetGroupsController < ApplicationController
|
|
7
|
+
include PermissionForge::CopyRefusal
|
|
8
|
+
|
|
7
9
|
before_action :set_group, except: %i[index create]
|
|
8
10
|
before_action :reject_locked, only: %i[update destroy confirm_destroy add_set remove_set]
|
|
9
11
|
|
|
@@ -17,7 +19,7 @@ module PermissionForge
|
|
|
17
19
|
return render_blank_name if @group.name.blank?
|
|
18
20
|
|
|
19
21
|
@group.save!
|
|
20
|
-
redirect_to permission_forge.group_path(@group), notice: t('permission_forge.flash.groups.created', name: @group
|
|
22
|
+
redirect_to permission_forge.group_path(@group), notice: t('permission_forge.flash.groups.created', name: pf_record_name(@group))
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def show
|
|
@@ -28,12 +30,16 @@ module PermissionForge
|
|
|
28
30
|
@people_counts = PermissionForge::PermissionSetUser.active_assignments.group(:permission_set_id).count
|
|
29
31
|
end
|
|
30
32
|
|
|
33
|
+
# "Group saved. No one's access changed." for a change; an unchanged
|
|
34
|
+
# group is not saved, and says so (SaveNotice).
|
|
31
35
|
def update
|
|
32
36
|
attrs = group_params
|
|
33
|
-
return redirect_to(
|
|
37
|
+
return redirect_to(group_page, alert: t('permission_forge.errors.admin.group_blank')) if blank_name?(attrs)
|
|
38
|
+
|
|
39
|
+
saved = pf_save_changed(@group, attrs, &:save)
|
|
40
|
+
return redirect_to(group_page, alert: PermissionForge::Admin::ErrorSentence.first(@group.errors)) unless saved
|
|
34
41
|
|
|
35
|
-
@group.
|
|
36
|
-
redirect_to permission_forge.group_path(@group), notice: t('permission_forge.flash.groups.updated')
|
|
42
|
+
redirect_to group_page, **pf_save_flash(@group) { t('permission_forge.flash.groups.updated') }
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
def duplicate
|
|
@@ -41,13 +47,15 @@ module PermissionForge
|
|
|
41
47
|
permission_set_group: @group, attrs: { name: t('permission_forge.permission_sets.copy_name', name: @group.name), is_editable: true },
|
|
42
48
|
unlock: true
|
|
43
49
|
)
|
|
44
|
-
redirect_to permission_forge.group_path(copy), notice: t('permission_forge.flash.groups.duplicated', name: @group
|
|
50
|
+
redirect_to permission_forge.group_path(copy), notice: t('permission_forge.flash.groups.duplicated', name: pf_record_name(@group))
|
|
51
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
52
|
+
redirect_to permission_forge.group_path(@group), alert: pf_copy_refused_message(e, pf_record_name(@group))
|
|
45
53
|
end
|
|
46
54
|
|
|
47
55
|
def confirm_destroy; end
|
|
48
56
|
|
|
49
57
|
def destroy
|
|
50
|
-
name = @group
|
|
58
|
+
name = pf_record_name(@group)
|
|
51
59
|
@group.destroy!
|
|
52
60
|
redirect_to permission_forge.groups_path, notice: t('permission_forge.flash.groups.deleted', name: name)
|
|
53
61
|
end
|
|
@@ -61,7 +69,7 @@ module PermissionForge
|
|
|
61
69
|
return if pf_reject_locked!([set, leaving], permission_forge.group_path(@group))
|
|
62
70
|
|
|
63
71
|
set.update!(permission_set_group: @group)
|
|
64
|
-
redirect_to permission_forge.group_path(@group), notice: t('permission_forge.flash.groups.set_added', set: set
|
|
72
|
+
redirect_to permission_forge.group_path(@group), notice: t('permission_forge.flash.groups.set_added', set: pf_record_name(set))
|
|
65
73
|
end
|
|
66
74
|
|
|
67
75
|
def remove_set
|
|
@@ -69,7 +77,7 @@ module PermissionForge
|
|
|
69
77
|
return if pf_reject_locked!(set, permission_forge.group_path(@group))
|
|
70
78
|
|
|
71
79
|
set.update!(permission_set_group: nil)
|
|
72
|
-
redirect_to permission_forge.group_path(@group), notice: t('permission_forge.flash.groups.set_removed', set: set
|
|
80
|
+
redirect_to permission_forge.group_path(@group), notice: t('permission_forge.flash.groups.set_removed', set: pf_record_name(set))
|
|
73
81
|
end
|
|
74
82
|
|
|
75
83
|
private
|
|
@@ -78,6 +86,8 @@ module PermissionForge
|
|
|
78
86
|
@group = PermissionForge::PermissionSetGroup.find(params[:id])
|
|
79
87
|
end
|
|
80
88
|
|
|
89
|
+
def group_page = permission_forge.group_path(@group)
|
|
90
|
+
|
|
81
91
|
def load_index
|
|
82
92
|
@groups = PermissionForge::PermissionSetGroup.includes(:permission_sets, :permission_set_group_users).order(:name)
|
|
83
93
|
@ungrouped = PermissionForge::PermissionSet.where(permission_set_group_id: nil).order(:name)
|
|
@@ -91,8 +101,12 @@ module PermissionForge
|
|
|
91
101
|
render(:index, status: :unprocessable_entity)
|
|
92
102
|
end
|
|
93
103
|
|
|
104
|
+
# A blank name over a generated one, or a blank description over none,
|
|
105
|
+
# is no change (Admin::Prefill.posted).
|
|
94
106
|
def group_params
|
|
95
|
-
pf_require_hash(:group).permit(:name, :description, :is_active).tap { |
|
|
107
|
+
attrs = pf_require_hash(:group).permit(:name, :description, :is_active).tap { |permitted| strip_name!(permitted) }
|
|
108
|
+
PermissionForge::Admin::Prefill.posted_attributes(@group, attrs, :name, :description)
|
|
109
|
+
pf_boolean_attributes(attrs, :is_active)
|
|
96
110
|
end
|
|
97
111
|
|
|
98
112
|
def strip_name!(attrs)
|
|
@@ -23,7 +23,7 @@ module PermissionForge
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def update
|
|
26
|
-
@form = PermissionForge::Admin::RuleForm.new(resource: @resource, params: rule_params)
|
|
26
|
+
@form = PermissionForge::Admin::RuleForm.new(resource: @resource, params: rule_params, rule: @rule)
|
|
27
27
|
save_rule(:edit, 'updated')
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -34,16 +34,26 @@ module PermissionForge
|
|
|
34
34
|
render(@rule ? :edit : :new)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# The rule being built as the save would answer it (Admin::RulePreview:
|
|
38
|
+
# the first error the save would show, else the sentence the Rules tab
|
|
39
|
+
# draws): the fragment pf_admin.js fetches as the value inputs change;
|
|
40
|
+
# without JS a page of its own. Changes nothing.
|
|
41
|
+
def preview
|
|
42
|
+
stored = @resource.permission_set_rules.find_by(id: params[:rule_id]) if params[:rule_id].present?
|
|
43
|
+
@form = PermissionForge::Admin::RuleForm.new(resource: @resource, params: rule_params, rule: stored)
|
|
44
|
+
@preview = PermissionForge::Admin::RulePreview.new(form: @form, resource: @resource, stored: stored)
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
def toggle
|
|
38
48
|
@rule.update!(is_active: !@rule.is_active)
|
|
39
49
|
key = @rule.is_active ? 'turned_on' : 'turned_off'
|
|
40
|
-
redirect_to rules_path, notice: t("permission_forge.flash.rules.#{key}", rule: @rule
|
|
50
|
+
redirect_to rules_path, notice: t("permission_forge.flash.rules.#{key}", rule: pf_record_name(@rule))
|
|
41
51
|
end
|
|
42
52
|
|
|
43
53
|
def confirm_destroy; end
|
|
44
54
|
|
|
45
55
|
def destroy
|
|
46
|
-
name = @rule
|
|
56
|
+
name = pf_record_name(@rule)
|
|
47
57
|
@rule.destroy!
|
|
48
58
|
redirect_to rules_path, notice: t('permission_forge.flash.rules.deleted', rule: name)
|
|
49
59
|
end
|
|
@@ -52,7 +62,8 @@ module PermissionForge
|
|
|
52
62
|
|
|
53
63
|
# What the builder page draws: built here, read by the template.
|
|
54
64
|
def rule_builder
|
|
55
|
-
@rule_builder ||= PermissionForge::Admin::RuleBuilder.new(form: @form, resource: @resource, model_errors: @model_errors
|
|
65
|
+
@rule_builder ||= PermissionForge::Admin::RuleBuilder.new(form: @form, resource: @resource, model_errors: @model_errors,
|
|
66
|
+
rule: @rule)
|
|
56
67
|
end
|
|
57
68
|
|
|
58
69
|
def set_resource
|
|
@@ -64,9 +75,9 @@ module PermissionForge
|
|
|
64
75
|
@rule = @resource.permission_set_rules.find(params[:id])
|
|
65
76
|
end
|
|
66
77
|
|
|
78
|
+
# The rule's chain (rule, Resource, Permission Set), or the Resource's for a new rule.
|
|
67
79
|
def reject_locked
|
|
68
|
-
|
|
69
|
-
pf_reject_locked!(locked, rules_path) if locked
|
|
80
|
+
pf_reject_locked!(@rule || @resource, rules_path)
|
|
70
81
|
end
|
|
71
82
|
|
|
72
83
|
def rules_path
|
|
@@ -85,11 +96,10 @@ module PermissionForge
|
|
|
85
96
|
return render(template, status: :unprocessable_entity)
|
|
86
97
|
end
|
|
87
98
|
|
|
88
|
-
@rule
|
|
89
|
-
|
|
90
|
-
redirect_to rules_path, notice: t("permission_forge.flash.rules.#{flash_key}", rule: @rule.name)
|
|
99
|
+
if pf_save_changed(@rule, @form.attributes, &:save) # an unchanged rule is not saved (SaveNotice)
|
|
100
|
+
redirect_to rules_path, **pf_save_flash(@rule) { t("permission_forge.flash.rules.#{flash_key}", rule: pf_record_name(@rule)) }
|
|
91
101
|
else
|
|
92
|
-
@model_errors = @rule.errors
|
|
102
|
+
@model_errors = @rule.errors
|
|
93
103
|
render template, status: :unprocessable_entity
|
|
94
104
|
end
|
|
95
105
|
end
|