Commit 74e3fc25 authored by Mark Chao's avatar Mark Chao Committed by Stan Hu

Admin: listing matching card details of an user

in order to find repeated use of forgery cards on multiple accounts.

Changelog: added
EE: true
parent 4c1f7449
......@@ -12,5 +12,13 @@ module Users
validates :last_digits, allow_nil: true, numericality: {
greater_than_or_equal_to: 0, less_than_or_equal_to: 9999
}
def similar_records
self.class.where(
expiration_date: expiration_date,
last_digits: last_digits,
holder_name: holder_name
).order(credit_card_validated_at: :desc).includes(:user)
end
end
end
......@@ -61,7 +61,6 @@
= _('Disabled')
= render_if_exists 'admin/namespace_plan_info', namespace: @user.namespace
= render_if_exists 'admin/users/credit_card_info', user: @user
%li
%span.light= _('External User:')
......@@ -139,6 +138,8 @@
= render_if_exists 'namespaces/shared_runner_status', namespace: @user.namespace
= render_if_exists 'admin/users/credit_card_info', user: @user, link_to_match_page: true
= render 'shared/custom_attributes', custom_attributes: @user.custom_attributes
-# Rendered on desktop only so order of cards can be different on desktop vs mobile
......
......@@ -17,6 +17,18 @@ module EE
end
end
def card_match
return render_404 unless ::Gitlab.com?
credit_card_validation = user.credit_card_validation
if credit_card_validation&.holder_name
@similar_credit_card_validations = credit_card_validation.similar_records.page(params[:page]).per(100)
else
redirect_to [:admin, @user], notice: _('No credit card data for matching')
end
end
private
override :users_with_included_associations
......
- return unless Gitlab.com?
%li#credit-card-status
- if user.credit_card_validated_at
%span.light= _('Credit card validated at:')
%strong
= user.credit_card_validated_at.to_s(:medium)
- else
%span.light= _('Credit card validated:')
%strong
= _('No')
- return if !Gitlab.com?
- credit_card_validation = user.credit_card_validation
.card
.card-header
= _('Credit card:')
- if local_assigns[:link_to_match_page] && credit_card_validation&.holder_name
.gl-float-right.small
= link_to card_match_admin_user_path(@user) do
= _('other card matches')
%ul.content-list
- if credit_card_validation.nil?
%li#credit-card-status
%span.light= _('Validated:')
%strong= _('No')
- else
%li
- if credit_card_validation.holder_name
%span.light= _('Holder name:')
%strong
= credit_card_validation.holder_name
%li#credit-card-status
%span.light= _('Validated at:')
%strong
= credit_card_validation.credit_card_validated_at.to_s(:medium)
%li
- if credit_card_validation.last_digits
%span.light= _('Card number:')
%strong
= credit_card_validation.last_digits.to_s.rjust(4, '0')
%li
- if credit_card_validation.expiration_date
%span.light= _('Expiration date:')
%strong
= credit_card_validation.expiration_date
- add_to_breadcrumbs _('Users'), admin_users_path
- add_to_breadcrumbs @user.name, admin_user_path(@user)
- breadcrumb_title _('All users with matching cards')
- page_title @user.name, _('All users with matching cards')
- stripe_time_zone = 'America/Los_Angeles'
- stripe_time_format = '%b %-d, %-I:%M%P %Z'
.gl-display-flex.gl-flex-wrap.gl-justify-content-space-between.gl-align-items-center.gl-py-3.gl-mb-5.gl-border-b-solid.gl-border-gray-100.gl-border-b-1
.gl-my-3
%h3.page-title.gl-m-0
= @user.name
\-
= _("All users with matching cards") % { name: @user.name }
.row
- if @similar_credit_card_validations.present?
.col-md-12
%table.table
%thead
%th= _('ID')
%th= _('User')
%th.gl-text-right= _('Validated at')
%th.gl-text-right= _('User created at')
%th.gl-text-right= _('Current sign-in ip')
- @similar_credit_card_validations.each do |credit_card_validation|
- user = credit_card_validation.user
- validated_at = user.credit_card_validated_at
%tr
%td
= user.id
%td
= link_to(user.username, admin_user_path(user))
- if user == @user
= _('(target)')
%td.gl-text-right
= validated_at.to_s(:medium)
\/
= validated_at.in_time_zone(stripe_time_zone).strftime(stripe_time_format)
%td.gl-text-right= user.created_at.to_s(:medium)
%td.gl-text-right
- if user.current_sign_in_ip
= user.current_sign_in_ip
= link_to sprite_icon('earth'), "https://api.hostip.info/country.php?ip=#{user.current_sign_in_ip}", target: '_blank', rel: 'noreferrer'
* All times are in UTC unless specified
= paginate @similar_credit_card_validations, theme: 'gitlab'
.gl-float-right
= render 'admin/users/credit_card_info', user: @user
......@@ -4,6 +4,7 @@ namespace :admin do
resources :users, only: [], constraints: { id: %r{[a-zA-Z./0-9_\-]+} } do
member do
post :reset_runners_minutes
get :card_match
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Admin::UsersController, :enable_admin_mode do
include AdminModeHelper
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
describe 'GET card_match' do
before do
sign_in(admin)
end
context 'when not SaaS' do
it 'responds with 404' do
send_request
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when SaaS', :saas do
context 'when user has no credit card validation' do
it 'redirects back to #show' do
send_request
expect(response).to redirect_to(admin_user_path(user))
end
end
context 'when user has credit card validation' do
let!(:credit_card_validation) { create(:credit_card_validation, user: user) }
let(:card_details) { credit_card_validation.attributes.slice(:expiration_date, :last_digits, :holder_name) }
let!(:match) { create(:credit_card_validation, card_details) }
it 'displays its own and matching card details' do
send_request
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to include(credit_card_validation.holder_name)
expect(response.body).to include(match.holder_name)
end
end
end
def send_request
get card_match_admin_user_path(user)
end
end
end
......@@ -27,7 +27,7 @@ RSpec.describe 'admin/users/show.html.haml' do
it 'includes credit card validation status' do
render
expect(status).to match /Credit card validated:\s+No/
expect(status).to match /Validated:\s+No/
end
context 'when user is validated' do
......@@ -36,7 +36,7 @@ RSpec.describe 'admin/users/show.html.haml' do
it 'includes credit card validation status' do
render
expect(status).to include 'Credit card validated at:'
expect(status).to include 'Validated at:'
end
end
end
......
......@@ -1141,6 +1141,9 @@ msgstr ""
msgid "(revoked)"
msgstr ""
msgid "(target)"
msgstr ""
msgid "(we need your current password to confirm your changes)"
msgstr ""
......@@ -3341,6 +3344,9 @@ msgstr ""
msgid "All users must have a name."
msgstr ""
msgid "All users with matching cards"
msgstr ""
msgid "Allow \"%{group_name}\" to sign you in"
msgstr ""
......@@ -6309,6 +6315,9 @@ msgstr ""
msgid "Capacity threshold"
msgstr ""
msgid "Card number:"
msgstr ""
msgid "CascadingSettings|Enforce for all subgroups"
msgstr ""
......@@ -9924,10 +9933,7 @@ msgstr ""
msgid "CredentialsInventory|SSH Keys"
msgstr ""
msgid "Credit card validated at:"
msgstr ""
msgid "Credit card validated:"
msgid "Credit card:"
msgstr ""
msgid "Critical vulnerabilities present"
......@@ -9984,6 +9990,9 @@ msgstr ""
msgid "Current sign-in at:"
msgstr ""
msgid "Current sign-in ip"
msgstr ""
msgid "Current vulnerabilities count"
msgstr ""
......@@ -13806,6 +13815,9 @@ msgstr ""
msgid "Expiration date (optional)"
msgstr ""
msgid "Expiration date:"
msgstr ""
msgid "Expired"
msgstr ""
......@@ -16853,6 +16865,9 @@ msgstr ""
msgid "History of authentications"
msgstr ""
msgid "Holder name:"
msgstr ""
msgid "Home page URL"
msgstr ""
......@@ -22939,6 +22954,9 @@ msgstr ""
msgid "No contributions were found"
msgstr ""
msgid "No credit card data for matching"
msgstr ""
msgid "No credit card required."
msgstr ""
......@@ -36953,6 +36971,9 @@ msgstr ""
msgid "User and IP rate limits"
msgstr ""
msgid "User created at"
msgstr ""
msgid "User does not have a pending request"
msgstr ""
......@@ -37313,6 +37334,15 @@ msgstr ""
msgid "Validate your GitLab CI configuration file"
msgstr ""
msgid "Validated at"
msgstr ""
msgid "Validated at:"
msgstr ""
msgid "Validated:"
msgstr ""
msgid "Validations failed."
msgstr ""
......@@ -40851,6 +40881,9 @@ msgstr ""
msgid "originating vulnerability"
msgstr ""
msgid "other card matches"
msgstr ""
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
......
......@@ -7,4 +7,19 @@ RSpec.describe Users::CreditCardValidation do
it { is_expected.to validate_length_of(:holder_name).is_at_most(26) }
it { is_expected.to validate_numericality_of(:last_digits).is_less_than_or_equal_to(9999) }
describe '.similar_records' do
let(:card_details) { subject.attributes.slice(:expiration_date, :last_digits, :holder_name) }
subject(:credit_card_validation) { create(:credit_card_validation) }
let!(:match1) { create(:credit_card_validation, card_details) }
let!(:other1) { create(:credit_card_validation, card_details.merge(last_digits: 9)) }
let!(:match2) { create(:credit_card_validation, card_details) }
let!(:other2) { create(:credit_card_validation, card_details.merge(holder_name: 'foo bar')) }
it 'returns records with matching credit card, ordered by credit_card_validated_at' do
expect(subject.similar_records).to eq([match2, match1, subject])
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment