mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-01 16:04:19 +00:00
120 lines
3.2 KiB
Ruby
120 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
module Utils
|
|
module Email
|
|
extend self
|
|
|
|
EMAIL_REGEXP = %r{(?>[a-zA-Z0-9]+|[\-._!#$%&'*+\/=?^{|}~]+){1,255}@[\w\-.]{1,253}\.{1}[a-zA-Z]{2,63}}
|
|
EMAIL_REGEXP_WITH_CAPTURING_GROUP = /(#{EMAIL_REGEXP})/
|
|
|
|
# Replaces most visible characters with * to obfuscate an email address
|
|
# deform adds a fix number of * to ensure the address cannot be guessed. Also obfuscates TLD with **
|
|
def obfuscated_email(email, deform: false)
|
|
return email if email.empty?
|
|
|
|
masker_class = deform ? Deform : Symmetrical
|
|
masker_class.new(email).masked
|
|
end
|
|
|
|
# Runs email address obfuscation on the given text.
|
|
def obfuscate_emails_in_text(text)
|
|
return text unless text.present?
|
|
|
|
text.gsub(EMAIL_REGEXP_WITH_CAPTURING_GROUP) do |email|
|
|
obfuscated_email(email, deform: true)
|
|
end
|
|
end
|
|
|
|
def normalize_email(email)
|
|
return email unless email.is_a?(String)
|
|
return email unless Devise.email_regexp.match?(email.strip)
|
|
|
|
portions = email.downcase.strip.split('@')
|
|
mailbox = portions.shift
|
|
domain = portions.join
|
|
|
|
mailbox_root = mailbox.split('+')[0]
|
|
|
|
# Gmail addresses strip the "." from their emails.
|
|
# For example, user.name@gmail.com is the same as username@gmail.com
|
|
mailbox_root = mailbox_root.tr('.', '') if domain == 'gmail.com'
|
|
|
|
[mailbox_root, domain].join('@')
|
|
end
|
|
|
|
class Masker
|
|
attr_reader :local_part, :sub_domain, :toplevel_domain, :at, :dot
|
|
|
|
def initialize(original)
|
|
@original = original
|
|
@local_part, @at, domain = original.rpartition('@')
|
|
@sub_domain, @dot, @toplevel_domain = domain.rpartition('.')
|
|
|
|
@at = nil if @at.empty?
|
|
@dot = nil if @dot.empty?
|
|
end
|
|
|
|
def masked
|
|
masked = [
|
|
local_part,
|
|
at,
|
|
sub_domain,
|
|
dot,
|
|
toplevel_domain
|
|
].compact.join('')
|
|
|
|
masked = mask(@original, visible_length: 1) if masked == @original
|
|
|
|
masked
|
|
end
|
|
|
|
private
|
|
|
|
def mask(plain, visible_length:, star_length: nil)
|
|
return if plain.empty?
|
|
return plain if visible_length < 0
|
|
|
|
plain = enlarge_if_needed(plain, visible_length)
|
|
|
|
star_length = plain.length - visible_length if star_length.nil?
|
|
|
|
first = plain[0, visible_length]
|
|
stars = '*' * star_length
|
|
|
|
"#{first}#{stars}"
|
|
end
|
|
|
|
def enlarge_if_needed(string, min)
|
|
string.ljust(min, string.first)
|
|
end
|
|
end
|
|
|
|
class Symmetrical < Masker
|
|
def local_part
|
|
mask(@local_part, visible_length: 2)
|
|
end
|
|
|
|
def sub_domain
|
|
mask(@sub_domain, visible_length: 1)
|
|
end
|
|
end
|
|
|
|
# Implements https://design.gitlab.com/patterns/obfuscation#email-addresses
|
|
class Deform < Masker
|
|
def local_part
|
|
mask(@local_part, visible_length: 2, star_length: 5)
|
|
end
|
|
|
|
def sub_domain
|
|
mask(@sub_domain, visible_length: 1, star_length: 5)
|
|
end
|
|
|
|
def toplevel_domain
|
|
mask(@toplevel_domain, visible_length: 1, star_length: 2)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|