Skip to content

Add a limit for the number of duplicate detumbed emails

Ian Anderson requested to merge ia-limit-users-by-normalized-email into master

What does this MR do and why?

Related to https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/812

Add a limit for the number of duplicate detumbled emails that can exist for a user's primary email address. Email providers offer alternative addressing schemes that allow different email addresses to be routed to the same inbox. For example, user+one@ex.com and user+two@ex.com will ultimately be routed to the same email inbox. The detumbled version of both email addresses would be user@ex.com. In order to prevent abusers from creating a large number of accounts with a single email address, this MR will introduce a limit to the number of primary email addresses for users that share the same detumbled address. This limit will not be applied when running in Saas if the email is from gitlab.com or a verified domain associated with a paid group.

Query Plans

Added users_by_detumbled_email_count scope to emails table

Link: https://console.postgres.ai/gitlab/gitlab-production-main/sessions/32308/commands/99808

 Aggregate  (cost=3.58..3.59 rows=1 width=8) (actual time=18.827..18.829 rows=1 loops=1)
   Buffers: shared hit=9 read=7
   I/O Timings: read=18.544 write=0.000
   ->  Index Scan using index_emails_on_detumbled_email on public.emails  (cost=0.56..3.58 rows=1 width=4) (actual time=14.140..18.667 rows=3 loops=1)
         Index Cond: (emails.detumbled_email = 'ianderson@gitlab.com'::text)
         Buffers: shared read=7
         I/O Timings: read=18.544 write=0.000
explain SELECT COUNT(DISTINCT "emails"."user_id") FROM "emails" WHERE "emails"."detumbled_email" = 'user@gitlab.com'

Paid verified domains queries

A query was added to check if a domain name is associated with a paid root group. This method is implemented similar to the worker that associates enterprise users.

root_group = ::PagesDomain.verified.find_by_domain_case_insensitive(email_domain)&.root_group

!!root_group&.paid?
Method Query Plan Links
::PagesDomain.verified.find_by_domain_case_insensitive(email_domain) https://console.postgres.ai/gitlab/gitlab-production-main/sessions/32308/commands/99818
root_group https://console.postgres.ai/gitlab/gitlab-production-main/sessions/32308/commands/99819 https://console.postgres.ai/gitlab/gitlab-production-main/sessions/32308/commands/99820
root_group.paid? https://console.postgres.ai/gitlab/gitlab-production-main/sessions/32308/commands/99821 https://console.postgres.ai/gitlab/gitlab-production-main/sessions/32308/commands/99822

::PagesDomain.verified.find_by_domain_case_insensitive(email_domain)

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Screenshots or screen recordings

Screenshots are required for UI changes, and strongly recommended for all other merge requests.

Before After

How to set up and validate locally

Numbered steps to set up and validate the change are strongly suggested.

  1. Enable the feature
    Feature.enable(:limit_normalized_email_reuse) 
  2. Apply the patch to set the reuse limit to 1 normalized email.
    diff --git a/app/validators/anti_abuse/unique_detumbled_email_validator.rb b/app/validators/anti_abuse/unique_detumbled_email_validator.rb
    index d65357980088..2994958cbb5a 100644
    --- a/app/validators/anti_abuse/unique_detumbled_email_validator.rb
    +++ b/app/validators/anti_abuse/unique_detumbled_email_validator.rb
    @@ -2,7 +2,7 @@
    
     module AntiAbuse
       class UniqueDetumbledEmailValidator < ActiveModel::Validator
    -    NORMALIZED_EMAIL_ACCOUNT_LIMIT = 5
    +    NORMALIZED_EMAIL_ACCOUNT_LIMIT = 1
    
         def validate(record)
           return if record.errors.include?(:email)
  3. Register a new user with the same detumbled email as another user. For example, if the user is registered with user@ex.com then attempt to register a new user with an email of user+test@ex.com. You should see an error that the email is not allowed when you try to register the user.
  4. Ensure GDK is running in SAAS mode by setting GITLAB_SIMULATE_SAAS=1.
  5. Setup a verified domain and attempt to the tests before verifying the domain. The registration should error when the limit is reached.
  6. Verify the domain
     PagesDomain.last.update!(verified_at: Time.now)
  7. Re-attempt the user registration. The user should not be allowed to register.
Edited by Ian Anderson

Merge request reports

Loading