Commit d1d05ae4 authored by Vladimir Shushlin's avatar Vladimir Shushlin Committed by Nick Thomas

Add certificate valid time to pages domain table

Save certificate validity time for pages domains on save
Fill validity time for existing pages domains in background migration
parent bb02557c
...@@ -135,6 +135,14 @@ class PagesDomain < ApplicationRecord ...@@ -135,6 +135,14 @@ class PagesDomain < ApplicationRecord
"#{VERIFICATION_KEY}=#{verification_code}" "#{VERIFICATION_KEY}=#{verification_code}"
end end
def certificate=(certificate)
super(certificate)
# set nil, if certificate is nil
self.certificate_valid_not_before = x509&.not_before
self.certificate_valid_not_after = x509&.not_after
end
private private
def set_verification_code def set_verification_code
...@@ -187,7 +195,7 @@ class PagesDomain < ApplicationRecord ...@@ -187,7 +195,7 @@ class PagesDomain < ApplicationRecord
end end
def x509 def x509
return unless certificate return unless certificate.present?
@x509 ||= OpenSSL::X509::Certificate.new(certificate) @x509 ||= OpenSSL::X509::Certificate.new(certificate)
rescue OpenSSL::X509::CertificateError rescue OpenSSL::X509::CertificateError
......
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddSslValidPeriodToPagesDomain < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def change
add_column :pages_domains, :certificate_valid_not_before, :datetime_with_timezone
add_column :pages_domains, :certificate_valid_not_after, :datetime_with_timezone
end
end
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class ScheduleFillValidTimeForPagesDomainCertificates < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
MIGRATION = 'FillValidTimeForPagesDomainCertificate'
BATCH_SIZE = 500
BATCH_TIME = 5.minutes
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
class PagesDomain < ActiveRecord::Base
include ::EachBatch
self.table_name = 'pages_domains'
end
def up
queue_background_migration_jobs_by_range_at_intervals(
PagesDomain.where.not(certificate: [nil, '']),
MIGRATION,
BATCH_TIME,
batch_size: BATCH_SIZE)
end
def down
end
end
...@@ -1597,6 +1597,8 @@ ActiveRecord::Schema.define(version: 20190530154715) do ...@@ -1597,6 +1597,8 @@ ActiveRecord::Schema.define(version: 20190530154715) do
t.datetime_with_timezone "enabled_until" t.datetime_with_timezone "enabled_until"
t.datetime_with_timezone "remove_at" t.datetime_with_timezone "remove_at"
t.boolean "auto_ssl_enabled", default: false, null: false t.boolean "auto_ssl_enabled", default: false, null: false
t.datetime_with_timezone "certificate_valid_not_before"
t.datetime_with_timezone "certificate_valid_not_after"
t.index ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree t.index ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree
t.index ["project_id"], name: "index_pages_domains_on_project_id", using: :btree t.index ["project_id"], name: "index_pages_domains_on_project_id", using: :btree
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# save validity time pages domain
class FillValidTimeForPagesDomainCertificate
# define PagesDomain with only needed code
class PagesDomain < ActiveRecord::Base
self.table_name = 'pages_domains'
def x509
return unless certificate.present?
@x509 ||= OpenSSL::X509::Certificate.new(certificate)
rescue OpenSSL::X509::CertificateError
nil
end
end
def perform(start_id, stop_id)
PagesDomain.where(id: start_id..stop_id).find_each do |domain|
if Gitlab::Database.mysql?
domain.update_columns(
certificate_valid_not_before: domain.x509&.not_before,
certificate_valid_not_after: domain.x509&.not_after
)
else
# for some reason activerecord doesn't append timezone, iso8601 forces this
domain.update_columns(
certificate_valid_not_before: domain.x509&.not_before&.iso8601,
certificate_valid_not_after: domain.x509&.not_after&.iso8601
)
end
rescue => e
Rails.logger.error "Failed to update pages domain certificate valid time. id: #{domain.id}, message: #{e.message}"
end
end
end
end
end
...@@ -8,9 +8,13 @@ describe EnqueueVerifyPagesDomainWorkers, :sidekiq, :migration do ...@@ -8,9 +8,13 @@ describe EnqueueVerifyPagesDomainWorkers, :sidekiq, :migration do
end end
end end
let(:domains_table) { table(:pages_domains) }
describe '#up' do describe '#up' do
it 'enqueues a verification worker for every domain' do it 'enqueues a verification worker for every domain' do
domains = 1.upto(3).map { |i| PagesDomain.create!(domain: "my#{i}.domain.com") } domains = Array.new(3) do |i|
domains_table.create!(domain: "my#{i}.domain.com", verification_code: "123#{i}")
end
expect { migrate! }.to change(PagesDomainVerificationWorker.jobs, :size).by(3) expect { migrate! }.to change(PagesDomainVerificationWorker.jobs, :size).by(3)
......
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20190524073827_schedule_fill_valid_time_for_pages_domain_certificates.rb')
describe ScheduleFillValidTimeForPagesDomainCertificates, :migration, :sidekiq do
let(:migration_class) { described_class::MIGRATION }
let(:migration_name) { migration_class.to_s.demodulize }
let(:domains_table) { table(:pages_domains) }
let(:certificate) do
File.read('spec/fixtures/passphrase_x509_certificate.crt')
end
before do
domains_table.create!(domain: "domain1.example.com", verification_code: "123")
domains_table.create!(domain: "domain2.example.com", verification_code: "123", certificate: '')
domains_table.create!(domain: "domain3.example.com", verification_code: "123", certificate: certificate)
domains_table.create!(domain: "domain4.example.com", verification_code: "123", certificate: certificate)
end
it 'correctly schedules background migrations' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
first_id = domains_table.find_by_domain("domain3.example.com").id
last_id = domains_table.find_by_domain("domain4.example.com").id
expect(migration_name).to be_scheduled_delayed_migration(5.minutes, first_id, last_id)
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
end
end
it 'sets certificate valid_not_before/not_after' do
perform_enqueued_jobs do
migrate!
domain = domains_table.find_by_domain("domain3.example.com")
expect(domain.certificate_valid_not_before)
.to eq(Time.parse("2018-03-23 14:02:08 UTC"))
expect(domain.certificate_valid_not_after)
.to eq(Time.parse("2019-03-23 14:02:08 UTC"))
end
end
end
...@@ -81,6 +81,17 @@ describe PagesDomain do ...@@ -81,6 +81,17 @@ describe PagesDomain do
end end
end end
describe 'when certificate is specified' do
let(:domain) { build(:pages_domain) }
it 'saves validity time' do
domain.save
expect(domain.certificate_valid_not_before).to be_like_time(Time.parse("2016-02-12 14:32:00 UTC"))
expect(domain.certificate_valid_not_after).to be_like_time(Time.parse("2020-04-12 14:32:00 UTC"))
end
end
describe 'validate certificate' do describe 'validate certificate' do
subject { domain } subject { domain }
......
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