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
data/lib/permission_forge.rb
CHANGED
|
@@ -258,6 +258,9 @@ module PermissionForge
|
|
|
258
258
|
end
|
|
259
259
|
|
|
260
260
|
require_relative 'permission_forge/execution_state'
|
|
261
|
+
require_relative 'permission_forge/execution_state/restore'
|
|
262
|
+
require_relative 'permission_forge/web_request'
|
|
263
|
+
require_relative 'permission_forge/inspection_filters'
|
|
261
264
|
require_relative 'permission_forge/model_bindings'
|
|
262
265
|
require_relative 'permission_forge/cache/base'
|
|
263
266
|
require_relative 'permission_forge/cache/memory_cache'
|
|
@@ -288,6 +291,7 @@ require_relative 'permission_forge/audit/subscribers'
|
|
|
288
291
|
require_relative 'permission_forge/audit/log_access'
|
|
289
292
|
require_relative 'permission_forge/audit/event_query'
|
|
290
293
|
require_relative 'permission_forge/audit/runtime_emission'
|
|
294
|
+
require_relative 'permission_forge/database_reach'
|
|
291
295
|
require_relative 'permission_forge/audit'
|
|
292
296
|
require_relative 'permission_forge/audit/gate'
|
|
293
297
|
require_relative 'permission_forge/audit/runtime'
|
|
@@ -295,32 +299,53 @@ require_relative 'permission_forge/audit/field_access'
|
|
|
295
299
|
require_relative 'permission_forge/audit/subscribers/base'
|
|
296
300
|
require_relative 'permission_forge/audit/subscribers/legacy_hooks'
|
|
297
301
|
require_relative 'permission_forge/audit/subscribers/log'
|
|
302
|
+
require_relative 'permission_forge/audit/actor_codes'
|
|
303
|
+
require_relative 'permission_forge/audit/reason_codes'
|
|
298
304
|
require_relative 'permission_forge/audit/actor_resolver'
|
|
299
305
|
require_relative 'permission_forge/audit/audited_views'
|
|
300
306
|
require_relative 'permission_forge/audit/request_context'
|
|
307
|
+
require_relative 'permission_forge/audit/unmasked_without_user'
|
|
301
308
|
require_relative 'permission_forge/audit/detached'
|
|
302
309
|
require_relative 'permission_forge/audit/refusals'
|
|
303
310
|
require_relative 'permission_forge/audit/job_context'
|
|
304
311
|
require_relative 'permission_forge/audit/settings_observer'
|
|
305
312
|
require_relative 'permission_forge/audit/boot_warnings'
|
|
306
313
|
|
|
314
|
+
require_relative 'permission_forge/schema_read'
|
|
315
|
+
require_relative 'permission_forge/schema_read/ordered_types'
|
|
307
316
|
require_relative 'permission_forge/resource_chain'
|
|
317
|
+
require_relative 'permission_forge/record_read_access'
|
|
318
|
+
require_relative 'permission_forge/unique_conflict'
|
|
308
319
|
require_relative 'permission_forge/virtual_fields'
|
|
320
|
+
require_relative 'permission_forge/managed_fields'
|
|
309
321
|
require_relative 'permission_forge/virtual_fields/discovery'
|
|
322
|
+
require_relative 'permission_forge/field_variants'
|
|
323
|
+
require_relative 'permission_forge/field_names'
|
|
324
|
+
require_relative 'permission_forge/timestamp_fields'
|
|
325
|
+
require_relative 'permission_forge/field_level'
|
|
326
|
+
require_relative 'permission_forge/sensitive_columns'
|
|
327
|
+
require_relative 'permission_forge/secret_columns'
|
|
328
|
+
require_relative 'permission_forge/encrypted_attributes'
|
|
329
|
+
require_relative 'permission_forge/generated_text'
|
|
310
330
|
|
|
311
331
|
require_relative 'permission_forge/concerns/has_active_scope'
|
|
312
332
|
require_relative 'permission_forge/concerns/has_tenant_scoping'
|
|
313
333
|
require_relative 'permission_forge/concerns/has_uuid'
|
|
334
|
+
require_relative 'permission_forge/concerns/keeps_copied_values'
|
|
314
335
|
require_relative 'permission_forge/concerns/has_audit_trail'
|
|
315
336
|
require_relative 'permission_forge/invalidation_batch'
|
|
316
337
|
require_relative 'permission_forge/concerns/invalidates_in_batch'
|
|
317
338
|
require_relative 'permission_forge/concerns/has_permission_set_users'
|
|
318
339
|
require_relative 'permission_forge/concerns/validates_dependency'
|
|
319
340
|
require_relative 'permission_forge/concerns/validates_column_lengths'
|
|
341
|
+
require_relative 'permission_forge/concerns/requires_name'
|
|
342
|
+
require_relative 'permission_forge/concerns/normalizes_label'
|
|
343
|
+
require_relative 'permission_forge/concerns/validates_required_columns'
|
|
320
344
|
require_relative 'permission_forge/concerns/permissions_regenerator'
|
|
321
345
|
require_relative 'permission_forge/concerns/invalidates_managed_actions'
|
|
322
346
|
require_relative 'permission_forge/concerns/records_audit_events'
|
|
323
347
|
require_relative 'permission_forge/concerns/audited_collection'
|
|
348
|
+
require_relative 'permission_forge/activation_conditions'
|
|
324
349
|
require_relative 'permission_forge/conditional_sets'
|
|
325
350
|
require_relative 'permission_forge/concerns/permissions_memo'
|
|
326
351
|
require_relative 'permission_forge/concerns/permissions_per_context'
|
|
@@ -335,7 +360,12 @@ require_relative 'permission_forge/concerns/masked_attributes'
|
|
|
335
360
|
require_relative 'permission_forge/operators/constants'
|
|
336
361
|
require_relative 'permission_forge/operators/validations'
|
|
337
362
|
require_relative 'permission_forge/operators/value_casters'
|
|
363
|
+
require_relative 'permission_forge/operators/rule_path'
|
|
364
|
+
require_relative 'permission_forge/operators/rule_sources'
|
|
365
|
+
require_relative 'permission_forge/model_lookup'
|
|
338
366
|
require_relative 'permission_forge/operators/rule_column'
|
|
367
|
+
require_relative 'permission_forge/operators/encrypted_alias'
|
|
368
|
+
require_relative 'permission_forge/operators/rule_problem'
|
|
339
369
|
require_relative 'permission_forge/operators/value_typing'
|
|
340
370
|
require_relative 'permission_forge/services/mask_value'
|
|
341
371
|
|
|
@@ -361,9 +391,11 @@ require_relative 'permission_forge/api/serializers/permission_set_user_serialize
|
|
|
361
391
|
require_relative 'permission_forge/api/serializers/audit_event_serializer'
|
|
362
392
|
require_relative 'permission_forge/api/serializers/permission_set_group_serializer'
|
|
363
393
|
require_relative 'permission_forge/api/serializers/permission_set_group_user_serializer'
|
|
394
|
+
require_relative 'permission_forge/api/schema'
|
|
364
395
|
|
|
365
396
|
require_relative 'permission_forge/services/concerns/clonable'
|
|
366
397
|
require_relative 'permission_forge/services/concerns/audited_operation'
|
|
398
|
+
require_relative 'permission_forge/services/concerns/settles_cache'
|
|
367
399
|
require_relative 'permission_forge/services/concerns/across_tenants'
|
|
368
400
|
require_relative 'permission_forge/services/bulk_permission_set_assignment'
|
|
369
401
|
require_relative 'permission_forge/services/consent_withdrawal_handler'
|
|
@@ -383,14 +415,17 @@ require_relative 'permission_forge/services/gdpr_field_auditor'
|
|
|
383
415
|
require_relative 'permission_forge/services/permission_hash_builder'
|
|
384
416
|
require_relative 'permission_forge/services/permits_attributes'
|
|
385
417
|
require_relative 'permission_forge/services/permits_attributes/nested_records'
|
|
418
|
+
require_relative 'permission_forge/services/permits_attributes/result'
|
|
386
419
|
require_relative 'permission_forge/services/data_subject_erasure'
|
|
387
420
|
require_relative 'permission_forge/services/host_key_guard'
|
|
388
421
|
require_relative 'permission_forge/services/stale_assignment_auditor'
|
|
389
422
|
require_relative 'permission_forge/services/audit_event_purger'
|
|
390
423
|
require_relative 'permission_forge/rake_audit'
|
|
424
|
+
require_relative 'permission_forge/rake_dry_run'
|
|
391
425
|
require_relative 'permission_forge/services/resource_field_generator'
|
|
392
426
|
require_relative 'permission_forge/services/unscoped_query_detector'
|
|
393
427
|
require_relative 'permission_forge/services/rls_operator_sql'
|
|
428
|
+
require_relative 'permission_forge/services/rls_read_access_sql'
|
|
394
429
|
require_relative 'permission_forge/services/rls_policy_generator'
|
|
395
430
|
require_relative 'permission_forge/rls'
|
|
396
431
|
|
|
@@ -399,12 +434,17 @@ require_relative 'permission_forge/admin/field_level'
|
|
|
399
434
|
require_relative 'permission_forge/admin/sensitive_columns'
|
|
400
435
|
require_relative 'permission_forge/admin/model_catalog'
|
|
401
436
|
require_relative 'permission_forge/admin/person_label'
|
|
437
|
+
require_relative 'permission_forge/admin/state_label'
|
|
438
|
+
require_relative 'permission_forge/admin/actor_label'
|
|
402
439
|
require_relative 'permission_forge/admin/people_candidates'
|
|
403
440
|
require_relative 'permission_forge/admin/people_directory'
|
|
441
|
+
require_relative 'permission_forge/admin/list_sentence'
|
|
404
442
|
require_relative 'permission_forge/admin/record_id'
|
|
405
443
|
require_relative 'permission_forge/admin/editable_lock'
|
|
406
444
|
require_relative 'permission_forge/admin/expiry_presets'
|
|
407
445
|
require_relative 'permission_forge/admin/effective_access'
|
|
446
|
+
require_relative 'permission_forge/admin/field_provenance'
|
|
447
|
+
require_relative 'permission_forge/admin/field_chain'
|
|
408
448
|
require_relative 'permission_forge/admin/effective_fields'
|
|
409
449
|
require_relative 'permission_forge/admin/settings_values'
|
|
410
450
|
require_relative 'permission_forge/admin/settings_catalog'
|
|
@@ -432,7 +472,15 @@ require_relative 'permission_forge/admin/change_set_parts/new_fields'
|
|
|
432
472
|
require_relative 'permission_forge/admin/change_set_parts/actions'
|
|
433
473
|
require_relative 'permission_forge/admin/change_set_parts/new_actions'
|
|
434
474
|
require_relative 'permission_forge/admin/change_set'
|
|
475
|
+
require_relative 'permission_forge/admin/field_label'
|
|
476
|
+
require_relative 'permission_forge/admin/model_label'
|
|
477
|
+
require_relative 'permission_forge/admin/action_label'
|
|
478
|
+
require_relative 'permission_forge/admin/error_sentence'
|
|
479
|
+
require_relative 'permission_forge/admin/prefill'
|
|
480
|
+
require_relative 'permission_forge/admin/record_name'
|
|
481
|
+
require_relative 'permission_forge/admin/chain_entries'
|
|
435
482
|
require_relative 'permission_forge/admin/field_rows'
|
|
483
|
+
require_relative 'permission_forge/admin/mask_strategy_choices'
|
|
436
484
|
require_relative 'permission_forge/admin/field_detail'
|
|
437
485
|
require_relative 'permission_forge/admin/audit_event_detail'
|
|
438
486
|
require_relative 'permission_forge/admin/rule_builder'
|
|
@@ -445,17 +493,23 @@ require_relative 'permission_forge/admin/conditions_form'
|
|
|
445
493
|
require_relative 'permission_forge/admin/permission_set_factory'
|
|
446
494
|
require_relative 'permission_forge/admin/rule_form_input'
|
|
447
495
|
require_relative 'permission_forge/admin/rule_form'
|
|
496
|
+
require_relative 'permission_forge/admin/rule_notes'
|
|
497
|
+
require_relative 'permission_forge/admin/rule_preview'
|
|
448
498
|
require_relative 'permission_forge/admin/bulk_people_matcher'
|
|
449
499
|
|
|
450
500
|
# Security review (rake permission_forge:audit)
|
|
501
|
+
require_relative 'permission_forge/api_authorizations'
|
|
451
502
|
require_relative 'permission_forge/services/security_audit'
|
|
452
503
|
require_relative 'permission_forge/services/security_audit/base_check'
|
|
504
|
+
require_relative 'permission_forge/services/security_audit/masking_without_user'
|
|
453
505
|
require_relative 'permission_forge/services/security_audit/config_check'
|
|
454
506
|
require_relative 'permission_forge/services/security_audit/privilege_check'
|
|
455
507
|
require_relative 'permission_forge/services/security_audit/assignment_check'
|
|
456
508
|
require_relative 'permission_forge/services/security_audit/rule_check'
|
|
457
509
|
require_relative 'permission_forge/services/security_audit/integrity_check'
|
|
510
|
+
require_relative 'permission_forge/services/security_audit/conditions_check'
|
|
458
511
|
require_relative 'permission_forge/services/security_audit/personal_data_check'
|
|
512
|
+
require_relative 'permission_forge/services/security_audit/unmanaged_actions'
|
|
459
513
|
require_relative 'permission_forge/services/security_audit/action_check'
|
|
460
514
|
require_relative 'permission_forge/services/security_audit/audit_trail_check'
|
|
461
515
|
require_relative 'permission_forge/services/security_audit/user_model_check'
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
namespace :permission_forge do
|
|
4
|
-
desc 'Report
|
|
4
|
+
desc 'Report personal-data fields that are not masked, and matching model columns with no field row. ' \
|
|
5
|
+
'ACTOR= names who ran it, REASON= why (recorded as report.generated).'
|
|
5
6
|
task audit_masks: :environment do
|
|
6
7
|
report = PermissionForge::RakeAudit.reporting('permission_forge:audit_masks', counts: ->(r) { { findings: r.size } }) do
|
|
7
8
|
PermissionForge::Services::GdprFieldAuditor.call
|
|
@@ -13,19 +14,38 @@ namespace :permission_forge do
|
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
namespace :permission_forge do
|
|
16
|
-
|
|
17
|
+
# Exits 1 when FAIL_ON is given and a finding of that severity or worse
|
|
18
|
+
# exists (saying so on stderr, so FORMAT=json output stays parseable), and 2
|
|
19
|
+
# when FAIL_ON names no severity (before the review runs).
|
|
20
|
+
# It changes no permission data; every run is recorded (report.generated)
|
|
21
|
+
# when the audit log is on.
|
|
22
|
+
desc 'Security review of the setup: changes no permission data, records the run in the audit log. ' \
|
|
23
|
+
'FORMAT=json, FAIL_ON=critical|high|medium|low|info; ACTOR= names who ran it, REASON= why.'
|
|
17
24
|
task audit: :environment do
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
audit = PermissionForge::Services::SecurityAudit
|
|
26
|
+
given = ENV.fetch('FAIL_ON', nil).presence
|
|
27
|
+
fail_on = given && audit.severity_named(given)
|
|
28
|
+
if given && fail_on.nil?
|
|
29
|
+
warn "FAIL_ON must be one of #{audit::SEVERITIES.join(', ')} (got #{given.inspect}). " \
|
|
30
|
+
'Usage: rake permission_forge:audit FAIL_ON=high'
|
|
31
|
+
exit 2
|
|
20
32
|
end
|
|
21
33
|
|
|
34
|
+
# A rake task does not eager-load: the review must see every model (an
|
|
35
|
+
# STI subclass's `encrypts`, the models on a table a rule joins).
|
|
36
|
+
PermissionForge::Admin::ModelCatalog.eager_load_app_models
|
|
37
|
+
report = PermissionForge::RakeAudit.reporting('permission_forge:audit', counts: :counts.to_proc) { audit.call }
|
|
38
|
+
|
|
22
39
|
if ENV['FORMAT'] == 'json'
|
|
23
40
|
puts JSON.pretty_generate(report.to_h)
|
|
24
41
|
else
|
|
25
42
|
puts PermissionForge::Services::SecurityAuditChecks::TextReport.new(report)
|
|
26
43
|
end
|
|
27
44
|
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
failing = fail_on ? report.at_least(fail_on).size : 0
|
|
46
|
+
if failing.positive?
|
|
47
|
+
warn "Failing: #{failing} finding(s) at or above #{fail_on} (FAIL_ON=#{given})."
|
|
48
|
+
exit 1
|
|
49
|
+
end
|
|
30
50
|
end
|
|
31
51
|
end
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
namespace :permission_forge do
|
|
4
4
|
namespace :audit_events do
|
|
5
|
-
|
|
5
|
+
# Prints the result the audit.purged events record (RakeOutput).
|
|
6
|
+
desc 'Delete audit events older than config.audit_retention_period. Deletes when DRY_RUN is unset or ' \
|
|
7
|
+
'false (or 0, no); any other DRY_RUN value (1, true, yes) only counts. ACTOR= names who ran it, REASON= why.'
|
|
6
8
|
task purge: :environment do
|
|
7
9
|
task_name = 'permission_forge:audit_events:purge'
|
|
8
|
-
dry_run =
|
|
10
|
+
dry_run = PermissionForge::RakeDryRun.dry_run?(ENV.fetch('DRY_RUN', nil), default: false)
|
|
9
11
|
purge = -> { PermissionForge::Services::AuditEventPurger.call(dry_run: dry_run) }
|
|
10
12
|
result = begin
|
|
11
13
|
dry_run ? purge.call : PermissionForge::RakeAudit.changing(task_name, &purge)
|
|
@@ -19,9 +21,11 @@ namespace :permission_forge do
|
|
|
19
21
|
if !result[:retention_configured]
|
|
20
22
|
puts 'Retention is not configured (config.audit_retention_period is nil): nothing was deleted.'
|
|
21
23
|
elsif dry_run
|
|
22
|
-
|
|
24
|
+
PermissionForge::RakeOutput.report("Dry run — would delete #{result[:purged]} audit event(s) older than " \
|
|
25
|
+
"#{result[:cutoff].iso8601}:", result)
|
|
23
26
|
else
|
|
24
|
-
|
|
27
|
+
PermissionForge::RakeOutput.report("Deleted #{result[:purged]} audit event(s) older than #{result[:cutoff].iso8601}:",
|
|
28
|
+
result.except(:dry_run))
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
end
|
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
namespace :permission_forge do
|
|
4
4
|
namespace :cache do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
# Upgrading from 0.13 or earlier: run it once the last process of the
|
|
6
|
+
# previous release is gone (it also removes the previous key namespace).
|
|
7
|
+
# What it prints is the result the cache.cleared event records
|
|
8
|
+
# (RakeOutput), the cache outcome included: on an in-memory store only
|
|
9
|
+
# this process's store is cleared, and the output says so.
|
|
10
|
+
desc "Remove every user's cached permissions at once; each user rebuilds on their next request. " \
|
|
11
|
+
'ACTOR=, REASON= (After an upgrade from 0.13: run it once the old processes are gone.)'
|
|
8
12
|
task clear: :environment do
|
|
9
13
|
task_name = 'permission_forge:cache:clear'
|
|
10
14
|
result = PermissionForge::RakeAudit.changing(task_name) { PermissionForge::Services::PermissionCacheClearer.call }
|
|
11
15
|
PermissionForge::RakeAudit.log(task_name, result.map { |key, value| "#{key}=#{value.inspect}" }.join(' '))
|
|
12
|
-
|
|
16
|
+
# what it means for the users is the outcome's "Cache scope" line (a shared store: every user rebuilds on their
|
|
17
|
+
# next request; a per-process store: this process's only), never a second claim here
|
|
13
18
|
done = if result[:strategy] == :pattern
|
|
14
|
-
'Removed every cached permission hash by key pattern
|
|
19
|
+
'Removed every cached permission hash by key pattern:'
|
|
15
20
|
else
|
|
16
|
-
|
|
21
|
+
'Invalidated the cached permissions of every user:'
|
|
17
22
|
end
|
|
18
|
-
|
|
19
|
-
if PermissionForge.cache.process_local?
|
|
20
|
-
puts 'Only the cache store of this rake process was cleared: an in-memory store (cache_backend :memory, or ' \
|
|
21
|
-
':memory_store) lives in each process, so the web and job processes keep their entries until they ' \
|
|
22
|
-
'restart or the entries expire (cache_ttl). Invalidate the users from inside those processes, or restart them.'
|
|
23
|
-
end
|
|
23
|
+
PermissionForge::RakeOutput.report(done, result)
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The gem's own documentation generators, run from the gem's Rakefile
|
|
4
|
+
# (`bundle exec rake docs:configuration`, `docs:api`). They are defined only in a checkout
|
|
5
|
+
# of the gem (where docs/ is) and never inside a Rails application — not in a
|
|
6
|
+
# host's `rails -T` (the gem ships without docs/), not in the dummy app's.
|
|
7
|
+
docs_root = File.expand_path('../../docs', __dir__)
|
|
8
|
+
inside_rails = defined?(Rails) && Rails.respond_to?(:application) && Rails.application
|
|
9
|
+
if File.directory?(docs_root) && !inside_rails
|
|
10
|
+
namespace :docs do
|
|
11
|
+
desc 'Regenerate the Option reference of docs/configuration.md from SettingsCatalog and Configuration.new'
|
|
12
|
+
task :configuration do
|
|
13
|
+
lib = File.expand_path('../../lib', __dir__)
|
|
14
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
15
|
+
require 'permission_forge'
|
|
16
|
+
require 'permission_forge/docs/configuration_reference'
|
|
17
|
+
|
|
18
|
+
path = File.join(docs_root, 'configuration.md')
|
|
19
|
+
reference = PermissionForge::Docs::ConfigurationReference.new(document: File.read(path))
|
|
20
|
+
if reference.stale?
|
|
21
|
+
File.write(path, reference.render)
|
|
22
|
+
puts 'docs/configuration.md: Option reference regenerated'
|
|
23
|
+
else
|
|
24
|
+
puts 'docs/configuration.md: Option reference is up to date'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc 'Regenerate the json sample blocks (sample=<name>) of docs/admin-api.md from Api::Schema and the serializers'
|
|
29
|
+
task :api do
|
|
30
|
+
lib = File.expand_path('../../lib', __dir__)
|
|
31
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
32
|
+
require 'permission_forge'
|
|
33
|
+
require 'permission_forge/docs/api_samples'
|
|
34
|
+
# The one parser of the docs' fenced blocks (a checkout has test/).
|
|
35
|
+
require File.expand_path('../../test/support/doc_blocks', __dir__)
|
|
36
|
+
|
|
37
|
+
path = File.join(docs_root, 'admin-api.md')
|
|
38
|
+
document = File.read(path)
|
|
39
|
+
samples = PermissionForge::Docs::ApiSamples.new(document: document, blocks: DocBlocks.parse(document, file: 'docs/admin-api.md'))
|
|
40
|
+
if samples.stale?
|
|
41
|
+
File.write(path, samples.render)
|
|
42
|
+
puts 'docs/admin-api.md: json samples regenerated'
|
|
43
|
+
else
|
|
44
|
+
puts 'docs/admin-api.md: json samples are up to date'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
namespace :permission_forge do
|
|
4
|
-
|
|
4
|
+
# Prints the result the cleanup.expired_assignments event records
|
|
5
|
+
# (RakeOutput): the people by name, the sets, the cache outcome.
|
|
6
|
+
desc "Remove expired permission set assignments and invalidate affected users' cached permissions. " \
|
|
7
|
+
'ACTOR= names who ran it, REASON= why.'
|
|
5
8
|
task cleanup_expired_assignments: :environment do
|
|
6
9
|
task_name = 'permission_forge:cleanup_expired_assignments'
|
|
7
10
|
result = PermissionForge::RakeAudit.changing(task_name) { PermissionForge::Services::ExpiredAssignmentCleaner.call }
|
|
@@ -9,11 +12,12 @@ namespace :permission_forge do
|
|
|
9
12
|
if result[:removed].zero?
|
|
10
13
|
puts 'No expired assignments found.'
|
|
11
14
|
else
|
|
12
|
-
|
|
15
|
+
PermissionForge::RakeOutput.report("Removed #{result[:removed]} expired assignment(s):", result)
|
|
13
16
|
end
|
|
14
17
|
end
|
|
15
18
|
|
|
16
|
-
desc '
|
|
19
|
+
desc 'List expired permission set assignments without removing them, as a dry run. ' \
|
|
20
|
+
'ACTOR= names who ran it, REASON= why (recorded as report.generated).'
|
|
17
21
|
task expired_assignments: :environment do
|
|
18
22
|
result = PermissionForge::RakeAudit.reporting(
|
|
19
23
|
'permission_forge:expired_assignments',
|
|
@@ -22,8 +26,10 @@ namespace :permission_forge do
|
|
|
22
26
|
if result[:expired_count].zero?
|
|
23
27
|
puts 'No expired assignments found.'
|
|
24
28
|
else
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
PermissionForge::RakeOutput.report(
|
|
30
|
+
"Dry run — would remove #{result[:expired_count]} expired assignment(s) affecting #{result[:affected_user_ids].size} person(s):",
|
|
31
|
+
result.slice(:expired_count, :affected_user_ids, :cache).merge(dry_run: true)
|
|
32
|
+
)
|
|
27
33
|
puts 'Run `rake permission_forge:cleanup_expired_assignments` to remove them.'
|
|
28
34
|
end
|
|
29
35
|
end
|
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
namespace :permission_forge do
|
|
4
|
-
|
|
4
|
+
# Exits 1 when it finds stale assignments, 2 when THRESHOLD_DAYS is not a
|
|
5
|
+
# number of days (rather than reading it as 0, which flags everything).
|
|
6
|
+
# People are named as the admin names them (RakeOutput.people).
|
|
7
|
+
desc 'Find permanent assignments not reviewed in THRESHOLD_DAYS, by default admin_stale_assignment_days. ' \
|
|
8
|
+
'Exits 1 when it finds any. ACTOR= names who ran it, REASON= why (recorded as report.generated).'
|
|
5
9
|
task stale_assignments: :environment do
|
|
6
|
-
|
|
10
|
+
task_name = 'permission_forge:stale_assignments'
|
|
11
|
+
threshold = ENV.fetch('THRESHOLD_DAYS', nil)&.then do |given|
|
|
12
|
+
digits = given.strip
|
|
13
|
+
# ASCII digits only: Integer() would also take Ruby's underscores ("1_0").
|
|
14
|
+
days = Integer(digits, 10) if digits.match?(/\A[0-9]+\z/)
|
|
15
|
+
next days if days&.between?(1, 36_500)
|
|
16
|
+
|
|
17
|
+
message = "THRESHOLD_DAYS must be a whole number of days from 1 to 36500 (got #{given.inspect}). " \
|
|
18
|
+
"Usage: rake #{task_name} THRESHOLD_DAYS=90"
|
|
19
|
+
PermissionForge::RakeAudit.log(task_name, "refused: #{message}")
|
|
20
|
+
warn message
|
|
21
|
+
exit 2
|
|
22
|
+
end
|
|
7
23
|
result = PermissionForge::RakeAudit.reporting(
|
|
8
|
-
|
|
9
|
-
counts: ->(r) { { stale: r[:stale_count], users: r[:affected_user_ids].size, threshold_days:
|
|
24
|
+
task_name,
|
|
25
|
+
counts: ->(r) { { stale: r[:stale_count], users: r[:affected_user_ids].size, threshold_days: r[:threshold_days] } }
|
|
10
26
|
) { PermissionForge::Services::StaleAssignmentAuditor.call(threshold_days: threshold) }
|
|
11
27
|
|
|
12
28
|
if result[:stale_count].zero?
|
|
13
|
-
puts "No stale assignments found (threshold: #{
|
|
29
|
+
puts "No stale assignments found (threshold: #{result[:threshold_days]} days)."
|
|
14
30
|
else
|
|
15
|
-
puts "Found #{result[:stale_count]} stale assignment(s) affecting #{result[:affected_user_ids].size}
|
|
31
|
+
puts "Found #{result[:stale_count]} stale assignment(s) affecting #{result[:affected_user_ids].size} person(s):"
|
|
32
|
+
names = PermissionForge::RakeOutput.people(result[:assignments].map { |a| a[:user_id] })
|
|
16
33
|
result[:assignments].each do |a|
|
|
17
|
-
puts "
|
|
34
|
+
puts " #{names.fetch(a[:user_id].to_s)} — #{a[:permission_set] || '(set not found)'} (#{a[:days_stale]} days since last review or grant)"
|
|
18
35
|
end
|
|
19
36
|
puts "\nConsider reviewing these assignments or setting expires_at for time-bound access."
|
|
20
37
|
exit 1
|
|
@@ -28,12 +45,16 @@ namespace :permission_forge do
|
|
|
28
45
|
# request id, reason and data of every audit event, the *_by_id columns on
|
|
29
46
|
# rows the person created, edited or reviewed, and the person's own record
|
|
30
47
|
# in the app. Assignments in other tenants stay when a tenant is current.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
# rake -T shows a description up to its first full stop after a word: the
|
|
49
|
+
# first sentence carries no abbreviation ("Article 17", not "Art. 17") and
|
|
50
|
+
# ends on a word, not a parenthesis. What it prints is the result the
|
|
51
|
+
# erasure.performed event records (RakeOutput), the cache outcome included.
|
|
52
|
+
desc "Erase a person's permission data (assignments, cache, audit-log names) for a GDPR Article 17 request. " \
|
|
53
|
+
'Ids and other event data stay. USER_ID= required; a dry run unless DRY_RUN=false (or 0, no); ' \
|
|
54
|
+
'ACTOR= names who ran it, REASON= why.'
|
|
34
55
|
task erasure: :environment do
|
|
35
56
|
user_id = ENV.fetch('USER_ID', nil)
|
|
36
|
-
dry_run = ENV.fetch('DRY_RUN',
|
|
57
|
+
dry_run = PermissionForge::RakeDryRun.dry_run?(ENV.fetch('DRY_RUN', nil), default: true)
|
|
37
58
|
|
|
38
59
|
abort 'USER_ID is required. Usage: rake permission_forge:erasure USER_ID=123' if user_id.blank?
|
|
39
60
|
|
|
@@ -51,18 +72,13 @@ namespace :permission_forge do
|
|
|
51
72
|
end
|
|
52
73
|
PermissionForge::RakeAudit.log(task_name, "user #{user_id}, dry_run=#{dry_run}: #{result.except(:permission_sets).to_json}")
|
|
53
74
|
|
|
75
|
+
person = PermissionForge::RakeOutput.person(user)
|
|
54
76
|
if dry_run
|
|
55
|
-
|
|
56
|
-
puts
|
|
57
|
-
puts " Group assignments: #{result[:group_assignments_count]}"
|
|
58
|
-
puts " Permission sets: #{result[:permission_sets].join(', ')}" if result[:permission_sets].any?
|
|
77
|
+
PermissionForge::RakeOutput.report("Dry run — would erase for #{person}:", result)
|
|
78
|
+
puts ' (plus the name labels on the events the erasure itself writes)'
|
|
59
79
|
puts "\nRun with DRY_RUN=false to execute."
|
|
60
80
|
else
|
|
61
|
-
|
|
62
|
-
puts " Assignments removed: #{result[:assignments_removed]}"
|
|
63
|
-
puts " Group assignments removed: #{result[:group_assignments_removed]}"
|
|
64
|
-
puts " Cache cleared: #{result[:cache_cleared]}"
|
|
65
|
-
puts " Assignments remaining in other tenants: #{result[:assignments_remaining_elsewhere]}"
|
|
81
|
+
PermissionForge::RakeOutput.report("Erasure complete for #{person}:", result)
|
|
66
82
|
puts ' Kept: audit event ids, ip, request id, reason and data; *_by_id columns; the user record itself.'
|
|
67
83
|
end
|
|
68
84
|
end
|