summaryrefslogtreecommitdiff
path: root/spec/models/concerns/sensitive_serializable_hash_spec.rb
blob: 591a4383a034bab993e186370f08e0c1648d637b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe SensitiveSerializableHash do
  describe '.prevent_from_serialization' do
    let(:base_class) do
      Class.new do
        include ActiveModel::Serialization
        include SensitiveSerializableHash
      end
    end

    let(:test_class) do
      Class.new(base_class) do
        attr_accessor :name, :super_secret

        prevent_from_serialization :super_secret

        def attributes
          { 'name' => nil, 'super_secret' => nil }
        end
      end
    end

    let(:another_class) do
      Class.new(base_class) do
        prevent_from_serialization :sub_secret
      end
    end

    let(:model) { test_class.new }

    it 'does not include the field in serializable_hash' do
      expect(model.serializable_hash).not_to include('super_secret')
    end

    it 'does not change parent class attributes_exempt_from_serializable_hash' do
      expect(test_class.attributes_exempt_from_serializable_hash).to contain_exactly(:super_secret)
      expect(another_class.attributes_exempt_from_serializable_hash).to contain_exactly(:sub_secret)
    end
  end

  describe '#serializable_hash' do
    shared_examples "attr_encrypted attribute" do |klass, attribute_name|
      context "#{klass.name}\##{attribute_name}" do
        let(:attributes) { [attribute_name, "encrypted_#{attribute_name}", "encrypted_#{attribute_name}_iv"] }

        it 'has a encrypted_attributes field' do
          expect(klass.encrypted_attributes).to include(attribute_name.to_sym)
        end

        it 'does not include the attribute in serializable_hash', :aggregate_failures do
          attributes.each do |attribute|
            expect(model.attributes).to include(attribute) # double-check the attribute does exist

            expect(model.serializable_hash).not_to include(attribute)
            expect(model.to_json).not_to include(attribute)
            expect(model.as_json).not_to include(attribute)
          end
        end
      end
    end

    context 'for a web hook' do
      let_it_be(:model) { create(:system_hook) }

      it_behaves_like 'attr_encrypted attribute', WebHook, 'token'
      it_behaves_like 'attr_encrypted attribute', WebHook, 'url'
      it_behaves_like 'attr_encrypted attribute', WebHook, 'url_variables'
    end

    it_behaves_like 'attr_encrypted attribute', Ci::InstanceVariable, 'value' do
      let_it_be(:model) { create(:ci_instance_variable) }
    end

    shared_examples "add_authentication_token_field attribute" do |klass, attribute_name, encrypted_attribute: true, digest_attribute: false|
      context "#{klass.name}\##{attribute_name}" do
        let(:attributes) do
          if digest_attribute
            ["#{attribute_name}_digest"]
          elsif encrypted_attribute
            [attribute_name, "#{attribute_name}_encrypted"]
          else
            [attribute_name]
          end
        end

        it 'has a add_authentication_token_field field' do
          expect(klass.token_authenticatable_fields).to include(attribute_name.to_sym)
        end

        it 'does not include the attribute in serializable_hash', :aggregate_failures do
          attributes.each do |attribute|
            expect(model.attributes).to include(attribute) # double-check the attribute does exist

            expect(model.serializable_hash).not_to include(attribute)
            expect(model.to_json).not_to include(attribute)
            expect(model.as_json).not_to include(attribute)
          end
        end
      end
    end

    it_behaves_like 'add_authentication_token_field attribute', Ci::Runner, 'token' do
      let_it_be(:model) { create(:ci_runner) }

      it 'does not include token_expires_at in serializable_hash' do
        attribute = 'token_expires_at'

        expect(model.attributes).to include(attribute) # double-check the attribute does exist

        expect(model.serializable_hash).not_to include(attribute)
        expect(model.to_json).not_to include(attribute)
        expect(model.as_json).not_to include(attribute)
      end
    end

    it_behaves_like 'add_authentication_token_field attribute', ApplicationSetting, 'health_check_access_token', encrypted_attribute: false do
      # health_check_access_token_encrypted column does not exist
      let_it_be(:model) { create(:application_setting) }
    end

    it_behaves_like 'add_authentication_token_field attribute', PersonalAccessToken, 'token', encrypted_attribute: false, digest_attribute: true do
      # PersonalAccessToken only has token_digest column
      let_it_be(:model) { create(:personal_access_token) }
    end
  end
end