Enable security policy bot for group policies
What does this MR do and why?
Until now, the security policy bots only get created when a project is directly linked to a security policy project.
With this MR, the policy bots will be created for each project in a group when it gets linked to a security policy project. Due to security considerations, there's a separate bot user for each project to avoid unauthorized cross-project access within a group.
How to set up and validate locally
- In rails console enable the experiment fully
Feature.enable(:scan_execution_group_bot_users)
- Create a group
- Under this group, create two projects
- Go to the group, Policies and create a new scan execution policy. Example: Run secret detection with a schedule on main branch.
- Click configure with a merge request
- There should be bot users created in each project
- Create another project under the same group
- The project should have a new bot user with
guest
access created automatically - Go to Policies and unlink the security policy project
- The bot users should get deleted from the projects
Database query
Scope with_security_policy_bots
Used to prevent N+1 queries here: !125411 (diffs)
Query:
SELECT "members"."id" AS t0_r0, "members"."access_level" AS t0_r1, "members"."source_id" AS t0_r2, "members"."source_type" AS t0_r3, "members"."user_id" AS t0_r4, "members"."notification_level" AS t0_r5, "members"."type" AS t0_r6, "members"."created_at" AS t0_r7, "members"."updated_at" AS t0_r8, "members"."created_by_id" AS t0_r9, "members"."invite_email" AS t0_r10, "members"."invite_token" AS t0_r11, "members"."invite_accepted_at" AS t0_r12, "members"."requested_at" AS t0_r13, "members"."expires_at" AS t0_r14, "members"."ldap" AS t0_r15, "members"."override" AS t0_r16, "members"."state" AS t0_r17, "members"."invite_email_success" AS t0_r18, "members"."member_namespace_id" AS t0_r19, "members"."member_role_id" AS t0_r20, "users"."id" AS t1_r0, "users"."email" AS t1_r1, "users"."encrypted_password" AS t1_r2, "users"."reset_password_token" AS t1_r3, "users"."reset_password_sent_at" AS t1_r4, "users"."remember_created_at" AS t1_r5, "users"."sign_in_count" AS t1_r6, "users"."current_sign_in_at" AS t1_r7, "users"."last_sign_in_at" AS t1_r8, "users"."current_sign_in_ip" AS t1_r9, "users"."last_sign_in_ip" AS t1_r10, "users"."created_at" AS t1_r11, "users"."updated_at" AS t1_r12, "users"."name" AS t1_r13, "users"."admin" AS t1_r14, "users"."projects_limit" AS t1_r15, "users"."failed_attempts" AS t1_r16, "users"."locked_at" AS t1_r17, "users"."username" AS t1_r18, "users"."can_create_group" AS t1_r19, "users"."can_create_team" AS t1_r20, "users"."state" AS t1_r21, "users"."color_scheme_id" AS t1_r22, "users"."password_expires_at" AS t1_r23, "users"."created_by_id" AS t1_r24, "users"."last_credential_check_at" AS t1_r25, "users"."avatar" AS t1_r26, "users"."confirmation_token" AS t1_r27, "users"."confirmed_at" AS t1_r28, "users"."confirmation_sent_at" AS t1_r29, "users"."unconfirmed_email" AS t1_r30, "users"."hide_no_ssh_key" AS t1_r31, "users"."admin_email_unsubscribed_at" AS t1_r32, "users"."notification_email" AS t1_r33, "users"."hide_no_password" AS t1_r34, "users"."password_automatically_set" AS t1_r35, "users"."encrypted_otp_secret" AS t1_r36, "users"."encrypted_otp_secret_iv" AS t1_r37, "users"."encrypted_otp_secret_salt" AS t1_r38, "users"."otp_required_for_login" AS t1_r39, "users"."otp_backup_codes" AS t1_r40, "users"."public_email" AS t1_r41, "users"."dashboard" AS t1_r42, "users"."project_view" AS t1_r43, "users"."consumed_timestep" AS t1_r44, "users"."layout" AS t1_r45, "users"."hide_project_limit" AS t1_r46, "users"."note" AS t1_r47, "users"."unlock_token" AS t1_r48, "users"."otp_grace_period_started_at" AS t1_r49, "users"."external" AS t1_r50, "users"."incoming_email_token" AS t1_r51, "users"."auditor" AS t1_r52, "users"."require_two_factor_authentication_from_group" AS t1_r53, "users"."two_factor_grace_period" AS t1_r54, "users"."last_activity_on" AS t1_r55, "users"."notified_of_own_activity" AS t1_r56, "users"."preferred_language" AS t1_r57, "users"."email_opted_in" AS t1_r58, "users"."email_opted_in_ip" AS t1_r59, "users"."email_opted_in_source_id" AS t1_r60, "users"."email_opted_in_at" AS t1_r61, "users"."theme_id" AS t1_r62, "users"."accepted_term_id" AS t1_r63, "users"."feed_token" AS t1_r64, "users"."private_profile" AS t1_r65, "users"."roadmap_layout" AS t1_r66, "users"."include_private_contributions" AS t1_r67, "users"."commit_email" AS t1_r68, "users"."group_view" AS t1_r69, "users"."managing_group_id" AS t1_r70, "users"."first_name" AS t1_r71, "users"."last_name" AS t1_r72, "users"."static_object_token" AS t1_r73, "users"."role" AS t1_r74, "users"."user_type" AS t1_r75, "users"."static_object_token_encrypted" AS t1_r76, "users"."otp_secret_expires_at" AS t1_r77, "users"."onboarding_in_progress" AS t1_r78 FROM "members" LEFT OUTER JOIN "users" ON "users"."id" = "members"."user_id" WHERE "members"."source_type" = 'Project' AND "members"."type" = 'ProjectMember' AND "members"."requested_at" IS NULL AND "users"."user_type" = 10 AND "members"."source_id" IN (47495114)
Nested Loop (cost=1.12..7.18 rows=1 width=1655) (actual time=61.026..61.037 rows=1 loops=1)
Buffers: shared hit=23 read=27 dirtied=2
I/O Timings: read=59.593 write=0.000
-> Index Scan using index_users_on_user_type_and_id on public.users (cost=0.56..3.58 rows=1 width=1464) (actual time=10.713..34.341 rows=8 loops=1)
Index Cond: (users.user_type = 10)
Buffers: shared hit=3 read=12
I/O Timings: read=34.049 write=0.000
-> Index Scan using idx_members_on_user_and_source_and_source_type_and_member_role on public.members (cost=0.56..3.59 rows=1 width=191) (actual time=3.327..3.328 rows=0 loops=8)
Index Cond: ((members.user_id = users.id) AND (members.source_id = 47495114) AND ((members.source_type)::text = 'Project'::text))
Filter: ((members.requested_at IS NULL) AND ((members.type)::text = 'ProjectMember'::text))
Rows Removed by Filter: 0
Buffers: shared hit=20 read=15 dirtied=2
I/O Timings: read=25.544 write=0.000
Time: 73.538 ms
- planning: 12.319 ms
- execution: 61.219 ms
- I/O read: 59.593 ms
- I/O write: 0.000 ms
Shared buffers:
- hits: 23 (~184.00 KiB) from the buffer pool
- reads: 27 (~216.00 KiB) from the OS file cache, including disk I/O
- dirtied: 2 (~16.00 KiB)
- writes: 0
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.
Related to #414328 (closed)
Edited by Martin Čavoj