Commit 803b2861 authored by Catalin Irimie's avatar Catalin Irimie Committed by Nick Thomas

Expose encrypted token_authenticatable fields

Similar to attr_encrypted's encrypted_attributes public method, exposing
encrypted_token_authenticatable_fields allows us to programatically
search for encrypted tokens in the current models.
parent aa82f724
......@@ -4,6 +4,10 @@ module TokenAuthenticatable
extend ActiveSupport::Concern
class_methods do
def encrypted_token_authenticatable_fields
@encrypted_token_authenticatable_fields ||= []
end
private
def add_authentication_token_field(token_field, options = {})
......@@ -12,6 +16,7 @@ module TokenAuthenticatable
end
token_authenticatable_fields.push(token_field)
encrypted_token_authenticatable_fields.push(token_field) if options[:encrypted]
attr_accessor :cleartext_tokens
......
---
title: Add rake task to verify encrypted data through secrets
merge_request: 21851
author:
type: added
# frozen_string_literal: true
module Gitlab
module Doctor
class Secrets
attr_reader :logger
def initialize(logger)
@logger = logger
end
def run!
logger.info "Checking encrypted values in the database"
Rails.application.eager_load! unless Rails.application.config.eager_load
models_with_attributes = Hash.new { |h, k| h[k] = [] }
models_with_encrypted_attributes.each do |model|
models_with_attributes[model] += model.encrypted_attributes.keys
end
models_with_encrypted_tokens.each do |model|
models_with_attributes[model] += model.encrypted_token_authenticatable_fields
end
check_model_attributes(models_with_attributes)
logger.info "Done!"
end
private
def check_model_attributes(models_with_attributes)
running_failures = 0
models_with_attributes.each do |model, attributes|
failures_per_row = Hash.new { |h, k| h[k] = [] }
model.find_each do |data|
attributes.each do |att|
failures_per_row[data.id] << att unless valid_attribute?(data, att)
end
end
running_failures += failures_per_row.keys.count
output_failures_for_model(model, failures_per_row)
end
logger.info "Total: #{running_failures} row(s) affected".color(:blue)
end
def output_failures_for_model(model, failures)
status_color = failures.empty? ? :green : :red
logger.info "- #{model} failures: #{failures.count}".color(status_color)
failures.each do |row_id, attributes|
logger.debug " - #{model}[#{row_id}]: #{attributes.join(", ")}".color(:red)
end
end
def models_with_encrypted_attributes
all_models.select { |d| d.encrypted_attributes.present? }
end
def models_with_encrypted_tokens
all_models.select do |d|
d.include?(TokenAuthenticatable) && d.encrypted_token_authenticatable_fields.present?
end
end
def all_models
@all_models ||= ApplicationRecord.descendants
end
def valid_attribute?(data, attr)
data.public_send(attr) # rubocop:disable GitlabSecurity/PublicSend
true
rescue OpenSSL::Cipher::CipherError, TypeError
false
rescue => e
logger.debug "> Something went wrong for #{data.class.name}[#{data.id}].#{attr}: #{e}".color(:red)
false
end
end
end
end
namespace :gitlab do
namespace :doctor do
desc "GitLab | Check if the database encrypted values can be decrypted using current secrets"
task secrets: :gitlab_environment do
logger = Logger.new(STDOUT)
logger.level = Gitlab::Utils.to_boolean(ENV['VERBOSE']) ? Logger::DEBUG : Logger::INFO
Gitlab::Doctor::Secrets.new(logger).run!
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Doctor::Secrets do
let!(:user) { create(:user, otp_secret: "test") }
let!(:group) { create(:group, runners_token: "test") }
let(:logger) { double(:logger).as_null_object }
subject { described_class.new(logger).run! }
context 'when encrypted attributes are properly set' do
it 'detects decryptable secrets' do
expect(logger).to receive(:info).with(/User failures: 0/)
expect(logger).to receive(:info).with(/Group failures: 0/)
subject
end
end
context 'when attr_encrypted values are not decrypting' do
it 'marks undecryptable values as bad' do
user.encrypted_otp_secret = "invalid"
user.save!
expect(logger).to receive(:info).with(/User failures: 1/)
subject
end
end
context 'when TokenAuthenticatable values are not decrypting' do
it 'marks undecryptable values as bad' do
group.runners_token_encrypted = "invalid"
group.save!
expect(logger).to receive(:info).with(/Group failures: 1/)
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