1# frozen_string_literal: true
2
3require 'spec_helper'
4
5RSpec.describe X509Certificate do
6  describe 'validation' do
7    it { is_expected.to validate_presence_of(:subject_key_identifier) }
8    it { is_expected.to validate_presence_of(:subject) }
9    it { is_expected.to validate_presence_of(:email) }
10    it { is_expected.to validate_presence_of(:serial_number) }
11    it { is_expected.to validate_presence_of(:x509_issuer_id) }
12  end
13
14  describe 'associations' do
15    it { is_expected.to belong_to(:x509_issuer).required }
16  end
17
18  describe '.safe_create!' do
19    let(:subject_key_identifier) { 'CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD' }
20    let(:subject) { 'CN=gitlab@example.com,OU=Example,O=World' }
21    let(:email) { 'gitlab@example.com' }
22    let(:serial_number) { '123456789' }
23    let(:issuer) { create(:x509_issuer) }
24
25    let(:attributes) do
26      {
27        subject_key_identifier: subject_key_identifier,
28        subject: subject,
29        email: email,
30        serial_number: serial_number,
31        x509_issuer_id: issuer.id
32      }
33    end
34
35    it 'creates a new certificate if it was not found' do
36      expect { described_class.safe_create!(attributes) }.to change { described_class.count }.by(1)
37    end
38
39    it 'assigns the correct attributes when creating' do
40      certificate = described_class.safe_create!(attributes)
41
42      expect(certificate.subject_key_identifier).to eq(subject_key_identifier)
43      expect(certificate.subject).to eq(subject)
44      expect(certificate.email).to eq(email)
45    end
46
47    it 'calls mark_commit_signatures_unverified' do
48      expect_any_instance_of(described_class).to receive(:mark_commit_signatures_unverified)
49
50      described_class.safe_create!(attributes)
51    end
52
53    context 'certificate revocation handling' do
54      let(:x509_certificate) { create(:x509_certificate) }
55
56      it 'starts a revoke worker if certificate is revoked' do
57        expect(X509CertificateRevokeWorker).to receive(:perform_async).with(x509_certificate.id)
58
59        x509_certificate.revoked!
60      end
61
62      it 'does not starts a revoke worker for good certificates' do
63        expect(X509CertificateRevokeWorker).not_to receive(:perform_async).with(x509_certificate.id)
64
65        x509_certificate
66      end
67    end
68  end
69
70  describe 'validators' do
71    let_it_be(:issuer) { create(:x509_issuer) }
72
73    it 'accepts correct subject_key_identifier' do
74      subject_key_identifiers = [
75        'AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB',
76        'CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD'
77      ]
78
79      subject_key_identifiers.each do |identifier|
80        expect(build(:x509_certificate, x509_issuer: issuer, subject_key_identifier: identifier)).to be_valid
81      end
82    end
83
84    it 'rejects invalid subject_key_identifier' do
85      subject_key_identifiers = [
86        'AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB',
87        'CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:CD:GG',
88        'random string',
89        '12321342545356434523412341245452345623453542345234523453245'
90      ]
91
92      subject_key_identifiers.each do |identifier|
93        expect(build(:x509_certificate, x509_issuer: issuer, subject_key_identifier: identifier)).to be_invalid
94      end
95    end
96
97    it 'accepts correct email address' do
98      emails = [
99        'smime@example.org',
100        'smime@example.com'
101      ]
102
103      emails.each do |email|
104        expect(build(:x509_certificate, x509_issuer: issuer, email: email)).to be_valid
105      end
106    end
107
108    it 'rejects invalid email' do
109      emails = [
110        'this is not an email',
111        '@example.org'
112      ]
113
114      emails.each do |email|
115        expect(build(:x509_certificate, x509_issuer: issuer, email: email)).to be_invalid
116      end
117    end
118
119    it 'accepts valid serial_number' do
120      expect(build(:x509_certificate, x509_issuer: issuer, serial_number: 123412341234)).to be_valid
121
122      # rfc 5280 - 4.1.2.2  Serial number (20 octets is the maximum)
123      expect(build(:x509_certificate, x509_issuer: issuer, serial_number: 1461501637330902918203684832716283019655932542975)).to be_valid
124      expect(build(:x509_certificate, x509_issuer: issuer, serial_number: 'ffffffffffffffffffffffffffffffffffffffff'.to_i(16))).to be_valid
125    end
126
127    it 'rejects invalid serial_number' do
128      expect(build(:x509_certificate, x509_issuer: issuer, serial_number: "sgsgfsdgdsfg")).to be_invalid
129    end
130  end
131end
132