security_spec.rb 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe Setting::Validation::Saml::Security do
  4. let(:setting_name) { 'auth_saml_credentials' }
  5. let(:setting_value) do
  6. {
  7. idp_sso_target_url: 'https://self-signed.badssl.com/',
  8. idp_slo_service_url: 'https://example.com',
  9. name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
  10. idp_cert: '-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----',
  11. ssl_verify: false,
  12. security: security,
  13. private_key: private_key_pem,
  14. private_key_secret: private_key_secret,
  15. certificate: certificate,
  16. }
  17. end
  18. let(:security) { 'on' }
  19. let(:private_key_pem) { OpenSSL::PKey::RSA.generate('2048').to_pem }
  20. let(:private_key_secret) { '' }
  21. let(:certificate) do
  22. private_key = private_key_pem.blank? ? OpenSSL::PKey::RSA.generate('2048') : OpenSSL::PKey.read(private_key_pem)
  23. create_certificate(private_key)
  24. end
  25. def create_certificate(private_key, expired: false, ca_cert: false, usable: true)
  26. cert = OpenSSL::X509::Certificate.new
  27. cert.subject = cert.issuer = OpenSSL::X509::Name.parse('/CN=Acme')
  28. cert.not_before = Time.zone.now
  29. cert.not_after = Time.zone.now + (365 * 24 * 60 * 60)
  30. cert.public_key = private_key.public_key if private_key.respond_to?(:public_key)
  31. cert.serial = 0x0
  32. cert.version = 2
  33. cert.not_after = Time.zone.now - (365 * 24 * 60 * 60) if expired
  34. ef = OpenSSL::X509::ExtensionFactory.new
  35. ef.subject_certificate = cert
  36. ef.issuer_certificate = cert
  37. certificate_extensions(cert, ef, ca_cert:, usable:)
  38. cert.sign(private_key, OpenSSL::Digest.new('SHA256'))
  39. cert.to_pem
  40. end
  41. def certificate_extensions(cert, extension_factory, ca_cert: false, usable: true)
  42. cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)) if ca_cert
  43. cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash', false))
  44. cert.add_extension(extension_factory.create_extension('keyUsage', usable ? 'digitalSignature,keyEncipherment' : 'cRLSign', true))
  45. cert.add_extension(extension_factory.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always', false))
  46. end
  47. context 'with blank settings' do
  48. it 'does not raise an error' do
  49. expect { Setting.set(setting_name, {}) }.not_to raise_error
  50. end
  51. end
  52. context 'with no security' do
  53. let(:security) { 'off' }
  54. it 'does not raise an error' do
  55. expect { Setting.set(setting_name, {}) }.not_to raise_error
  56. end
  57. end
  58. context 'with missing prerequisites' do
  59. context 'when certificate is missing' do
  60. let(:certificate) { '' }
  61. it 'raises an error' do
  62. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: No certificate found.')
  63. end
  64. end
  65. context 'when private key is missing' do
  66. let(:private_key_pem) { '' }
  67. it 'raises an error' do
  68. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: No private key found.')
  69. end
  70. end
  71. end
  72. context 'when private key has wrong type' do
  73. let(:certificate) { create_certificate(OpenSSL::PKey::RSA.generate('2048')) }
  74. let(:private_key_pem) { OpenSSL::PKey::EC.generate('prime256v1').to_pem }
  75. it 'raises an error' do
  76. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: The type of the private key is wrong.')
  77. end
  78. end
  79. context 'when private key has wrong length' do
  80. let(:private_key_pem) { OpenSSL::PKey::RSA.generate('1024').to_pem }
  81. it 'raises an error' do
  82. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: The length of the private key is too short.')
  83. end
  84. end
  85. context 'when certificate contains non-parsable content' do
  86. let(:certificate) { 'dummy' }
  87. it 'raises an error' do
  88. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: The certificate could not be parsed.')
  89. end
  90. end
  91. context 'when certificate is a CA certificate' do
  92. let(:certificate) { create_certificate(OpenSSL::PKey.read(private_key_pem), ca_cert: true) }
  93. it 'raises an error' do
  94. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: The certificate is not usable due to being a CA certificate.')
  95. end
  96. end
  97. context 'when certificate is not usable (e.g. expired)' do
  98. let(:certificate) { create_certificate(OpenSSL::PKey.read(private_key_pem), expired: true) }
  99. it 'raises an error' do
  100. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: The certificate is not usable (e.g. expired).')
  101. end
  102. end
  103. context 'when certificate is not usable for signing/encrypting' do
  104. let(:certificate) { create_certificate(OpenSSL::PKey.read(private_key_pem), usable: false) }
  105. it 'raises an error' do
  106. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: The certificate is not usable for signing and encryption.')
  107. end
  108. end
  109. context 'when certificate does not match the private key' do
  110. let(:private_key_pem) { OpenSSL::PKey::RSA.generate('2048').to_pem }
  111. let(:certificate) { create_certificate(OpenSSL::PKey::RSA.generate('2048')) }
  112. it 'raises an error' do
  113. expect { Setting.set(setting_name, setting_value) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: The certificate does not match the given private key.')
  114. end
  115. end
  116. context 'with a valid certificate and private key' do
  117. it 'does not raise an error' do
  118. expect { Setting.set(setting_name, setting_value) }.not_to raise_error
  119. end
  120. end
  121. end