main
1# frozen_string_literal: true
2
3module Saml
4 module Kit
5 # This class represents the main configuration that is use for generating
6 # SAML documents.
7 #
8 # Saml::Kit::Configuration.new do |config|
9 # config.entity_id = "com:saml:kit"
10 # config.signature_method = :SHA256
11 # config.digest_method = :SHA256
12 # config.registry = Saml::Kit::DefaultRegistry.new
13 # config.session_timeout = 30.minutes
14 # config.logger = Rails.logger
15 # end
16 #
17 # To specify global configuration it is best to do this in an initializer
18 # that runs at the start of the program.
19 #
20 # Saml::Kit.configure do |configuration|
21 # configuration.entity_id = "https://www.example.com/saml/metadata"
22 # configuration.generate_key_pair_for(use: :signing)
23 # configuration.add_key_pair(
24 # ENV["X509_CERTIFICATE"],
25 # ENV["PRIVATE_KEY"],
26 # passphrase: ENV['PRIVATE_KEY_PASSPHRASE'],
27 # use: :encryption
28 # )
29 # end
30 class Configuration
31 USES = %i[signing encryption].freeze
32 # The issuer to use in requests or responses from this entity to use.
33 attr_accessor :entity_id
34 # The signature method to use when generating signatures
35 # (See {Saml::Kit::Builders::XmlSignature::SIGNATURE_METHODS})
36 attr_accessor :signature_method
37 # The digest method to use when generating signatures
38 # (See {Saml::Kit::Builders::XmlSignature::DIGEST_METHODS})
39 attr_accessor :digest_method
40 # The metadata registry to use for searching for metadata associated
41 # with an issuer.
42 attr_accessor :registry
43 # The session timeout to use when generating an Assertion.
44 attr_accessor :session_timeout
45 # The logger to write log messages to.
46 attr_accessor :logger
47 # The total allowable clock drift for session timeout validation.
48 attr_accessor :clock_drift
49 # Whether a document that asserts an identity must carry a signature.
50 #
51 # An unsigned Response or Assertion proves nothing about who issued it,
52 # so this defaults to true and should stay that way. Set it to false only
53 # to keep an identity provider that signs nothing working while it is
54 # being fixed, and understand that doing so accepts forged assertions.
55 attr_accessor :signature_required
56
57 def initialize
58 @clock_drift = 30.seconds
59 @digest_method = :SHA256
60 @key_pairs = []
61 @logger = Logger.new(STDOUT)
62 @registry = DefaultRegistry.new
63 @session_timeout = 3.hours
64 @signature_method = :SHA256
65 @signature_required = true
66 yield self if block_given?
67 end
68
69 # Add a key pair that can be used for either signing or encryption.
70 #
71 # @param certificate [String] the x509 certificate with public key.
72 # @param private_key [String] the plain text private key.
73 # @param passphrase [String] the password to decrypt the private key.
74 # @param use [Symbol] the type of key pair, `:signing` or `:encryption`
75 def add_key_pair(certificate, private_key, passphrase: nil, use: :signing)
76 ensure_proper_use(use)
77 @key_pairs.push(
78 ::Xml::Kit::KeyPair.new(
79 certificate, private_key, passphrase, use.to_sym
80 )
81 )
82 end
83
84 # Generates a unique key pair that can be used for signing or encryption.
85 #
86 # @param use [Symbol] the type of key pair, `:signing` or `:encryption`
87 # @param passphrase [String] the private key passphrase to use.
88 def generate_key_pair_for(use:, passphrase: SecureRandom.uuid)
89 ensure_proper_use(use)
90 certificate, private_key = ::Xml::Kit::SelfSignedCertificate.new.create(
91 passphrase: passphrase
92 )
93 add_key_pair(certificate, private_key, passphrase: passphrase, use: use)
94 end
95
96 # Return each key pair for a specific use.
97 #
98 # @param use [Symbol] the type of key pair to return
99 # `nil`, `:signing` or `:encryption`
100 def key_pairs(use: nil)
101 use.present? ? active_key_pairs.find_all { |xxx| xxx.for?(use) } : active_key_pairs
102 end
103
104 # Return each certificate for a specific use.
105 #
106 # @param use [Symbol] the type of key pair to return
107 # `nil`, `:signing` or `:encryption`
108 def certificates(use: nil)
109 key_pairs(use: use).flat_map(&:certificate)
110 end
111
112 # Return each private for a specific use.
113 #
114 # @param use [Symbol] the type of key pair to return
115 # `nil`, `:signing` or `:encryption`
116 def private_keys(use: nil)
117 key_pairs(use: use).flat_map(&:private_key)
118 end
119
120 # Returns true if there is at least one signing certificate registered.
121 def sign?
122 @sign ||= certificates(use: :signing).any?
123 end
124
125 private
126
127 def ensure_proper_use(use)
128 return if USES.include?(use)
129
130 error_message = 'Use must be either :signing or :encryption'
131 raise ArgumentError, error_message
132 end
133
134 def active_key_pairs
135 @key_pairs.find_all { |x| active?(x) }.sort_by { |x| x.certificate.not_after }.reverse
136 end
137
138 def active?(key_pair)
139 key_pair.certificate.active?
140 rescue OpenSSL::X509::CertificateError => error
141 Saml::Kit.logger.error(error)
142 false
143 end
144 end
145 end
146end