From d4b744407b482b7b5ceb6cdf8094e38aca3fefb3 Mon Sep 17 00:00:00 2001 From: smriti Date: Mon, 26 Jun 2023 21:22:16 +0530 Subject: [PATCH 01/10] Application Settings auditor --- ee/app/models/ee/application_setting.rb | 4 + .../ee/application_settings/update_service.rb | 7 + .../application_setting_changes_auditor.rb | 347 ++++++++++++++++++ ...pplication_setting_changes_auditor_spec.rb | 60 +++ 4 files changed, 418 insertions(+) create mode 100644 ee/lib/audit/application_setting_changes_auditor.rb create mode 100644 ee/spec/lib/audit/application_setting_changes_auditor_spec.rb diff --git a/ee/app/models/ee/application_setting.rb b/ee/app/models/ee/application_setting.rb index 8af5fe99165772..63563a2ae772fd 100644 --- a/ee/app/models/ee/application_setting.rb +++ b/ee/app/models/ee/application_setting.rb @@ -519,6 +519,10 @@ def unique_project_download_limit_enabled? false end + def full_path + "ApplicationSettings" + end + private def elasticsearch_limited_project_exists?(project) diff --git a/ee/app/services/ee/application_settings/update_service.rb b/ee/app/services/ee/application_settings/update_service.rb index df799d5222b076..5e4a43cc341935 100644 --- a/ee/app/services/ee/application_settings/update_service.rb +++ b/ee/app/services/ee/application_settings/update_service.rb @@ -27,6 +27,9 @@ def execute update_elasticsearch_containers(ElasticsearchIndexedNamespace, elasticsearch_namespace_ids) update_elasticsearch_containers(ElasticsearchIndexedProject, elasticsearch_project_ids) update_elasticsearch_index_settings(number_of_replicas: elasticsearch_replicas, number_of_shards: elasticsearch_shards) + + log_audit_events + end result @@ -79,6 +82,10 @@ def should_auto_approve_blocked_users? super || user_cap_increased? end + def log_audit_events + Audit::ApplicationSettingChangesAuditor.new(current_user, application_setting).execute + end + def user_cap_increased? return false unless application_setting.previous_changes.key?(:new_user_signups_cap) diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb new file mode 100644 index 00000000000000..93da94840d32c3 --- /dev/null +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -0,0 +1,347 @@ +# frozen_string_literal: true + +module Audit + class ApplicationSettingChangesAuditor < BaseChangesAuditor + COLUMNS_EVENT_TYPE_HASH = { + abuse_notification_email: 'abuse_notification_email', + admin_mode: 'admin_mode', + after_sign_out_path: 'after_sign_out_path', + after_sign_up_text: 'after_sign_up_text', + ai_access_token: 'ai_access_token', + akismet_api_key: 'akismet_api_key', + akismet_enabled: 'akismet_enabled', + allow_local_requests_from_hooks_and_services: 'allow_local_requests_from_hooks_and_services', + allow_local_requests_from_web_hooks_and_services: 'allow_local_requests_from_web_hooks_and_services', + allow_local_requests_from_system_hooks: 'allow_local_requests_from_system_hooks', + allow_possible_spam: 'allow_possible_spam', + ns_rebinding_protection_enabled: 'dns_rebinding_protection_enabled', + rchive_builds_in_human_readable: 'archive_builds_in_human_readable', + sset_proxy_enabled: 'asset_proxy_enabled', + asset_proxy_secret_key: 'asset_proxy_secret_key', + asset_proxy_url: 'asset_proxy_url', + asset_proxy_allowlist: 'asset_proxy_allowlist', + static_objects_external_storage_auth_token: 'static_objects_external_storage_auth_token', + static_objects_external_storage_url: 'static_objects_external_storage_url', + authorized_keys_enabled: 'authorized_keys_enabled', + auto_devops_enabled: 'auto_devops_enabled', + auto_devops_domain: 'auto_devops_domain', + container_expiration_policies_enable_historic_entries: 'container_expiration_policies_enable_historic_entries', + container_registry_expiration_policies_caching: 'container_registry_expiration_policies_caching', + container_registry_token_expire_delay: 'container_registry_token_expire_delay', + default_artifacts_expire_in: 'default_artifacts_expire_in', + default_branch_name: 'default_branch_name', + default_branch_protection: 'default_branch_protection', + default_ci_config_path: 'default_ci_config_path', + default_group_visibility: 'default_group_visibility', + default_preferred_language: 'default_preferred_language', + default_project_creation: 'default_project_creation', + default_project_visibility: 'default_project_visibility', + default_projects_limit: 'default_projects_limit', + default_snippet_visibility: 'default_snippet_visibility', + default_syntax_highlighting_theme: 'default_syntax_highlighting_theme', + delete_inactive_projects: 'delete_inactive_projects', + deny_all_requests_except_allowed: 'deny_all_requests_except_allowed', + disable_admin_oauth_scopes: 'disable_admin_oauth_scopes', + disable_feed_token: 'disable_feed_token', + disabled_oauth_sign_in_sources: 'disabled_oauth_sign_in_sources', + omain_denylist: 'domain_denylist', + omain_denylist_enabled: 'domain_denylist_enabled', + # TODO Remove domain_denylist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204) + omain_denylist_raw: 'domain_denylist_raw', + omain_allowlist: 'domain_allowlist', + # TODO Remove domain_allowlist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204) + domain_allowlist_raw: 'domain_allowlist_raw', + outbound_local_requests_allowlist_raw: 'outbound_local_requests_allowlist_raw', + dsa_key_restriction: 'dsa_key_restriction', + ecdsa_key_restriction: 'ecdsa_key_restriction', + ecdsa_sk_key_restriction: 'ecdsa_sk_key_restriction', + ed25519_key_restriction: 'ed25519_key_restriction', + ed25519_sk_key_restriction: 'ed25519_sk_key_restriction', + eks_integration_enabled: 'eks_integration_enabled', + eks_account_id: 'eks_account_id', + eks_access_key_id: 'eks_access_key_id', + eks_secret_access_key: 'eks_secret_access_key', + email_author_in_body: 'email_author_in_body', + email_confirmation_setting: 'email_confirmation_setting', + enabled_git_access_protocol: 'enabled_git_access_protocol', + enforce_terms: 'enforce_terms', + error_tracking_enabled: 'error_tracking_enabled', + error_tracking_api_url: 'error_tracking_api_url', + external_pipeline_validation_service_timeout: 'external_pipeline_validation_service_timeout', + external_pipeline_validation_service_token: 'external_pipeline_validation_service_token', + external_pipeline_validation_service_url: 'external_pipeline_validation_service_url', + first_day_of_week: 'first_day_of_week', + floc_enabled: 'floc_enabled', + force_pages_access_control: 'force_pages_access_control', + gitaly_timeout_default: 'gitaly_timeout_default', + gitaly_timeout_medium: 'gitaly_timeout_medium', + gitaly_timeout_fast: 'gitaly_timeout_fast', + gitpod_enabled: 'gitpod_enabled', + gitpod_url: 'gitpod_url', + grafana_enabled: 'grafana_enabled', + grafana_url: 'grafana_url', + gravatar_enabled: 'gravatar_enabled', + hashed_storage_enabled: 'hashed_storage_enabled', + help_page_hide_commercial_content: 'help_page_hide_commercial_content', + help_page_support_url: 'help_page_support_url', + help_page_documentation_base_url: 'help_page_documentation_base_url', + help_page_text: 'help_page_text', + hide_third_party_offers: 'hide_third_party_offers', + home_page_url: 'home_page_url', + housekeeping_enabled: 'housekeeping_enabled', + housekeeping_full_repack_period: 'housekeeping_full_repack_period', + housekeeping_gc_period: 'housekeeping_gc_period', + housekeeping_incremental_repack_period: 'housekeeping_incremental_repack_period', + housekeeping_optimize_repository_period: 'housekeeping_optimize_repository_period', + html_emails_enabled: 'html_emails_enabled', + import_sources: 'import_sources', + in_product_marketing_emails_enabled: 'in_product_marketing_emails_enabled', + inactive_projects_delete_after_months: 'inactive_projects_delete_after_months', + inactive_projects_min_size_mb: 'inactive_projects_min_size_mb', + inactive_projects_send_warning_email_after_months: 'inactive_projects_send_warning_email_after_months', + instance_level_code_suggestions_enabled: 'instance_level_code_suggestions_enabled', + invisible_captcha_enabled: 'invisible_captcha_enabled', + jira_connect_application_key: 'jira_connect_application_key', + jira_connect_public_key_storage_enabled: 'jira_connect_public_key_storage_enabled', + jira_connect_proxy_url: 'jira_connect_proxy_url', + max_artifacts_size: 'max_artifacts_size', + max_attachment_size: 'max_attachment_size', + max_export_size: 'max_export_size', + max_import_size: 'max_import_size', + max_pages_size: 'max_pages_size', + max_pages_custom_domains_per_project: 'max_pages_custom_domains_per_project', + max_terraform_state_size_bytes: 'max_terraform_state_size_bytes', + max_yaml_size_bytes: 'max_yaml_size_bytes', + max_yaml_depth: 'max_yaml_depth', + metrics_method_call_threshold: 'metrics_method_call_threshold', + minimum_password_length: 'minimum_password_length', + mirror_available: 'mirror_available', + notify_on_unknown_sign_in: 'notify_on_unknown_sign_in', + pages_domain_verification_enabled: 'pages_domain_verification_enabled', + password_authentication_enabled_for_web: 'password_authentication_enabled_for_web', + password_authentication_enabled_for_git: 'password_authentication_enabled_for_git', + performance_bar_allowed_group_path: 'performance_bar_allowed_group_path', + personal_access_token_prefix: 'personal_access_token_prefix', + performance_bar_enabled: 'performance_bar_enabled', + kroki_url: 'kroki_url', + kroki_enabled: 'kroki_enabled', + kroki_formats: 'kroki_formats', + plantuml_enabled: 'plantuml_enabled', + plantuml_url: 'plantuml_url', + diagramsnet_enabled: 'diagramsnet_enabled', + diagramsnet_url: 'diagramsnet_url', + polling_interval_multiplier: 'polling_interval_multiplier', + project_export_enabled: 'project_export_enabled', + prometheus_metrics_enabled: 'prometheus_metrics_enabled', + recaptcha_enabled: 'recaptcha_enabled', + recaptcha_private_key: 'recaptcha_private_key', + recaptcha_site_key: 'recaptcha_site_key', + login_recaptcha_protection_enabled: 'login_recaptcha_protection_enabled', + receive_max_input_size: 'receive_max_input_size', + repository_checks_enabled: 'repository_checks_enabled', + repository_storages_weighted: 'repository_storages_weighted', + require_admin_approval_after_user_signup: 'require_admin_approval_after_user_signup', + require_two_factor_authentication: 'require_two_factor_authentication', + remember_me_enabled: 'remember_me_enabled', + restricted_visibility_levels: 'restricted_visibility_levels', + rsa_key_restriction: 'rsa_key_restriction', + session_expire_delay: 'session_expire_delay', + shared_runners_enabled: 'shared_runners_enabled', + shared_runners_text: 'shared_runners_text', + sign_in_text: 'sign_in_text', + signup_enabled: 'signup_enabled', + silent_mode_enabled: 'silent_mode_enabled', + slack_app_enabled: 'slack_app_enabled', + slack_app_id: 'slack_app_id', + slack_app_secret: 'slack_app_secret', + slack_app_signing_secret: 'slack_app_signing_secret', + slack_app_verification_token: 'slack_app_verification_token', + sourcegraph_enabled: 'sourcegraph_enabled', + sourcegraph_url: 'sourcegraph_url', + sourcegraph_public_only: 'sourcegraph_public_only', + spam_check_endpoint_enabled: 'spam_check_endpoint_enabled', + spam_check_endpoint_url: 'spam_check_endpoint_url', + spam_check_api_key: 'spam_check_api_key', + terminal_max_session_time: 'terminal_max_session_time', + terms: 'terms', + throttle_authenticated_api_enabled: 'throttle_authenticated_api_enabled', + throttle_authenticated_api_period_in_seconds: 'throttle_authenticated_api_period_in_seconds', + throttle_authenticated_api_requests_per_period: 'throttle_authenticated_api_requests_per_period', + throttle_authenticated_git_lfs_enabled: 'throttle_authenticated_git_lfs_enabled', + throttle_authenticated_git_lfs_period_in_seconds: 'throttle_authenticated_git_lfs_period_in_seconds', + throttle_authenticated_git_lfs_requests_per_period: 'throttle_authenticated_git_lfs_requests_per_period', + throttle_authenticated_web_enabled: 'throttle_authenticated_web_enabled', + throttle_authenticated_web_period_in_seconds: 'throttle_authenticated_web_period_in_seconds', + throttle_authenticated_web_requests_per_period: 'throttle_authenticated_web_requests_per_period', + throttle_authenticated_packages_api_enabled: 'throttle_authenticated_packages_api_enabled', + throttle_authenticated_packages_api_period_in_seconds: 'throttle_authenticated_packages_api_period_in_seconds', + throttle_authenticated_packages_api_requests_per_period: + 'throttle_authenticated_packages_api_requests_per_period', + throttle_authenticated_files_api_enabled: 'throttle_authenticated_files_api_enabled', + throttle_authenticated_files_api_period_in_seconds: 'throttle_authenticated_files_api_period_in_seconds', + throttle_authenticated_files_api_requests_per_period: 'throttle_authenticated_files_api_requests_per_period', + throttle_authenticated_deprecated_api_enabled: 'throttle_authenticated_deprecated_api_enabled', + throttle_authenticated_deprecated_api_period_in_seconds: + 'throttle_authenticated_deprecated_api_period_in_seconds', + throttle_authenticated_deprecated_api_requests_per_period: + 'throttle_authenticated_deprecated_api_requests_per_period', + throttle_unauthenticated_api_enabled: 'throttle_unauthenticated_api_enabled', + throttle_unauthenticated_api_period_in_seconds: 'throttle_unauthenticated_api_period_in_seconds', + throttle_unauthenticated_api_requests_per_period: 'throttle_unauthenticated_api_requests_per_period', + throttle_unauthenticated_enabled: 'throttle_unauthenticated_enabled', + throttle_unauthenticated_period_in_seconds: 'throttle_unauthenticated_period_in_seconds', + throttle_unauthenticated_requests_per_period: 'throttle_unauthenticated_requests_per_period', + throttle_unauthenticated_packages_api_enabled: 'throttle_unauthenticated_packages_api_enable', + throttle_unauthenticated_packages_api_period_in_seconds: + 'throttle_unauthenticated_packages_api_period_in_seconds', + throttle_unauthenticated_packages_api_requests_per_period: + 'throttle_unauthenticated_packages_api_requests_per_period', + throttle_unauthenticated_files_api_enabled: 'throttle_unauthenticated_files_api_enable', + throttle_unauthenticated_files_api_period_in_seconds: 'throttle_unauthenticated_files_api_period_in_second', + throttle_unauthenticated_files_api_requests_per_period: + 'throttle_unauthenticated_files_api_requests_per_period', + throttle_unauthenticated_deprecated_api_enabled: 'throttle_unauthenticated_deprecated_api_enable', + throttle_unauthenticated_deprecated_api_period_in_seconds: + 'throttle_unauthenticated_deprecated_api_period_in_second', + throttle_unauthenticated_deprecated_api_requests_per_period: + 'throttle_unauthenticated_deprecated_api_requests_per_period', + throttle_protected_paths_enabled: 'throttle_protected_paths_enable', + throttle_protected_paths_period_in_seconds: 'throttle_protected_paths_period_in_second', + throttle_protected_paths_requests_per_period: 'throttle_protected_paths_requests_per_perio', + protected_paths_raw: 'protected_paths_ra', + time_tracking_limit_to_hours: 'time_tracking_limit_to_hour', + two_factor_grace_period: 'two_factor_grace_perio', + update_runner_versions_enabled: 'update_runner_versions_enable', + unique_ips_limit_enabled: 'unique_ips_limit_enable', + unique_ips_limit_per_user: 'unique_ips_limit_per_use', + unique_ips_limit_time_window: 'unique_ips_limit_time_windo', + usage_ping_enabled: 'usage_ping_enable', + usage_ping_features_enabled: 'usage_ping_features_enable', + user_default_external: 'user_default_externa', + user_show_add_ssh_key_message: 'user_show_add_ssh_key_messag', + user_default_internal_regex: 'user_default_internal_rege', + user_oauth_applications: 'user_oauth_application', + version_check_enabled: 'version_check_enable', + diff_max_patch_bytes: 'diff_max_patch_byte', + diff_max_files: 'diff_max_file', + diff_max_lines: 'diff_max_line', + commit_email_hostname: 'commit_email_hostnam', + protected_ci_variables: 'protected_ci_variable', + local_markdown_version: 'local_markdown_versio', + mailgun_signing_key: 'mailgun_signing_ke', + mailgun_events_enabled: 'mailgun_events_enable', + snowplow_collector_hostname: 'snowplow_collector_hostnam', + snowplow_cookie_domain: 'snowplow_cookie_domai', + snowplow_enabled: 'snowplow_enable', + snowplow_app_id: 'snowplow_app_id', + push_event_hooks_limit: 'push_event_hooks_limit', + push_event_activities_limit: 'push_event_activities_limit', + custom_http_clone_url_root: 'custom_http_clone_url_root', + snippet_size_limit: 'snippet_size_limit', + email_restrictions_enabled: 'email_restrictions_enabled', + email_restrictions: 'email_restrictions', + issues_create_limit: 'issues_create_limit', + notes_create_limit: 'notes_create_limit', + notes_create_limit_allowlist_raw: 'notes_create_limit_allowlist_raw', + raw_blob_request_limit: 'raw_blob_request_limit', + project_import_limit: 'project_import_limit', + project_export_limit: 'project_export_limit', + project_download_export_limit: 'project_download_export_limit', + group_import_limit: 'group_import_limit', + group_export_limit: 'group_export_limit', + group_download_export_limit: 'group_download_export_limit', + wiki_page_max_content_bytes: 'wiki_page_max_content_bytes', + wiki_asciidoc_allow_uri_includes: 'wiki_asciidoc_allow_uri_includes', + container_registry_delete_tags_service_timeout: 'container_registry_delete_tags_service_timeout', + rate_limiting_response_text: 'rate_limiting_response_text', + package_registry_cleanup_policies_worker_capacity: 'package_registry_cleanup_policies_worker_capacity', + container_registry_expiration_policies_worker_capacity: 'container_registry_expiration_policies_worker_capacity', + container_registry_cleanup_tags_service_max_list_size: 'container_registry_cleanup_tags_service_max_list_size', + container_registry_import_max_tags_count: 'container_registry_import_max_tags_count', + container_registry_import_max_retries: 'container_registry_import_max_retries', + container_registry_import_start_max_retries: 'container_registry_import_start_max_retries', + container_registry_import_max_step_duration: 'container_registry_import_max_step_duration', + container_registry_pre_import_tags_rate: 'container_registry_pre_import_tags_rate', + container_registry_pre_import_timeout: 'container_registry_pre_import_timeout', + container_registry_import_timeout: 'container_registry_import_timeout', + container_registry_import_target_plan: 'container_registry_import_target_plan', + container_registry_import_created_before: 'container_registry_import_created_before', + keep_latest_artifact: 'keep_latest_artifact', + whats_new_variant: 'whats_new_variant', + user_deactivation_emails_enabled: 'user_deactivation_emails_enabled', + sentry_enabled: 'sentry_enabled', + sentry_dsn: 'sentry_dsn', + sentry_clientside_dsn: 'sentry_clientside_dsn', + sentry_environment: 'sentry_environment', + sidekiq_job_limiter_mode: 'sidekiq_job_limiter_mode', + sidekiq_job_limiter_compression_threshold_bytes: 'sidekiq_job_limiter_compression_threshold_bytes', + sidekiq_job_limiter_limit_bytes: 'sidekiq_job_limiter_limit_bytes', + suggest_pipeline_enabled: 'suggest_pipeline_enabled', + search_rate_limit: 'search_rate_limit', + search_rate_limit_unauthenticated: 'search_rate_limit_unauthenticated', + users_get_by_id_limit: 'users_get_by_id_limit', + users_get_by_id_limit_allowlist_raw: 'users_get_by_id_limit_allowlist_raw', + runner_token_expiration_interval: 'runner_token_expiration_interval', + group_runner_token_expiration_interval: 'group_runner_token_expiration_interval', + project_runner_token_expiration_interval: 'project_runner_token_expiration_interval', + pipeline_limit_per_project_user_sha: 'pipeline_limit_per_project_user_sha', + invitation_flow_enforcement: 'invitation_flow_enforcement', + can_create_group: 'can_create_group', + bulk_import_enabled: 'bulk_import_enabled', + allow_runner_registration_token: 'allow_runner_registration_token', + user_defaults_to_private_profile: 'user_defaults_to_private_profile', + deactivation_email_additional_text: 'deactivation_email_additional_text', + projects_api_rate_limit_unauthenticated: 'projects_api_rate_limit_unauthenticated', + gitlab_dedicated_instance: 'gitlab_dedicated_instance', + ci_max_includes: 'ci_max_includes', + allow_account_deletion: 'allow_account_deletion', + external_auth_client_cert: 'external_auth_client_cert', + external_auth_client_key: 'external_auth_client_key', + external_auth_client_key_pass: 'external_auth_client_key_pass', + external_authorization_service_default_label: 'external_authorization_service_default_label', + external_authorization_service_enabled: 'external_authorization_service_enabled', + external_authorization_service_timeout: 'external_authorization_service_timeout', + external_authorization_service_url: 'external_authorization_service_url', + allow_deploy_tokens_and_keys_with_external_auth: 'allow_deploy_tokens_and_keys_with_external_auth', + lets_encrypt_notification_email: 'lets_encrypt_notification_email', + lets_encrypt_terms_of_service_accepted: 'lets_encrypt_terms_of_service_accepted', + domain_denylist_file: 'domain_denylist_file', + email_additional_text: 'email_additional_text', + file_template_project_id: 'file_template_project_id', + git_two_factor_session_expiry: 'git_two_factor_session_expiry', + group_owners_can_manage_default_branch_protection: 'group_owners_can_manage_default_branch_protection', + default_project_deletion_protection: 'default_project_deletion_protection', + disable_personal_access_tokens: 'disable_personal_access_tokens', + deletion_adjourned_period: 'deletion_adjourned_period', + updating_name_disabled_for_users: 'updating_name_disabled_for_users', + maven_package_requests_forwarding: 'maven_package_requests_forwarding', + npm_package_requests_forwarding: 'npm_package_requests_forwarding', + pypi_package_requests_forwarding: 'pypi_package_requests_forwarding', + maintenance_mode: 'maintenance_mode', + maintenance_mode_message: 'maintenance_mode_message', + globally_allowed_ips: 'globally_allowed_ips' + }.freeze + + def execute + return if model.blank? + + COLUMNS_EVENT_TYPE_HASH.each do |column, event_name| + audit_changes(column, as: "ApplicationSettings #{column}", model: model, event_type: event_name) + end + end + + private + + # def audit_method + + # end + + def attributes_from_auditable_model(column) + { + from: model.previous_changes[column].first, + to: model.previous_changes[column].last, + target_details: @current_user.full_path + } + end + end +end diff --git a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb new file mode 100644 index 00000000000000..fe6c86fe681032 --- /dev/null +++ b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Audit::ApplicationSettingChangesAuditor, feature_category: :audit_events do + using RSpec::Parameterized::TableSyntax + + describe '.audit_changes' do + let_it_be(:user) { create(:user) } + let(:application_setting) { ApplicationSetting.create_from_defaults } + + subject(:auditor_instance) { described_class.new(user, application_setting) } + + shared_examples 'application_setting_audit_events_from_to' do + it 'calls auditor' do + expect(Gitlab::Audit::Auditor).to receive(:audit).with( + { + name: event.to_s, + author: user, + scope: application_setting, + target: application_setting, + message: "Changed ApplicationSetting #{change} from #{change_from} to #{change_to}", + additional_details: { + change: change.to_s, + from: change_from, + target_details: application_setting.full_path.to_s, + to: change_to + }, + target_details: application_setting.full_path.to_s + } + ).and_call_original + + auditor_instance.execute + end + end + + shared_examples 'application_setting_audit_events_to' do + it 'calls auditor' do + expect(Gitlab::Audit::Auditor).to receive(:audit).with( + { + name: event.to_s, + author: user, + scope: application_setting, + target: application_setting, + message: "Changed #{change} to #{change_to}", + additional_details: { + change: change.to_s, + from: change_from, + target_details: application_setting.full_path.to_s, + to: change_to + }, + target_details: application_setting.full_path.to_s + } + ).and_call_original + + auditor_instance.execute + end + end + end +end -- GitLab From 0c87577f37e37a071b950cc9faa0669046eafa1b Mon Sep 17 00:00:00 2001 From: smriti Date: Mon, 26 Jun 2023 22:23:11 +0530 Subject: [PATCH 02/10] Iterate only over changed columns --- .../application_setting_changes_auditor.rb | 326 +----------------- 1 file changed, 4 insertions(+), 322 deletions(-) diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index 93da94840d32c3..905b71b3df6eea 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -2,331 +2,13 @@ module Audit class ApplicationSettingChangesAuditor < BaseChangesAuditor - COLUMNS_EVENT_TYPE_HASH = { - abuse_notification_email: 'abuse_notification_email', - admin_mode: 'admin_mode', - after_sign_out_path: 'after_sign_out_path', - after_sign_up_text: 'after_sign_up_text', - ai_access_token: 'ai_access_token', - akismet_api_key: 'akismet_api_key', - akismet_enabled: 'akismet_enabled', - allow_local_requests_from_hooks_and_services: 'allow_local_requests_from_hooks_and_services', - allow_local_requests_from_web_hooks_and_services: 'allow_local_requests_from_web_hooks_and_services', - allow_local_requests_from_system_hooks: 'allow_local_requests_from_system_hooks', - allow_possible_spam: 'allow_possible_spam', - ns_rebinding_protection_enabled: 'dns_rebinding_protection_enabled', - rchive_builds_in_human_readable: 'archive_builds_in_human_readable', - sset_proxy_enabled: 'asset_proxy_enabled', - asset_proxy_secret_key: 'asset_proxy_secret_key', - asset_proxy_url: 'asset_proxy_url', - asset_proxy_allowlist: 'asset_proxy_allowlist', - static_objects_external_storage_auth_token: 'static_objects_external_storage_auth_token', - static_objects_external_storage_url: 'static_objects_external_storage_url', - authorized_keys_enabled: 'authorized_keys_enabled', - auto_devops_enabled: 'auto_devops_enabled', - auto_devops_domain: 'auto_devops_domain', - container_expiration_policies_enable_historic_entries: 'container_expiration_policies_enable_historic_entries', - container_registry_expiration_policies_caching: 'container_registry_expiration_policies_caching', - container_registry_token_expire_delay: 'container_registry_token_expire_delay', - default_artifacts_expire_in: 'default_artifacts_expire_in', - default_branch_name: 'default_branch_name', - default_branch_protection: 'default_branch_protection', - default_ci_config_path: 'default_ci_config_path', - default_group_visibility: 'default_group_visibility', - default_preferred_language: 'default_preferred_language', - default_project_creation: 'default_project_creation', - default_project_visibility: 'default_project_visibility', - default_projects_limit: 'default_projects_limit', - default_snippet_visibility: 'default_snippet_visibility', - default_syntax_highlighting_theme: 'default_syntax_highlighting_theme', - delete_inactive_projects: 'delete_inactive_projects', - deny_all_requests_except_allowed: 'deny_all_requests_except_allowed', - disable_admin_oauth_scopes: 'disable_admin_oauth_scopes', - disable_feed_token: 'disable_feed_token', - disabled_oauth_sign_in_sources: 'disabled_oauth_sign_in_sources', - omain_denylist: 'domain_denylist', - omain_denylist_enabled: 'domain_denylist_enabled', - # TODO Remove domain_denylist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204) - omain_denylist_raw: 'domain_denylist_raw', - omain_allowlist: 'domain_allowlist', - # TODO Remove domain_allowlist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204) - domain_allowlist_raw: 'domain_allowlist_raw', - outbound_local_requests_allowlist_raw: 'outbound_local_requests_allowlist_raw', - dsa_key_restriction: 'dsa_key_restriction', - ecdsa_key_restriction: 'ecdsa_key_restriction', - ecdsa_sk_key_restriction: 'ecdsa_sk_key_restriction', - ed25519_key_restriction: 'ed25519_key_restriction', - ed25519_sk_key_restriction: 'ed25519_sk_key_restriction', - eks_integration_enabled: 'eks_integration_enabled', - eks_account_id: 'eks_account_id', - eks_access_key_id: 'eks_access_key_id', - eks_secret_access_key: 'eks_secret_access_key', - email_author_in_body: 'email_author_in_body', - email_confirmation_setting: 'email_confirmation_setting', - enabled_git_access_protocol: 'enabled_git_access_protocol', - enforce_terms: 'enforce_terms', - error_tracking_enabled: 'error_tracking_enabled', - error_tracking_api_url: 'error_tracking_api_url', - external_pipeline_validation_service_timeout: 'external_pipeline_validation_service_timeout', - external_pipeline_validation_service_token: 'external_pipeline_validation_service_token', - external_pipeline_validation_service_url: 'external_pipeline_validation_service_url', - first_day_of_week: 'first_day_of_week', - floc_enabled: 'floc_enabled', - force_pages_access_control: 'force_pages_access_control', - gitaly_timeout_default: 'gitaly_timeout_default', - gitaly_timeout_medium: 'gitaly_timeout_medium', - gitaly_timeout_fast: 'gitaly_timeout_fast', - gitpod_enabled: 'gitpod_enabled', - gitpod_url: 'gitpod_url', - grafana_enabled: 'grafana_enabled', - grafana_url: 'grafana_url', - gravatar_enabled: 'gravatar_enabled', - hashed_storage_enabled: 'hashed_storage_enabled', - help_page_hide_commercial_content: 'help_page_hide_commercial_content', - help_page_support_url: 'help_page_support_url', - help_page_documentation_base_url: 'help_page_documentation_base_url', - help_page_text: 'help_page_text', - hide_third_party_offers: 'hide_third_party_offers', - home_page_url: 'home_page_url', - housekeeping_enabled: 'housekeeping_enabled', - housekeeping_full_repack_period: 'housekeeping_full_repack_period', - housekeeping_gc_period: 'housekeeping_gc_period', - housekeeping_incremental_repack_period: 'housekeeping_incremental_repack_period', - housekeeping_optimize_repository_period: 'housekeeping_optimize_repository_period', - html_emails_enabled: 'html_emails_enabled', - import_sources: 'import_sources', - in_product_marketing_emails_enabled: 'in_product_marketing_emails_enabled', - inactive_projects_delete_after_months: 'inactive_projects_delete_after_months', - inactive_projects_min_size_mb: 'inactive_projects_min_size_mb', - inactive_projects_send_warning_email_after_months: 'inactive_projects_send_warning_email_after_months', - instance_level_code_suggestions_enabled: 'instance_level_code_suggestions_enabled', - invisible_captcha_enabled: 'invisible_captcha_enabled', - jira_connect_application_key: 'jira_connect_application_key', - jira_connect_public_key_storage_enabled: 'jira_connect_public_key_storage_enabled', - jira_connect_proxy_url: 'jira_connect_proxy_url', - max_artifacts_size: 'max_artifacts_size', - max_attachment_size: 'max_attachment_size', - max_export_size: 'max_export_size', - max_import_size: 'max_import_size', - max_pages_size: 'max_pages_size', - max_pages_custom_domains_per_project: 'max_pages_custom_domains_per_project', - max_terraform_state_size_bytes: 'max_terraform_state_size_bytes', - max_yaml_size_bytes: 'max_yaml_size_bytes', - max_yaml_depth: 'max_yaml_depth', - metrics_method_call_threshold: 'metrics_method_call_threshold', - minimum_password_length: 'minimum_password_length', - mirror_available: 'mirror_available', - notify_on_unknown_sign_in: 'notify_on_unknown_sign_in', - pages_domain_verification_enabled: 'pages_domain_verification_enabled', - password_authentication_enabled_for_web: 'password_authentication_enabled_for_web', - password_authentication_enabled_for_git: 'password_authentication_enabled_for_git', - performance_bar_allowed_group_path: 'performance_bar_allowed_group_path', - personal_access_token_prefix: 'personal_access_token_prefix', - performance_bar_enabled: 'performance_bar_enabled', - kroki_url: 'kroki_url', - kroki_enabled: 'kroki_enabled', - kroki_formats: 'kroki_formats', - plantuml_enabled: 'plantuml_enabled', - plantuml_url: 'plantuml_url', - diagramsnet_enabled: 'diagramsnet_enabled', - diagramsnet_url: 'diagramsnet_url', - polling_interval_multiplier: 'polling_interval_multiplier', - project_export_enabled: 'project_export_enabled', - prometheus_metrics_enabled: 'prometheus_metrics_enabled', - recaptcha_enabled: 'recaptcha_enabled', - recaptcha_private_key: 'recaptcha_private_key', - recaptcha_site_key: 'recaptcha_site_key', - login_recaptcha_protection_enabled: 'login_recaptcha_protection_enabled', - receive_max_input_size: 'receive_max_input_size', - repository_checks_enabled: 'repository_checks_enabled', - repository_storages_weighted: 'repository_storages_weighted', - require_admin_approval_after_user_signup: 'require_admin_approval_after_user_signup', - require_two_factor_authentication: 'require_two_factor_authentication', - remember_me_enabled: 'remember_me_enabled', - restricted_visibility_levels: 'restricted_visibility_levels', - rsa_key_restriction: 'rsa_key_restriction', - session_expire_delay: 'session_expire_delay', - shared_runners_enabled: 'shared_runners_enabled', - shared_runners_text: 'shared_runners_text', - sign_in_text: 'sign_in_text', - signup_enabled: 'signup_enabled', - silent_mode_enabled: 'silent_mode_enabled', - slack_app_enabled: 'slack_app_enabled', - slack_app_id: 'slack_app_id', - slack_app_secret: 'slack_app_secret', - slack_app_signing_secret: 'slack_app_signing_secret', - slack_app_verification_token: 'slack_app_verification_token', - sourcegraph_enabled: 'sourcegraph_enabled', - sourcegraph_url: 'sourcegraph_url', - sourcegraph_public_only: 'sourcegraph_public_only', - spam_check_endpoint_enabled: 'spam_check_endpoint_enabled', - spam_check_endpoint_url: 'spam_check_endpoint_url', - spam_check_api_key: 'spam_check_api_key', - terminal_max_session_time: 'terminal_max_session_time', - terms: 'terms', - throttle_authenticated_api_enabled: 'throttle_authenticated_api_enabled', - throttle_authenticated_api_period_in_seconds: 'throttle_authenticated_api_period_in_seconds', - throttle_authenticated_api_requests_per_period: 'throttle_authenticated_api_requests_per_period', - throttle_authenticated_git_lfs_enabled: 'throttle_authenticated_git_lfs_enabled', - throttle_authenticated_git_lfs_period_in_seconds: 'throttle_authenticated_git_lfs_period_in_seconds', - throttle_authenticated_git_lfs_requests_per_period: 'throttle_authenticated_git_lfs_requests_per_period', - throttle_authenticated_web_enabled: 'throttle_authenticated_web_enabled', - throttle_authenticated_web_period_in_seconds: 'throttle_authenticated_web_period_in_seconds', - throttle_authenticated_web_requests_per_period: 'throttle_authenticated_web_requests_per_period', - throttle_authenticated_packages_api_enabled: 'throttle_authenticated_packages_api_enabled', - throttle_authenticated_packages_api_period_in_seconds: 'throttle_authenticated_packages_api_period_in_seconds', - throttle_authenticated_packages_api_requests_per_period: - 'throttle_authenticated_packages_api_requests_per_period', - throttle_authenticated_files_api_enabled: 'throttle_authenticated_files_api_enabled', - throttle_authenticated_files_api_period_in_seconds: 'throttle_authenticated_files_api_period_in_seconds', - throttle_authenticated_files_api_requests_per_period: 'throttle_authenticated_files_api_requests_per_period', - throttle_authenticated_deprecated_api_enabled: 'throttle_authenticated_deprecated_api_enabled', - throttle_authenticated_deprecated_api_period_in_seconds: - 'throttle_authenticated_deprecated_api_period_in_seconds', - throttle_authenticated_deprecated_api_requests_per_period: - 'throttle_authenticated_deprecated_api_requests_per_period', - throttle_unauthenticated_api_enabled: 'throttle_unauthenticated_api_enabled', - throttle_unauthenticated_api_period_in_seconds: 'throttle_unauthenticated_api_period_in_seconds', - throttle_unauthenticated_api_requests_per_period: 'throttle_unauthenticated_api_requests_per_period', - throttle_unauthenticated_enabled: 'throttle_unauthenticated_enabled', - throttle_unauthenticated_period_in_seconds: 'throttle_unauthenticated_period_in_seconds', - throttle_unauthenticated_requests_per_period: 'throttle_unauthenticated_requests_per_period', - throttle_unauthenticated_packages_api_enabled: 'throttle_unauthenticated_packages_api_enable', - throttle_unauthenticated_packages_api_period_in_seconds: - 'throttle_unauthenticated_packages_api_period_in_seconds', - throttle_unauthenticated_packages_api_requests_per_period: - 'throttle_unauthenticated_packages_api_requests_per_period', - throttle_unauthenticated_files_api_enabled: 'throttle_unauthenticated_files_api_enable', - throttle_unauthenticated_files_api_period_in_seconds: 'throttle_unauthenticated_files_api_period_in_second', - throttle_unauthenticated_files_api_requests_per_period: - 'throttle_unauthenticated_files_api_requests_per_period', - throttle_unauthenticated_deprecated_api_enabled: 'throttle_unauthenticated_deprecated_api_enable', - throttle_unauthenticated_deprecated_api_period_in_seconds: - 'throttle_unauthenticated_deprecated_api_period_in_second', - throttle_unauthenticated_deprecated_api_requests_per_period: - 'throttle_unauthenticated_deprecated_api_requests_per_period', - throttle_protected_paths_enabled: 'throttle_protected_paths_enable', - throttle_protected_paths_period_in_seconds: 'throttle_protected_paths_period_in_second', - throttle_protected_paths_requests_per_period: 'throttle_protected_paths_requests_per_perio', - protected_paths_raw: 'protected_paths_ra', - time_tracking_limit_to_hours: 'time_tracking_limit_to_hour', - two_factor_grace_period: 'two_factor_grace_perio', - update_runner_versions_enabled: 'update_runner_versions_enable', - unique_ips_limit_enabled: 'unique_ips_limit_enable', - unique_ips_limit_per_user: 'unique_ips_limit_per_use', - unique_ips_limit_time_window: 'unique_ips_limit_time_windo', - usage_ping_enabled: 'usage_ping_enable', - usage_ping_features_enabled: 'usage_ping_features_enable', - user_default_external: 'user_default_externa', - user_show_add_ssh_key_message: 'user_show_add_ssh_key_messag', - user_default_internal_regex: 'user_default_internal_rege', - user_oauth_applications: 'user_oauth_application', - version_check_enabled: 'version_check_enable', - diff_max_patch_bytes: 'diff_max_patch_byte', - diff_max_files: 'diff_max_file', - diff_max_lines: 'diff_max_line', - commit_email_hostname: 'commit_email_hostnam', - protected_ci_variables: 'protected_ci_variable', - local_markdown_version: 'local_markdown_versio', - mailgun_signing_key: 'mailgun_signing_ke', - mailgun_events_enabled: 'mailgun_events_enable', - snowplow_collector_hostname: 'snowplow_collector_hostnam', - snowplow_cookie_domain: 'snowplow_cookie_domai', - snowplow_enabled: 'snowplow_enable', - snowplow_app_id: 'snowplow_app_id', - push_event_hooks_limit: 'push_event_hooks_limit', - push_event_activities_limit: 'push_event_activities_limit', - custom_http_clone_url_root: 'custom_http_clone_url_root', - snippet_size_limit: 'snippet_size_limit', - email_restrictions_enabled: 'email_restrictions_enabled', - email_restrictions: 'email_restrictions', - issues_create_limit: 'issues_create_limit', - notes_create_limit: 'notes_create_limit', - notes_create_limit_allowlist_raw: 'notes_create_limit_allowlist_raw', - raw_blob_request_limit: 'raw_blob_request_limit', - project_import_limit: 'project_import_limit', - project_export_limit: 'project_export_limit', - project_download_export_limit: 'project_download_export_limit', - group_import_limit: 'group_import_limit', - group_export_limit: 'group_export_limit', - group_download_export_limit: 'group_download_export_limit', - wiki_page_max_content_bytes: 'wiki_page_max_content_bytes', - wiki_asciidoc_allow_uri_includes: 'wiki_asciidoc_allow_uri_includes', - container_registry_delete_tags_service_timeout: 'container_registry_delete_tags_service_timeout', - rate_limiting_response_text: 'rate_limiting_response_text', - package_registry_cleanup_policies_worker_capacity: 'package_registry_cleanup_policies_worker_capacity', - container_registry_expiration_policies_worker_capacity: 'container_registry_expiration_policies_worker_capacity', - container_registry_cleanup_tags_service_max_list_size: 'container_registry_cleanup_tags_service_max_list_size', - container_registry_import_max_tags_count: 'container_registry_import_max_tags_count', - container_registry_import_max_retries: 'container_registry_import_max_retries', - container_registry_import_start_max_retries: 'container_registry_import_start_max_retries', - container_registry_import_max_step_duration: 'container_registry_import_max_step_duration', - container_registry_pre_import_tags_rate: 'container_registry_pre_import_tags_rate', - container_registry_pre_import_timeout: 'container_registry_pre_import_timeout', - container_registry_import_timeout: 'container_registry_import_timeout', - container_registry_import_target_plan: 'container_registry_import_target_plan', - container_registry_import_created_before: 'container_registry_import_created_before', - keep_latest_artifact: 'keep_latest_artifact', - whats_new_variant: 'whats_new_variant', - user_deactivation_emails_enabled: 'user_deactivation_emails_enabled', - sentry_enabled: 'sentry_enabled', - sentry_dsn: 'sentry_dsn', - sentry_clientside_dsn: 'sentry_clientside_dsn', - sentry_environment: 'sentry_environment', - sidekiq_job_limiter_mode: 'sidekiq_job_limiter_mode', - sidekiq_job_limiter_compression_threshold_bytes: 'sidekiq_job_limiter_compression_threshold_bytes', - sidekiq_job_limiter_limit_bytes: 'sidekiq_job_limiter_limit_bytes', - suggest_pipeline_enabled: 'suggest_pipeline_enabled', - search_rate_limit: 'search_rate_limit', - search_rate_limit_unauthenticated: 'search_rate_limit_unauthenticated', - users_get_by_id_limit: 'users_get_by_id_limit', - users_get_by_id_limit_allowlist_raw: 'users_get_by_id_limit_allowlist_raw', - runner_token_expiration_interval: 'runner_token_expiration_interval', - group_runner_token_expiration_interval: 'group_runner_token_expiration_interval', - project_runner_token_expiration_interval: 'project_runner_token_expiration_interval', - pipeline_limit_per_project_user_sha: 'pipeline_limit_per_project_user_sha', - invitation_flow_enforcement: 'invitation_flow_enforcement', - can_create_group: 'can_create_group', - bulk_import_enabled: 'bulk_import_enabled', - allow_runner_registration_token: 'allow_runner_registration_token', - user_defaults_to_private_profile: 'user_defaults_to_private_profile', - deactivation_email_additional_text: 'deactivation_email_additional_text', - projects_api_rate_limit_unauthenticated: 'projects_api_rate_limit_unauthenticated', - gitlab_dedicated_instance: 'gitlab_dedicated_instance', - ci_max_includes: 'ci_max_includes', - allow_account_deletion: 'allow_account_deletion', - external_auth_client_cert: 'external_auth_client_cert', - external_auth_client_key: 'external_auth_client_key', - external_auth_client_key_pass: 'external_auth_client_key_pass', - external_authorization_service_default_label: 'external_authorization_service_default_label', - external_authorization_service_enabled: 'external_authorization_service_enabled', - external_authorization_service_timeout: 'external_authorization_service_timeout', - external_authorization_service_url: 'external_authorization_service_url', - allow_deploy_tokens_and_keys_with_external_auth: 'allow_deploy_tokens_and_keys_with_external_auth', - lets_encrypt_notification_email: 'lets_encrypt_notification_email', - lets_encrypt_terms_of_service_accepted: 'lets_encrypt_terms_of_service_accepted', - domain_denylist_file: 'domain_denylist_file', - email_additional_text: 'email_additional_text', - file_template_project_id: 'file_template_project_id', - git_two_factor_session_expiry: 'git_two_factor_session_expiry', - group_owners_can_manage_default_branch_protection: 'group_owners_can_manage_default_branch_protection', - default_project_deletion_protection: 'default_project_deletion_protection', - disable_personal_access_tokens: 'disable_personal_access_tokens', - deletion_adjourned_period: 'deletion_adjourned_period', - updating_name_disabled_for_users: 'updating_name_disabled_for_users', - maven_package_requests_forwarding: 'maven_package_requests_forwarding', - npm_package_requests_forwarding: 'npm_package_requests_forwarding', - pypi_package_requests_forwarding: 'pypi_package_requests_forwarding', - maintenance_mode: 'maintenance_mode', - maintenance_mode_message: 'maintenance_mode_message', - globally_allowed_ips: 'globally_allowed_ips' - }.freeze - def execute return if model.blank? - COLUMNS_EVENT_TYPE_HASH.each do |column, event_name| - audit_changes(column, as: "ApplicationSettings #{column}", model: model, event_type: event_name) + changed_columns = model.previous_changes.except!(:updated_at) + + changed_columns.each_key do |column| + audit_changes(column, as: "ApplicationSettings #{column}", model: model, event_type: "#{column}_updated") end end -- GitLab From f6a4efe0b35f457daccd966ede8624712f152b28 Mon Sep 17 00:00:00 2001 From: smriti Date: Tue, 27 Jun 2023 19:46:14 +0530 Subject: [PATCH 03/10] YML configuration files for settings --- .../audit_events/types/abuse_notification_email.yml | 9 +++++++++ ee/config/audit_events/types/after_sign_out_path.yml | 9 +++++++++ ee/config/audit_events/types/after_sign_up_text.yml | 9 +++++++++ ee/config/audit_events/types/ai_access_token.yml | 9 +++++++++ ee/config/audit_events/types/akismet_api_key.yml | 9 +++++++++ ee/config/audit_events/types/akismet_enabled.yml | 9 +++++++++ .../allow_local_requests_from_hooks_and_services.yml | 9 +++++++++ .../allow_local_requests_from_web_hooks_and_services.yml | 9 +++++++++ ee/lib/audit/application_setting_changes_auditor.rb | 4 ---- 9 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 ee/config/audit_events/types/abuse_notification_email.yml create mode 100644 ee/config/audit_events/types/after_sign_out_path.yml create mode 100644 ee/config/audit_events/types/after_sign_up_text.yml create mode 100644 ee/config/audit_events/types/ai_access_token.yml create mode 100644 ee/config/audit_events/types/akismet_api_key.yml create mode 100644 ee/config/audit_events/types/akismet_enabled.yml create mode 100644 ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml create mode 100644 ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml diff --git a/ee/config/audit_events/types/abuse_notification_email.yml b/ee/config/audit_events/types/abuse_notification_email.yml new file mode 100644 index 00000000000000..fadf178501b519 --- /dev/null +++ b/ee/config/audit_events/types/abuse_notification_email.yml @@ -0,0 +1,9 @@ +--- +name: abuse_notification_email +description: If set abuse reports are sent to this address +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: shared +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/after_sign_out_path.yml b/ee/config/audit_events/types/after_sign_out_path.yml new file mode 100644 index 00000000000000..7e83d2a457364f --- /dev/null +++ b/ee/config/audit_events/types/after_sign_out_path.yml @@ -0,0 +1,9 @@ +--- +name: after_sign_out_path +description: Direct users to this page when they sign out +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: system_access +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/after_sign_up_text.yml b/ee/config/audit_events/types/after_sign_up_text.yml new file mode 100644 index 00000000000000..008fd41ed503b5 --- /dev/null +++ b/ee/config/audit_events/types/after_sign_up_text.yml @@ -0,0 +1,9 @@ +--- +name: after_sign_up_text +description: Text to display after sign up is performed +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: system_access +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/ai_access_token.yml b/ee/config/audit_events/types/ai_access_token.yml new file mode 100644 index 00000000000000..95f5945d7de21d --- /dev/null +++ b/ee/config/audit_events/types/ai_access_token.yml @@ -0,0 +1,9 @@ +--- +name: ai_access_token +description: Token to turn on code suggestions for instance +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: system_access +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/akismet_api_key.yml b/ee/config/audit_events/types/akismet_api_key.yml new file mode 100644 index 00000000000000..abe6788faa3406 --- /dev/null +++ b/ee/config/audit_events/types/akismet_api_key.yml @@ -0,0 +1,9 @@ +--- +name: akismet_api_key +description: Enable or disable Akismet spam protection +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: system_access +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/akismet_enabled.yml b/ee/config/audit_events/types/akismet_enabled.yml new file mode 100644 index 00000000000000..8c3fd9fe8c2fd6 --- /dev/null +++ b/ee/config/audit_events/types/akismet_enabled.yml @@ -0,0 +1,9 @@ +--- +name: akismet_enabled +description: Helps prevent bots from creating issues +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: system_access +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml b/ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml new file mode 100644 index 00000000000000..dc6ef771d2db81 --- /dev/null +++ b/ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml @@ -0,0 +1,9 @@ +--- +name: allow_local_requests_from_hooks_and_services +description: Allow requests to the local network from hooks and services. +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: system_access +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml b/ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml new file mode 100644 index 00000000000000..4a7830b86a3e70 --- /dev/null +++ b/ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml @@ -0,0 +1,9 @@ +--- +name: allow_local_requests_from_web_hooks_and_services +description: Allow requests to the local network from hooks and services. +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +feature_category: system_access +milestone: '16.1' +saved_to_database: true +streamed: true diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index 905b71b3df6eea..3adf9049c04404 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -14,10 +14,6 @@ def execute private - # def audit_method - - # end - def attributes_from_auditable_model(column) { from: model.previous_changes[column].first, -- GitLab From 41936801c9a0273b68bafaf6722634e5f8d1ca0a Mon Sep 17 00:00:00 2001 From: smriti Date: Mon, 3 Jul 2023 15:40:07 +0530 Subject: [PATCH 04/10] Removed additional settings yml --- ee/app/models/ee/application_setting.rb | 4 -- .../ee/application_settings/update_service.rb | 1 - .../types/abuse_notification_email.yml | 9 --- .../types/after_sign_out_path.yml | 9 --- .../audit_events/types/after_sign_up_text.yml | 9 --- .../audit_events/types/ai_access_token.yml | 9 --- .../audit_events/types/akismet_enabled.yml | 9 --- ...local_requests_from_hooks_and_services.yml | 9 --- ...l_requests_from_web_hooks_and_services.yml | 9 --- ...ey.yml => application_setting_updated.yml} | 6 +- .../application_setting_changes_auditor.rb | 6 +- ...pplication_setting_changes_auditor_spec.rb | 55 ++++++++++++------- 12 files changed, 41 insertions(+), 94 deletions(-) delete mode 100644 ee/config/audit_events/types/abuse_notification_email.yml delete mode 100644 ee/config/audit_events/types/after_sign_out_path.yml delete mode 100644 ee/config/audit_events/types/after_sign_up_text.yml delete mode 100644 ee/config/audit_events/types/ai_access_token.yml delete mode 100644 ee/config/audit_events/types/akismet_enabled.yml delete mode 100644 ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml delete mode 100644 ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml rename ee/config/audit_events/types/{akismet_api_key.yml => application_setting_updated.yml} (67%) diff --git a/ee/app/models/ee/application_setting.rb b/ee/app/models/ee/application_setting.rb index 63563a2ae772fd..8af5fe99165772 100644 --- a/ee/app/models/ee/application_setting.rb +++ b/ee/app/models/ee/application_setting.rb @@ -519,10 +519,6 @@ def unique_project_download_limit_enabled? false end - def full_path - "ApplicationSettings" - end - private def elasticsearch_limited_project_exists?(project) diff --git a/ee/app/services/ee/application_settings/update_service.rb b/ee/app/services/ee/application_settings/update_service.rb index 5e4a43cc341935..e09ccf71f54c77 100644 --- a/ee/app/services/ee/application_settings/update_service.rb +++ b/ee/app/services/ee/application_settings/update_service.rb @@ -29,7 +29,6 @@ def execute update_elasticsearch_index_settings(number_of_replicas: elasticsearch_replicas, number_of_shards: elasticsearch_shards) log_audit_events - end result diff --git a/ee/config/audit_events/types/abuse_notification_email.yml b/ee/config/audit_events/types/abuse_notification_email.yml deleted file mode 100644 index fadf178501b519..00000000000000 --- a/ee/config/audit_events/types/abuse_notification_email.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: abuse_notification_email -description: If set abuse reports are sent to this address -introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 -feature_category: shared -milestone: '16.1' -saved_to_database: true -streamed: true diff --git a/ee/config/audit_events/types/after_sign_out_path.yml b/ee/config/audit_events/types/after_sign_out_path.yml deleted file mode 100644 index 7e83d2a457364f..00000000000000 --- a/ee/config/audit_events/types/after_sign_out_path.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: after_sign_out_path -description: Direct users to this page when they sign out -introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 -feature_category: system_access -milestone: '16.1' -saved_to_database: true -streamed: true diff --git a/ee/config/audit_events/types/after_sign_up_text.yml b/ee/config/audit_events/types/after_sign_up_text.yml deleted file mode 100644 index 008fd41ed503b5..00000000000000 --- a/ee/config/audit_events/types/after_sign_up_text.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: after_sign_up_text -description: Text to display after sign up is performed -introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 -feature_category: system_access -milestone: '16.1' -saved_to_database: true -streamed: true diff --git a/ee/config/audit_events/types/ai_access_token.yml b/ee/config/audit_events/types/ai_access_token.yml deleted file mode 100644 index 95f5945d7de21d..00000000000000 --- a/ee/config/audit_events/types/ai_access_token.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ai_access_token -description: Token to turn on code suggestions for instance -introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 -feature_category: system_access -milestone: '16.1' -saved_to_database: true -streamed: true diff --git a/ee/config/audit_events/types/akismet_enabled.yml b/ee/config/audit_events/types/akismet_enabled.yml deleted file mode 100644 index 8c3fd9fe8c2fd6..00000000000000 --- a/ee/config/audit_events/types/akismet_enabled.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: akismet_enabled -description: Helps prevent bots from creating issues -introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 -feature_category: system_access -milestone: '16.1' -saved_to_database: true -streamed: true diff --git a/ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml b/ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml deleted file mode 100644 index dc6ef771d2db81..00000000000000 --- a/ee/config/audit_events/types/allow_local_requests_from_hooks_and_services.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: allow_local_requests_from_hooks_and_services -description: Allow requests to the local network from hooks and services. -introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 -feature_category: system_access -milestone: '16.1' -saved_to_database: true -streamed: true diff --git a/ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml b/ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml deleted file mode 100644 index 4a7830b86a3e70..00000000000000 --- a/ee/config/audit_events/types/allow_local_requests_from_web_hooks_and_services.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: allow_local_requests_from_web_hooks_and_services -description: Allow requests to the local network from hooks and services. -introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 -feature_category: system_access -milestone: '16.1' -saved_to_database: true -streamed: true diff --git a/ee/config/audit_events/types/akismet_api_key.yml b/ee/config/audit_events/types/application_setting_updated.yml similarity index 67% rename from ee/config/audit_events/types/akismet_api_key.yml rename to ee/config/audit_events/types/application_setting_updated.yml index abe6788faa3406..3e977a486399a2 100644 --- a/ee/config/audit_events/types/akismet_api_key.yml +++ b/ee/config/audit_events/types/application_setting_updated.yml @@ -1,8 +1,8 @@ --- -name: akismet_api_key -description: Enable or disable Akismet spam protection +name: application_setting_updated +description: Triggered when Application setting is updated introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639/ feature_category: system_access milestone: '16.1' saved_to_database: true diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index 3adf9049c04404..6c7921afacadc6 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -8,7 +8,9 @@ def execute changed_columns = model.previous_changes.except!(:updated_at) changed_columns.each_key do |column| - audit_changes(column, as: "ApplicationSettings #{column}", model: model, event_type: "#{column}_updated") + audit_changes(column, as: column.to_s, model: model, + entity: Gitlab::Audit::InstanceScope.new, + event_type: "application_setting_updated") end end @@ -18,7 +20,7 @@ def attributes_from_auditable_model(column) { from: model.previous_changes[column].first, to: model.previous_changes[column].last, - target_details: @current_user.full_path + target_details: "#{column} changed for User #{@current_user.full_path}" } end end diff --git a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb index fe6c86fe681032..36d2de92a801a5 100644 --- a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb +++ b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb @@ -11,6 +11,10 @@ subject(:auditor_instance) { described_class.new(user, application_setting) } + before do + stub_licensed_features(extended_audit_events: true, admin_audit_log: true, code_owner_approval_required: true) + end + shared_examples 'application_setting_audit_events_from_to' do it 'calls auditor' do expect(Gitlab::Audit::Auditor).to receive(:audit).with( @@ -23,10 +27,10 @@ additional_details: { change: change.to_s, from: change_from, - target_details: application_setting.full_path.to_s, + target_details: Gitlab::Audit::InstanceScope.new, to: change_to }, - target_details: application_setting.full_path.to_s + target_details: "#{column} changed for User #{current_user.full_path}" } ).and_call_original @@ -34,26 +38,35 @@ end end - shared_examples 'application_setting_audit_events_to' do - it 'calls auditor' do - expect(Gitlab::Audit::Auditor).to receive(:audit).with( - { - name: event.to_s, - author: user, - scope: application_setting, - target: application_setting, - message: "Changed #{change} to #{change_to}", - additional_details: { - change: change.to_s, - from: change_from, - target_details: application_setting.full_path.to_s, - to: change_to - }, - target_details: application_setting.full_path.to_s - } - ).and_call_original + context "when visible attributes are changed" do + it "creates an event" do + columns = ApplicationSettingsHelper.visible_attributes - auditor_instance.execute + columns.each do |column| + data = application_setting.attributes[column.to_s] + + value = + case ApplicationSetting.type_for_attribute(column.to_s).type + when :integer + data.present? ? data + 1 : 0 + when :boolean + !data + else + "#{data}-next" + end + + application_setting.update_attribute(column, value) + + expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async) + .with("application_setting_update", anything, anything) + + expect { auditor_instance.execute }.to change { AuditEvent.count }.by(1) + + event = AuditEvent.last + expect(event.details[:from]).to eq data + expect(event.details[:to]).to eq value + expect(event.details[:change]).to eq column.to_s + end end end end -- GitLab From 1ba448f2059ee3936101e136638aff669f8b7a06 Mon Sep 17 00:00:00 2001 From: smriti Date: Wed, 12 Jul 2023 11:49:18 +0530 Subject: [PATCH 05/10] Rspec changes wrapped up --- .../ee/application_settings/update_service.rb | 2 + .../types/application_setting_updated.yml | 2 +- .../application_setting_changes_auditor.rb | 8 ++- ...pplication_setting_changes_auditor_spec.rb | 69 ++++++++----------- .../update_service_spec.rb | 27 +++++++- .../audit_event_logging_shared_examples.rb | 2 +- spec/support/helpers/terms_helper.rb | 2 +- 7 files changed, 68 insertions(+), 44 deletions(-) diff --git a/ee/app/services/ee/application_settings/update_service.rb b/ee/app/services/ee/application_settings/update_service.rb index e09ccf71f54c77..0879931d59578d 100644 --- a/ee/app/services/ee/application_settings/update_service.rb +++ b/ee/app/services/ee/application_settings/update_service.rb @@ -22,6 +22,8 @@ def execute elasticsearch_namespace_ids = params.delete(:elasticsearch_namespace_ids) elasticsearch_project_ids = params.delete(:elasticsearch_project_ids) + return false if application_setting.errors.present? + if result = super find_or_create_elasticsearch_index if params.keys.any? { |key| key.to_s.start_with?('elasticsearch') } update_elasticsearch_containers(ElasticsearchIndexedNamespace, elasticsearch_namespace_ids) diff --git a/ee/config/audit_events/types/application_setting_updated.yml b/ee/config/audit_events/types/application_setting_updated.yml index 3e977a486399a2..716d533c415db0 100644 --- a/ee/config/audit_events/types/application_setting_updated.yml +++ b/ee/config/audit_events/types/application_setting_updated.yml @@ -4,6 +4,6 @@ description: Triggered when Application setting is updated introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639/ feature_category: system_access -milestone: '16.1' +milestone: '16.3' saved_to_database: true streamed: true diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index 6c7921afacadc6..4016f809303f13 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -8,6 +8,8 @@ def execute changed_columns = model.previous_changes.except!(:updated_at) changed_columns.each_key do |column| + next if virtual_column?(column) + audit_changes(column, as: column.to_s, model: model, entity: Gitlab::Audit::InstanceScope.new, event_type: "application_setting_updated") @@ -16,11 +18,15 @@ def execute private + def virtual_column?(column) + !!(column =~ /_html\Z/) + end + def attributes_from_auditable_model(column) { from: model.previous_changes[column].first, to: model.previous_changes[column].last, - target_details: "#{column} changed for User #{@current_user.full_path}" + target_details: "#{column} changed for User #{@current_user&.full_path}" } end end diff --git a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb index 36d2de92a801a5..36e017d79f9efa 100644 --- a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb +++ b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb @@ -3,70 +3,61 @@ require 'spec_helper' RSpec.describe Audit::ApplicationSettingChangesAuditor, feature_category: :audit_events do - using RSpec::Parameterized::TableSyntax - describe '.audit_changes' do - let_it_be(:user) { create(:user) } - let(:application_setting) { ApplicationSetting.create_from_defaults } - - subject(:auditor_instance) { described_class.new(user, application_setting) } + let!(:user) { create(:user) } + let!(:application_setting) { ApplicationSetting.create_from_defaults } + let!(:application_setting_auditor_instance) { described_class.new(user, application_setting) } before do - stub_licensed_features(extended_audit_events: true, admin_audit_log: true, code_owner_approval_required: true) + stub_licensed_features(extended_audit_events: true, admin_audit_log: true) end shared_examples 'application_setting_audit_events_from_to' do it 'calls auditor' do expect(Gitlab::Audit::Auditor).to receive(:audit).with( { - name: event.to_s, + name: "application_setting_updated", author: user, - scope: application_setting, + scope: be_an_instance_of(Gitlab::Audit::InstanceScope), target: application_setting, - message: "Changed ApplicationSetting #{change} from #{change_from} to #{change_to}", + message: "Changed #{change_field} from #{change_from} to #{change_to}", additional_details: { - change: change.to_s, + change: change_field.to_s, from: change_from, - target_details: Gitlab::Audit::InstanceScope.new, + target_details: "#{change_field} changed for User #{user.full_path}", to: change_to }, - target_details: "#{column} changed for User #{current_user.full_path}" + target_details: "#{change_field} changed for User #{user.full_path}" } ).and_call_original - auditor_instance.execute + expect { application_setting_auditor_instance.execute }.to change { AuditEvent.count }.by(1) + + event = AuditEvent.last + expect(event.details[:from]).to eq change_from + expect(event.details[:to]).to eq change_to + expect(event.details[:change]).to eq change_field end end - context "when visible attributes are changed" do - it "creates an event" do - columns = ApplicationSettingsHelper.visible_attributes - - columns.each do |column| - data = application_setting.attributes[column.to_s] + context 'when any model change is made' do + let(:change_from) { 0 } + let(:change_to) { 10 } + let(:change_field) { "default_project_visibility" } - value = - case ApplicationSetting.type_for_attribute(column.to_s).type - when :integer - data.present? ? data + 1 : 0 - when :boolean - !data - else - "#{data}-next" - end - - application_setting.update_attribute(column, value) + before do + application_setting.update!(default_project_visibility: 10) + end - expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async) - .with("application_setting_update", anything, anything) + it_behaves_like 'application_setting_audit_events_from_to' + end - expect { auditor_instance.execute }.to change { AuditEvent.count }.by(1) + context 'when ignored column is updated' do + it "does not create an event for _html columns" do + application_setting.update!(after_sign_up_text_html: "test_text") - event = AuditEvent.last - expect(event.details[:from]).to eq data - expect(event.details[:to]).to eq value - expect(event.details[:change]).to eq column.to_s - end + expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async) + expect { application_setting_auditor_instance.execute }.not_to change { AuditEvent.count } end end end diff --git a/ee/spec/services/application_settings/update_service_spec.rb b/ee/spec/services/application_settings/update_service_spec.rb index f6d01ad96d5086..4656448a8272c5 100644 --- a/ee/spec/services/application_settings/update_service_spec.rb +++ b/ee/spec/services/application_settings/update_service_spec.rb @@ -3,19 +3,40 @@ require 'spec_helper' RSpec.describe ApplicationSettings::UpdateService do - let(:user) { create(:user) } + let!(:user) { create(:user) } let(:setting) { ApplicationSetting.create_from_defaults } let(:service) { described_class.new(setting, user, opts) } + shared_examples 'application_setting_audit_events_from_to' do + it 'calls auditor' do + expect { service.execute }.to change { AuditEvent.count }.by(1) + service.execute + + event = AuditEvent.last + expect(event.details[:from]).to eq change_from + expect(event.details[:to]).to eq change_to + expect(event.details[:change]).to eq change_field + end + end + describe '#execute' do context 'common params' do let(:opts) { { home_page_url: 'http://foo.bar' } } + let(:change_field) { 'home_page_url' } + let(:change_to) { 'http://foo.bar' } + let(:change_from) { nil } + + before do + stub_licensed_features(extended_audit_events: true, admin_audit_log: true, code_owner_approval_required: true) + end it 'properly updates settings with given params' do service.execute expect(setting.home_page_url).to eql(opts[:home_page_url]) end + + it_behaves_like 'application_setting_audit_events_from_to' end context 'with valid params' do @@ -281,4 +302,8 @@ end end end + + def update_setting(setting, user, opts) + ApplicationSettings::UpdateService.new(setting, user, opts).execute + end end diff --git a/ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb b/ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb index 6b29b654337684..ebd7a72fbc4754 100644 --- a/ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb +++ b/ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb @@ -41,7 +41,7 @@ it 'does not log audit event if operation fails' do fail_condition! - expect { operation }.not_to change(AuditEvent, :count) + expect { operation }.not_to change { AuditEvent.count } end it 'does not log audit event if operation results in no change' do diff --git a/spec/support/helpers/terms_helper.rb b/spec/support/helpers/terms_helper.rb index 2547ea62e37f7d..5b5caa43f4339a 100644 --- a/spec/support/helpers/terms_helper.rb +++ b/spec/support/helpers/terms_helper.rb @@ -5,7 +5,7 @@ def enforce_terms stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') settings = Gitlab::CurrentSettings.current_application_settings ApplicationSettings::UpdateService.new( - settings, nil, terms: 'These are the terms', enforce_terms: true + settings, user, terms: 'These are the terms', enforce_terms: true ).execute end -- GitLab From f7f162ff330bd5413cf04fbbb2e8111d240b27bc Mon Sep 17 00:00:00 2001 From: smriti Date: Fri, 21 Jul 2023 21:57:18 +0530 Subject: [PATCH 06/10] Changes for skipping encrypted attributes while creating Audit Event --- ee/app/services/ee/application_settings/update_service.rb | 4 +++- ee/lib/audit/application_setting_changes_auditor.rb | 2 +- .../services/application_settings/update_service_spec.rb | 8 ++++++++ spec/support/helpers/terms_helper.rb | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ee/app/services/ee/application_settings/update_service.rb b/ee/app/services/ee/application_settings/update_service.rb index 0879931d59578d..4f31704bccff15 100644 --- a/ee/app/services/ee/application_settings/update_service.rb +++ b/ee/app/services/ee/application_settings/update_service.rb @@ -30,7 +30,9 @@ def execute update_elasticsearch_containers(ElasticsearchIndexedProject, elasticsearch_project_ids) update_elasticsearch_index_settings(number_of_replicas: elasticsearch_replicas, number_of_shards: elasticsearch_shards) - log_audit_events + # There are cases when current user is passed as nil like in elastic.rake + # we should not log audit events in such cases + log_audit_events if current_user end result diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index 4016f809303f13..1e1534cf70bb82 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -19,7 +19,7 @@ def execute private def virtual_column?(column) - !!(column =~ /_html\Z/) + !!(column =~ /_html\Z/) || !ApplicationSetting.attr_encrypted?(column) end def attributes_from_auditable_model(column) diff --git a/ee/spec/services/application_settings/update_service_spec.rb b/ee/spec/services/application_settings/update_service_spec.rb index 4656448a8272c5..c16c62009876c6 100644 --- a/ee/spec/services/application_settings/update_service_spec.rb +++ b/ee/spec/services/application_settings/update_service_spec.rb @@ -17,6 +17,14 @@ expect(event.details[:to]).to eq change_to expect(event.details[:change]).to eq change_field end + + context 'when user is nil' do + let(:user) {nil} + + it "does not log an event" do + expect { service.execute }.to change { AuditEvent.count }.by(0) + end + end end describe '#execute' do diff --git a/spec/support/helpers/terms_helper.rb b/spec/support/helpers/terms_helper.rb index 5b5caa43f4339a..2547ea62e37f7d 100644 --- a/spec/support/helpers/terms_helper.rb +++ b/spec/support/helpers/terms_helper.rb @@ -5,7 +5,7 @@ def enforce_terms stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') settings = Gitlab::CurrentSettings.current_application_settings ApplicationSettings::UpdateService.new( - settings, user, terms: 'These are the terms', enforce_terms: true + settings, nil, terms: 'These are the terms', enforce_terms: true ).execute end -- GitLab From e4612061ffa7483cac36cb872894e138cde9d69a Mon Sep 17 00:00:00 2001 From: smriti Date: Mon, 24 Jul 2023 12:28:01 +0530 Subject: [PATCH 07/10] Ignoring encrypted attributes for ApplicationSetting --- ee/lib/audit/application_setting_changes_auditor.rb | 2 +- .../audit/application_setting_changes_auditor_spec.rb | 11 +++++++++++ .../application_settings/update_service_spec.rb | 2 +- vendor/gems/attr_encrypted/lib/attr_encrypted.rb | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index 1e1534cf70bb82..1332113dc5255b 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -19,7 +19,7 @@ def execute private def virtual_column?(column) - !!(column =~ /_html\Z/) || !ApplicationSetting.attr_encrypted?(column) + !!(column =~ /_html\Z/) || ApplicationSetting.attr_encrypted?(column) || !!(column =~ /^encrypted_/) end def attributes_from_auditable_model(column) diff --git a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb index 36e017d79f9efa..d1bcee0eef6f66 100644 --- a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb +++ b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Audit::ApplicationSettingChangesAuditor, feature_category: :audit_events do + include StubENV + describe '.audit_changes' do let!(:user) { create(:user) } let!(:application_setting) { ApplicationSetting.create_from_defaults } @@ -60,5 +62,14 @@ expect { application_setting_auditor_instance.execute }.not_to change { AuditEvent.count } end end + + context 'when encrypted column is updated' do + it "does not create an event for _html columns" do + application_setting.update!(anthropic_api_key: 'ANTHROPIC_API_KEY') + + expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async) + expect { application_setting_auditor_instance.execute }.not_to change { AuditEvent.count } + end + end end end diff --git a/ee/spec/services/application_settings/update_service_spec.rb b/ee/spec/services/application_settings/update_service_spec.rb index c16c62009876c6..7806ccf0d866c8 100644 --- a/ee/spec/services/application_settings/update_service_spec.rb +++ b/ee/spec/services/application_settings/update_service_spec.rb @@ -19,7 +19,7 @@ end context 'when user is nil' do - let(:user) {nil} + let(:user) { nil } it "does not log an event" do expect { service.execute }.to change { AuditEvent.count }.by(0) diff --git a/vendor/gems/attr_encrypted/lib/attr_encrypted.rb b/vendor/gems/attr_encrypted/lib/attr_encrypted.rb index 82a14086adea52..c4dd2844e2ecfc 100644 --- a/vendor/gems/attr_encrypted/lib/attr_encrypted.rb +++ b/vendor/gems/attr_encrypted/lib/attr_encrypted.rb @@ -186,6 +186,8 @@ def attr_encrypted_options @attr_encrypted_options ||= superclass.attr_encrypted_options.dup end + # Encrypted attributes are ignored as part of ApplicationSetting Audit Event + # Log. Prefix should not be updated as that will lead to failure of Logging functoinality def attr_encrypted_default_options { prefix: 'encrypted_', -- GitLab From 93df3af3f8ba8917e608fb52a3848709a7fb6d6d Mon Sep 17 00:00:00 2001 From: smriti Date: Tue, 25 Jul 2023 20:55:19 +0530 Subject: [PATCH 08/10] Review feedback integrated Encrypted column fix --- .../services/ee/application_settings/update_service.rb | 2 -- ee/lib/audit/application_setting_changes_auditor.rb | 2 +- .../audit/application_setting_changes_auditor_spec.rb | 10 ++++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ee/app/services/ee/application_settings/update_service.rb b/ee/app/services/ee/application_settings/update_service.rb index 4f31704bccff15..810fccb7918526 100644 --- a/ee/app/services/ee/application_settings/update_service.rb +++ b/ee/app/services/ee/application_settings/update_service.rb @@ -22,8 +22,6 @@ def execute elasticsearch_namespace_ids = params.delete(:elasticsearch_namespace_ids) elasticsearch_project_ids = params.delete(:elasticsearch_project_ids) - return false if application_setting.errors.present? - if result = super find_or_create_elasticsearch_index if params.keys.any? { |key| key.to_s.start_with?('elasticsearch') } update_elasticsearch_containers(ElasticsearchIndexedNamespace, elasticsearch_namespace_ids) diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index 1332113dc5255b..fee3932eabacd3 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -19,7 +19,7 @@ def execute private def virtual_column?(column) - !!(column =~ /_html\Z/) || ApplicationSetting.attr_encrypted?(column) || !!(column =~ /^encrypted_/) + !!(column =~ /_html\Z/) || !!(column =~ /^encrypted_/) end def attributes_from_auditable_model(column) diff --git a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb index d1bcee0eef6f66..4ec434bf07e5e8 100644 --- a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb +++ b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb @@ -55,7 +55,7 @@ end context 'when ignored column is updated' do - it "does not create an event for _html columns" do + it 'does not create an event for _html columns' do application_setting.update!(after_sign_up_text_html: "test_text") expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async) @@ -64,11 +64,13 @@ end context 'when encrypted column is updated' do - it "does not create an event for _html columns" do + it 'creates an event for encrypted columns' do application_setting.update!(anthropic_api_key: 'ANTHROPIC_API_KEY') - expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async) - expect { application_setting_auditor_instance.execute }.not_to change { AuditEvent.count } + expect { application_setting_auditor_instance.execute }.to change { AuditEvent.count }.by(1) + event = AuditEvent.last + expect(event.details[:change]).to eq "anthropic_api_key" + expect(event.details[:to]).to eq nil # For encrypted column encrypted_ column contains the ciper end end end -- GitLab From 6f9f6d5cacbc8d85402dd574e670b487320593c5 Mon Sep 17 00:00:00 2001 From: smriti Date: Thu, 27 Jul 2023 22:18:36 +0530 Subject: [PATCH 09/10] Changes for code review --- .../audit_events/types/application_setting_updated.yml | 2 +- ee/lib/audit/application_setting_changes_auditor.rb | 6 +++--- .../lib/audit/application_setting_changes_auditor_spec.rb | 7 ++++--- .../services/application_settings/update_service_spec.rb | 4 ---- vendor/gems/attr_encrypted/lib/attr_encrypted.rb | 2 +- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/ee/config/audit_events/types/application_setting_updated.yml b/ee/config/audit_events/types/application_setting_updated.yml index 716d533c415db0..0ad85b4562e154 100644 --- a/ee/config/audit_events/types/application_setting_updated.yml +++ b/ee/config/audit_events/types/application_setting_updated.yml @@ -2,7 +2,7 @@ name: application_setting_updated description: Triggered when Application setting is updated introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/282428 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639/ +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639 feature_category: system_access milestone: '16.3' saved_to_database: true diff --git a/ee/lib/audit/application_setting_changes_auditor.rb b/ee/lib/audit/application_setting_changes_auditor.rb index fee3932eabacd3..151655192c8595 100644 --- a/ee/lib/audit/application_setting_changes_auditor.rb +++ b/ee/lib/audit/application_setting_changes_auditor.rb @@ -8,7 +8,7 @@ def execute changed_columns = model.previous_changes.except!(:updated_at) changed_columns.each_key do |column| - next if virtual_column?(column) + next if auditable_attribute?(column) audit_changes(column, as: column.to_s, model: model, entity: Gitlab::Audit::InstanceScope.new, @@ -18,7 +18,7 @@ def execute private - def virtual_column?(column) + def auditable_attribute?(column) !!(column =~ /_html\Z/) || !!(column =~ /^encrypted_/) end @@ -26,7 +26,7 @@ def attributes_from_auditable_model(column) { from: model.previous_changes[column].first, to: model.previous_changes[column].last, - target_details: "#{column} changed for User #{@current_user&.full_path}" + target_details: column.humanize } end end diff --git a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb index 4ec434bf07e5e8..fee14eee164fa9 100644 --- a/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb +++ b/ee/spec/lib/audit/application_setting_changes_auditor_spec.rb @@ -26,10 +26,10 @@ additional_details: { change: change_field.to_s, from: change_from, - target_details: "#{change_field} changed for User #{user.full_path}", + target_details: change_field.humanize, to: change_to }, - target_details: "#{change_field} changed for User #{user.full_path}" + target_details: change_field.humanize } ).and_call_original @@ -68,9 +68,10 @@ application_setting.update!(anthropic_api_key: 'ANTHROPIC_API_KEY') expect { application_setting_auditor_instance.execute }.to change { AuditEvent.count }.by(1) + event = AuditEvent.last expect(event.details[:change]).to eq "anthropic_api_key" - expect(event.details[:to]).to eq nil # For encrypted column encrypted_ column contains the ciper + expect(event.details[:to]).to eq nil # For encrypted column encrypted_ column contains the cipher end end end diff --git a/ee/spec/services/application_settings/update_service_spec.rb b/ee/spec/services/application_settings/update_service_spec.rb index 7806ccf0d866c8..e5060260e64d52 100644 --- a/ee/spec/services/application_settings/update_service_spec.rb +++ b/ee/spec/services/application_settings/update_service_spec.rb @@ -310,8 +310,4 @@ end end end - - def update_setting(setting, user, opts) - ApplicationSettings::UpdateService.new(setting, user, opts).execute - end end diff --git a/vendor/gems/attr_encrypted/lib/attr_encrypted.rb b/vendor/gems/attr_encrypted/lib/attr_encrypted.rb index c4dd2844e2ecfc..28508e6d2603fd 100644 --- a/vendor/gems/attr_encrypted/lib/attr_encrypted.rb +++ b/vendor/gems/attr_encrypted/lib/attr_encrypted.rb @@ -187,7 +187,7 @@ def attr_encrypted_options end # Encrypted attributes are ignored as part of ApplicationSetting Audit Event - # Log. Prefix should not be updated as that will lead to failure of Logging functoinality + # Log. If the prefix is updated then the corresponding Regex in ApplicationSettingChangesAuditor should also be updated. def attr_encrypted_default_options { prefix: 'encrypted_', -- GitLab From 6a80117727d958573161f7efd571f75f58b06fed Mon Sep 17 00:00:00 2001 From: smriti Date: Fri, 28 Jul 2023 09:23:04 +0530 Subject: [PATCH 10/10] Documentation updated --- doc/administration/audit_event_streaming/audit_event_types.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/administration/audit_event_streaming/audit_event_types.md b/doc/administration/audit_event_streaming/audit_event_types.md index a8fa06dbc64779..b34fab8f1fa406 100644 --- a/doc/administration/audit_event_streaming/audit_event_types.md +++ b/doc/administration/audit_event_streaming/audit_event_types.md @@ -21,6 +21,7 @@ Audit event types are used to [filter streamed audit events](index.md#update-eve | [`allow_committer_approval_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102256) | Event triggered on updating prevent merge request approval from committers from group merge request setting | **{check-circle}** Yes | **{check-circle}** Yes | `compliance_management` | [15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/373949) | | [`allow_merge_on_skipped_pipeline_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83922) | There is a project setting which toggles the ability to merge when a pipeline is skipped. This audit event tracks changes to that setting. This MR adds a setting to allow this (like previous GitLab versions). | **{check-circle}** Yes | **{check-circle}** Yes | `continuous_integration` | [14.10](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) | | [`allow_overrides_to_approver_list_per_merge_request_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102256) | Event triggered on updating prevent users from modifying MR approval rules in merge requests from group merge request setting | **{check-circle}** Yes | **{check-circle}** Yes | `compliance_management` | [15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/373949) | +| [`application_setting_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639) | Triggered when Application setting is updated | **{check-circle}** Yes | **{check-circle}** Yes | `system_access` | [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/282428) | | [`approval_rule_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89939) | Triggered when a merge request approval rule is created | **{check-circle}** Yes | **{check-circle}** Yes | `source_code_management` | [15.2](https://gitlab.com/gitlab-org/gitlab/-/issues/363092) | | [`approval_rule_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82297) | Triggered on successful approval rule deletion | **{check-circle}** Yes | **{check-circle}** Yes | `source_code_management` | [14.9](https://gitlab.com/gitlab-org/gitlab/-/issues/329514) | | [`audit_events_streaming_headers_create`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92068) | Triggered when a streaming header for audit events is created | **{check-circle}** Yes | **{check-circle}** Yes | `audit_events` | [15.3](https://gitlab.com/gitlab-org/gitlab/-/issues/366350) | -- GitLab