pgp_key_spec.rb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe PGPKey, type: :model do
  4. let(:user) { create(:admin) }
  5. describe '#create' do
  6. let(:fixture) { 'zammad@localhost' }
  7. let(:key) { Rails.root.join("spec/fixtures/files/pgp/#{fixture}.asc").read }
  8. let(:fingerprint) { Rails.root.join("spec/fixtures/files/pgp/#{fixture}.fingerprint").read }
  9. let(:created_at) { DateTime.parse(Rails.root.join("spec/fixtures/files/pgp/#{fixture}.created_at").read) }
  10. let(:expires_at) { DateTime.parse(Rails.root.join("spec/fixtures/files/pgp/#{fixture}.expires_at").read) }
  11. let(:passphrase) { Rails.root.join("spec/fixtures/files/pgp/#{fixture}.passphrase").read }
  12. let(:params) do
  13. {
  14. key: key,
  15. passphrase: passphrase,
  16. created_by_id: user.id,
  17. updated_by_id: user.id,
  18. }
  19. end
  20. shared_examples 'saving the record' do
  21. it 'saves the record' do
  22. expect(described_class.create!(params)).to have_attributes(
  23. name: fixture,
  24. fingerprint: fingerprint,
  25. created_at: created_at,
  26. expires_at: expires_at,
  27. email_addresses: [fixture],
  28. secret: true,
  29. )
  30. end
  31. it 'prevents duplicate imports' do
  32. expect { 2.times { described_class.create!(params) } }.to raise_error(ActiveRecord::RecordInvalid)
  33. end
  34. end
  35. it_behaves_like 'saving the record'
  36. context 'with a wrong passphrase' do
  37. let(:passphrase) { 'foobar' }
  38. it 'raises an error' do
  39. expect { described_class.create!(params) }.to raise_error(ActiveRecord::RecordInvalid)
  40. end
  41. end
  42. context 'with active domain alias feature' do
  43. shared_examples 'saving the record with expected domain alias' do |expected|
  44. it "saves the record with expected domain alias: #{expected.inspect}" do
  45. expect(described_class.create!(extended_params)).to have_attributes(
  46. name: fixture,
  47. fingerprint: fingerprint,
  48. created_at: created_at,
  49. expires_at: expires_at,
  50. email_addresses: [fixture],
  51. secret: true,
  52. domain_alias: expected
  53. )
  54. end
  55. end
  56. let(:extended_params) do
  57. params.merge(domain_alias: 'zammad.org')
  58. end
  59. it_behaves_like 'saving the record with expected domain alias', '%@zammad.org'
  60. context 'without given domain alias' do
  61. let(:extended_params) do
  62. params.merge(domain_alias: nil)
  63. end
  64. it_behaves_like 'saving the record with expected domain alias', nil
  65. end
  66. context 'with an empty domain alias' do
  67. let(:extended_params) do
  68. params.merge(domain_alias: '')
  69. end
  70. it_behaves_like 'saving the record with expected domain alias', nil
  71. end
  72. end
  73. describe 'keyfile' do
  74. let(:key) { File.open(key_path) }
  75. context 'with a binary keyfile' do
  76. let(:key_path) { Rails.root.join('spec/fixtures/files/pgp/zammad@localhost.pgp') }
  77. it_behaves_like 'saving the record'
  78. end
  79. context 'with an invalid keyfile' do
  80. let(:key_path) { Rails.root.join('spec/fixtures/files/upload/hello_world.txt') }
  81. it 'raises an error' do
  82. expect { described_class.create!(params) }.to raise_error(ActiveRecord::RecordInvalid)
  83. end
  84. end
  85. end
  86. end
  87. describe '#prepare_email_addresses' do
  88. let(:pgp_key) { create(:pgp_key) }
  89. before do
  90. pgp_key.update(name: name)
  91. pgp_key.prepare_email_addresses
  92. end
  93. shared_examples 'saving only email addresses' do |expected|
  94. it 'saves only email addresses' do
  95. expect(pgp_key.email_addresses).to eq(expected)
  96. end
  97. end
  98. context 'with single UID' do
  99. context 'with real name and email address' do
  100. let(:name) { 'Zammad Helpdesk <zammad@localhost>' }
  101. it_behaves_like 'saving only email addresses', ['zammad@localhost']
  102. end
  103. end
  104. context 'with multiple UIDs' do
  105. let(:name) { 'Multi PGP1 <multipgp1@example.com>, Multi PGP2 <multipgp2@example.com>' }
  106. it_behaves_like 'saving only email addresses', ['multipgp1@example.com', 'multipgp2@example.com']
  107. end
  108. end
  109. describe '#email_addresses' do
  110. let(:pgp_key) { create(:'pgp_key/zammad@localhost') }
  111. it 'returns correct email address' do
  112. expect(pgp_key.email_addresses).to eq(['zammad@localhost'])
  113. end
  114. context 'with multiple UIDs' do
  115. let(:pgp_key) { create(:'pgp_key/multipgp2@example.com') }
  116. it 'returns all associated email addresses' do
  117. expect(pgp_key.email_addresses).to eq(['multipgp1@example.com', 'multipgp2@example.com'])
  118. end
  119. end
  120. end
  121. describe '.for_recipient_email_addresses!', mariadb: true do
  122. let!(:pgp_key1) { create(:'pgp_key/pgp1@example.com') }
  123. let!(:pgp_key2) { create(:'pgp_key/pgp2@example.com') }
  124. let!(:pgp_key3) { create(:'pgp_key/pgp3@example.com') }
  125. context 'when all supplied addresses have corresponding keys available' do
  126. let(:addresses) { %w[pgp1@example.com pgp2@example.com pgp3@example.com] }
  127. it 'returns correct keys' do
  128. expect(described_class.for_recipient_email_addresses!(addresses)).to include(pgp_key1, pgp_key2, pgp_key3)
  129. end
  130. end
  131. context 'when one of supplied addresses does not have a corresponding key available' do
  132. let(:addresses) { %w[pgp1@example.com pgp2@example.com pgp3@example.com nonexistentkey@example.com] }
  133. it 'raises an error' do
  134. expect { described_class.for_recipient_email_addresses!(addresses) }.to raise_error(ActiveRecord::RecordNotFound)
  135. end
  136. end
  137. context 'when none of supplied addresses has a corresponding key available' do
  138. let(:addresses) { %w[nonexistentkey@example.com anothernonexistentkey@example.com] }
  139. it 'raises an error' do
  140. expect { described_class.for_recipient_email_addresses!(addresses) }.to raise_error(ActiveRecord::RecordNotFound)
  141. end
  142. end
  143. context 'with keys with multiple UIDs' do
  144. let!(:multipgp_key) { create(:'pgp_key/multipgp2@example.com') }
  145. let(:addresses) { %w[pgp1@example.com pgp2@example.com pgp3@example.com multipgp1@example.com multipgp2@example.com] }
  146. it 'returns correct keys' do
  147. expect(described_class.for_recipient_email_addresses!(addresses)).to include(pgp_key1, pgp_key2, pgp_key3, multipgp_key)
  148. end
  149. end
  150. context 'with active domain alias feature' do
  151. before do
  152. Setting.set('pgp_recipient_alias_configuration', true)
  153. end
  154. let!(:pgp_key3) { create(:pgp_key, fixture: 'pgp3@example.com', domain_alias: 'domain3.com') }
  155. let(:addresses) { %w[pgp1@example.com pgp2@example.com example@domain3.com] }
  156. it 'returns correct keys' do
  157. expect(described_class.for_recipient_email_addresses!(addresses)).to include(pgp_key1, pgp_key2, pgp_key3)
  158. end
  159. end
  160. end
  161. describe '.find_by_uid', mariadb: true do
  162. let!(:pgp_key1) { create(:'pgp_key/pgp1@example.com') }
  163. context 'when an existing uid is used' do
  164. it 'returns the correct key' do
  165. expect(described_class.find_by_uid('pgp1@example.com')).to eq(pgp_key1) # rubocop:disable Rails/DynamicFindBy
  166. end
  167. end
  168. context 'when a non-existing uid is used' do
  169. it 'raises an error' do
  170. expect { described_class.find_by_uid('pgp123@example.com') }.to raise_error(ActiveRecord::RecordNotFound) # rubocop:disable Rails/DynamicFindBy
  171. end
  172. end
  173. context 'when a key with a similar uid is present' do
  174. before do
  175. create(:'pgp_key/noexpirepgp1@example.com')
  176. end
  177. it 'returns the correct key' do
  178. expect(described_class.find_by_uid('pgp1@example.com')).to eq(pgp_key1) # rubocop:disable Rails/DynamicFindBy
  179. end
  180. end
  181. context 'when recipient alias configuration is active' do
  182. before do
  183. Setting.set('pgp_recipient_alias_configuration', true)
  184. end
  185. context 'when there is no match for the domain' do
  186. it 'raises an error' do
  187. expect { described_class.find_by_uid('nicole.braun@zammad.org') }.to raise_error(ActiveRecord::RecordNotFound) # rubocop:disable Rails/DynamicFindBy
  188. end
  189. end
  190. context 'when there is at least one match for the domain' do
  191. let!(:pgp_key1) { create(:'pgp_key/pgp1@example.com', domain_alias: 'zammad.org') }
  192. it 'returns the correct key' do
  193. expect(described_class.find_by_uid('nicole.braun@zammad.org')).to eq(pgp_key1) # rubocop:disable Rails/DynamicFindBy
  194. end
  195. end
  196. end
  197. end
  198. describe '.find_all_by_uid', mariadb: true do
  199. let!(:pgp_key1) { create(:'pgp_key/pgp1@example.com', domain_alias: 'zammad.org') }
  200. let!(:pgp_key2) { create(:'pgp_key/pgp2@example.com', domain_alias: 'zammad.org') }
  201. let!(:pgp_key3) { create(:'pgp_key/pgp3@example.com', domain_alias: 'zammad.org') }
  202. context 'when there is no match for the uid' do
  203. it 'returns an empty array' do
  204. expect(described_class.find_all_by_uid('nicole.braun@zammad.org')).to eq([])
  205. end
  206. end
  207. context 'when recipient alias configuration is active' do
  208. before do
  209. Setting.set('pgp_recipient_alias_configuration', true)
  210. end
  211. context 'when there is no match for the uid' do
  212. it 'returns an empty array' do
  213. expect(described_class.find_all_by_uid('nicole.braun@zammad.com')).to eq([])
  214. end
  215. end
  216. context 'when there is at least one match for the uid' do
  217. it 'returns the correct keys' do
  218. expect(described_class.find_all_by_uid('pgp1@example.com')).to include(pgp_key1)
  219. end
  220. end
  221. context 'when there is more than one match for the uid (domain alias)' do
  222. it 'returns the correct keys' do
  223. expect(described_class.find_all_by_uid('nicole.braun@zammad.org')).to include(pgp_key1, pgp_key2, pgp_key3)
  224. end
  225. end
  226. end
  227. context 'when recipient alias configuration is inactive' do
  228. before do
  229. Setting.set('pgp_recipient_alias_configuration', false)
  230. end
  231. context 'when there is no match for the uid' do
  232. it 'returns an empty array' do
  233. expect(described_class.find_all_by_uid('nicole.braun@zammad.org')).to eq([])
  234. end
  235. end
  236. context 'when there is at least one match for the uid' do
  237. it 'returns the correct keys' do
  238. expect(described_class.find_all_by_uid('pgp1@example.com')).to include(pgp_key1)
  239. end
  240. end
  241. end
  242. end
  243. end