spid 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Spid
6
+ module Saml2
7
+ class ServiceProvider # :nodoc:
8
+ attr_reader :host
9
+ attr_reader :acs_path
10
+ attr_reader :acs_binding
11
+ attr_reader :slo_path
12
+ attr_reader :slo_binding
13
+ attr_reader :metadata_path
14
+ attr_reader :private_key
15
+ attr_reader :certificate
16
+ attr_reader :digest_method
17
+ attr_reader :signature_method
18
+ attr_reader :attribute_service_name
19
+
20
+ # rubocop:disable Metrics/ParameterLists
21
+ # rubocop:disable Metrics/MethodLength
22
+ def initialize(
23
+ host:,
24
+ acs_path:,
25
+ acs_binding:,
26
+ slo_path:,
27
+ slo_binding:,
28
+ metadata_path:,
29
+ private_key:,
30
+ certificate:,
31
+ digest_method:,
32
+ signature_method:,
33
+ attribute_service_name:
34
+ )
35
+ @host = host
36
+ @acs_path = acs_path
37
+ @acs_binding = acs_binding
38
+ @slo_path = slo_path
39
+ @slo_binding = slo_binding
40
+ @metadata_path = metadata_path
41
+ @private_key = private_key
42
+ @certificate = certificate
43
+ @digest_method = digest_method
44
+ @signature_method = signature_method
45
+ @attribute_service_name = attribute_service_name
46
+ validate_attributes
47
+ end
48
+ # rubocop:enable Metrics/MethodLength
49
+ # rubocop:enable Metrics/ParameterLists
50
+
51
+ def acs_url
52
+ @acs_url ||= URI.join(host, acs_path).to_s
53
+ end
54
+
55
+ def slo_url
56
+ @slo_url ||= URI.join(host, slo_path).to_s
57
+ end
58
+
59
+ def metadata_url
60
+ @metadata_url ||= URI.join(host, metadata_path).to_s
61
+ end
62
+
63
+ private
64
+
65
+ def validate_attributes
66
+ if !DIGEST_METHODS.include?(digest_method)
67
+ raise UnknownDigestMethodError,
68
+ "Provided digest method is not valid:" \
69
+ " use one of #{DIGEST_METHODS.join(', ')}"
70
+ elsif !SIGNATURE_METHODS.include?(signature_method)
71
+ raise UnknownSignatureMethodError,
72
+ "Provided digest method is not valid:" \
73
+ " use one of #{SIGNATURE_METHODS.join(', ')}"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module Spid
6
+ module Saml2
7
+ class Settings # :nodoc:
8
+ attr_reader :identity_provider
9
+ attr_reader :service_provider
10
+ attr_reader :authn_context
11
+
12
+ def initialize(identity_provider:, service_provider:, authn_context: nil)
13
+ @authn_context = authn_context || Spid::L1
14
+ unless AUTHN_CONTEXTS.include?(@authn_context)
15
+ raise Spid::UnknownAuthnContextError,
16
+ "Provided authn_context '#{@authn_context}' is not valid:" \
17
+ " use one of #{AUTHN_CONTEXTS.join(', ')}"
18
+ end
19
+
20
+ @identity_provider = identity_provider
21
+ @service_provider = service_provider
22
+ end
23
+
24
+ def idp_entity_id
25
+ identity_provider.entity_id
26
+ end
27
+
28
+ def idp_sso_target_url
29
+ identity_provider.sso_target_url
30
+ end
31
+
32
+ def idp_slo_target_url
33
+ identity_provider.slo_target_url
34
+ end
35
+
36
+ def sp_entity_id
37
+ service_provider.host
38
+ end
39
+
40
+ def sp_acs_url
41
+ service_provider.acs_url
42
+ end
43
+
44
+ def sp_acs_binding
45
+ service_provider.acs_binding
46
+ end
47
+
48
+ def sp_slo_service_url
49
+ service_provider.slo_url
50
+ end
51
+
52
+ def sp_slo_service_binding
53
+ service_provider.slo_binding
54
+ end
55
+
56
+ def private_key
57
+ service_provider.private_key
58
+ end
59
+
60
+ def certificate
61
+ service_provider.certificate
62
+ end
63
+
64
+ def signature_method
65
+ service_provider.signature_method
66
+ end
67
+
68
+ def acs_index
69
+ "0"
70
+ end
71
+
72
+ def force_authn?
73
+ authn_context > Spid::L1
74
+ end
75
+
76
+ def x509_certificate_der
77
+ @x509_certificate_der ||=
78
+ begin
79
+ cert = OpenSSL::X509::Certificate.new(certificate)
80
+ Base64.encode64(cert.to_der).delete("\n")
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spid
4
+ module Saml2
5
+ class SPMetadata # :nodoc:
6
+ attr_reader :document
7
+ attr_reader :settings
8
+ attr_reader :uuid
9
+
10
+ def initialize(settings:, uuid: nil)
11
+ @document = REXML::Document.new
12
+ @settings = settings
13
+ @uuid = uuid || SecureRandom.uuid
14
+ end
15
+
16
+ def to_saml
17
+ document.add_element(entity_descriptor)
18
+ document.to_s
19
+ end
20
+
21
+ def entity_descriptor
22
+ @entity_descriptor ||=
23
+ begin
24
+ element = REXML::Element.new("md:EntityDescriptor")
25
+ element.add_attributes(entity_descriptor_attributes)
26
+ element.add_element sp_sso_descriptor
27
+ element
28
+ end
29
+ end
30
+
31
+ def entity_descriptor_attributes
32
+ @entity_descriptor_attributes ||= {
33
+ "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#",
34
+ "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata",
35
+ "entityID" => settings.sp_entity_id,
36
+ "ID" => "_#{uuid}"
37
+ }
38
+ end
39
+
40
+ def sp_sso_descriptor
41
+ @sp_sso_descriptor ||=
42
+ begin
43
+ element = REXML::Element.new("md:SPSSODescriptor")
44
+ element.add_attributes(sp_sso_descriptor_attributes)
45
+ element.add_element key_descriptor
46
+ element.add_element ac_service
47
+ element.add_element slo_service
48
+ element
49
+ end
50
+ end
51
+
52
+ def sp_sso_descriptor_attributes
53
+ @sp_sso_descriptor_attributes ||= {
54
+ "protocolSupportEnumeration" =>
55
+ "urn:oasis:names:tc:SAML:2.0:protocol",
56
+ "AuthnRequestsSigned" => true
57
+ }
58
+ end
59
+
60
+ def ac_service
61
+ @ac_service ||=
62
+ begin
63
+ element = REXML::Element.new("md:AssertionConsumerService")
64
+ element.add_attributes(ac_service_attributes)
65
+ element
66
+ end
67
+ end
68
+
69
+ def ac_service_attributes
70
+ @ac_service_attributes ||= {
71
+ "Binding" => settings.sp_acs_binding,
72
+ "Location" => settings.sp_acs_url,
73
+ "index" => 0,
74
+ "isDefault" => true
75
+ }
76
+ end
77
+
78
+ def slo_service
79
+ @slo_service ||=
80
+ begin
81
+ element = REXML::Element.new("md:SingleLogoutService")
82
+ element.add_attributes(
83
+ "Binding" => settings.sp_slo_service_binding,
84
+ "Location" => settings.sp_slo_service_url
85
+ )
86
+ element
87
+ end
88
+ end
89
+
90
+ def key_descriptor
91
+ @key_descriptor ||=
92
+ begin
93
+ kd = REXML::Element.new("md:KeyDescriptor")
94
+ kd.add_attributes("use" => "signing")
95
+ ki = kd.add_element "ds:KeyInfo"
96
+ data = ki.add_element "ds:X509Data"
97
+ certificate = data.add_element "ds:X509Certificate"
98
+ certificate.text = settings.x509_certificate_der
99
+ kd
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "zlib"
5
+ require "cgi"
6
+ require "spid/saml2/utils/query_params_signer"
7
+
8
+ module Spid
9
+ module Saml2
10
+ module Utils # :nodoc:
11
+ def decode(message)
12
+ Base64.decode64(message)
13
+ end
14
+
15
+ def encode(message)
16
+ Base64.encode64(message)
17
+ end
18
+
19
+ def deflate(message)
20
+ Zlib::Deflate.deflate(message, 9)[2..-5]
21
+ end
22
+
23
+ def inflate(message)
24
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(message)
25
+ rescue Zlib::DataError => _e
26
+ message
27
+ end
28
+
29
+ def deflate_and_encode(message)
30
+ encode(deflate(message)).delete("\n")
31
+ end
32
+
33
+ def decode_and_inflate(message)
34
+ inflate(decode(message))
35
+ end
36
+
37
+ def escaped_params(params)
38
+ params.each_with_object({}) do |(key, value), acc|
39
+ acc[key] = CGI.escape(value)
40
+ end
41
+ end
42
+
43
+ def query_param(key, value)
44
+ "#{key}=#{value}"
45
+ end
46
+
47
+ def query_params(params)
48
+ params.map do |key, value|
49
+ query_param(key, value)
50
+ end
51
+ end
52
+
53
+ def query_string(params)
54
+ query_params(params).join("&")
55
+ end
56
+
57
+ def escaped_query_string(params)
58
+ query_string(escaped_params(params))
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module Spid
6
+ module Saml2
7
+ module Utils
8
+ class QueryParamsSigner # :nodoc:
9
+ include Spid::Saml2::Utils
10
+
11
+ attr_reader :saml_message
12
+ attr_reader :private_key
13
+ attr_reader :signature_method
14
+ attr_reader :relay_state
15
+
16
+ def initialize(
17
+ saml_message:,
18
+ private_key:,
19
+ signature_method:,
20
+ relay_state: nil
21
+ )
22
+ @saml_message = saml_message.delete("\n")
23
+ @private_key = OpenSSL::PKey::RSA.new(private_key)
24
+ @signature_method = signature_method
25
+ @relay_state = relay_state
26
+ end
27
+
28
+ def signature_algorithm
29
+ @signature_algorithm ||= Spid::SIGNATURE_ALGORITHMS[signature_method]
30
+ end
31
+
32
+ def signature
33
+ @signature ||=
34
+ begin
35
+ encode(raw_signature)
36
+ end
37
+ end
38
+
39
+ def signed_query_params
40
+ params_for_signature.merge(
41
+ "Signature" => signature
42
+ )
43
+ end
44
+
45
+ def escaped_signed_query_string
46
+ @escaped_signed_query_string ||=
47
+ escaped_query_string(signed_query_params)
48
+ end
49
+
50
+ def raw_signature
51
+ @raw_signature ||=
52
+ begin
53
+ private_key.sign(
54
+ signature_algorithm,
55
+ escaped_query_string(params_for_signature)
56
+ )
57
+ end
58
+ end
59
+
60
+ def params_for_signature
61
+ @params_for_signature ||=
62
+ begin
63
+ params = {
64
+ "SAMLRequest" => deflate_and_encode(saml_message),
65
+ "RelayState" => relay_state,
66
+ "SigAlg" => signature_method
67
+ }
68
+ params.delete("RelayState") if params["RelayState"].nil?
69
+ params
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "spid/slo/request"
4
4
  require "spid/slo/response"
5
- require "spid/slo/settings"
6
5
 
7
6
  module Spid
8
7
  module Slo # :nodoc:
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spid/logout_request"
4
- require "onelogin/ruby-saml/settings"
5
-
6
3
  module Spid
7
4
  module Slo
8
5
  class Request # :nodoc:
@@ -19,25 +16,43 @@ module Spid
19
16
  end
20
17
  end
21
18
 
22
- def to_saml
23
- logout_request.create(
24
- saml_settings,
25
- "RelayState" => relay_state
26
- )
19
+ def url
20
+ [
21
+ settings.idp_slo_target_url,
22
+ query_params_signer.escaped_signed_query_string
23
+ ].join("?")
24
+ end
25
+
26
+ def query_params_signer
27
+ @query_params_signer ||=
28
+ begin
29
+ Spid::Saml2::Utils::QueryParamsSigner.new(
30
+ saml_message: saml_message,
31
+ relay_state: relay_state,
32
+ private_key: settings.private_key,
33
+ signature_method: settings.signature_method
34
+ )
35
+ end
27
36
  end
28
37
 
29
- def saml_settings
30
- slo_settings.saml_settings
38
+ def saml_message
39
+ @saml_message ||= logout_request.to_saml
31
40
  end
32
41
 
33
- def slo_settings
34
- Settings.new(
35
- service_provider: service_provider,
36
- identity_provider: identity_provider,
42
+ def logout_request
43
+ @logout_request ||= Spid::Saml2::LogoutRequest.new(
44
+ settings: settings,
37
45
  session_index: session_index
38
46
  )
39
47
  end
40
48
 
49
+ def settings
50
+ @settings ||= Spid::Saml2::Settings.new(
51
+ service_provider: service_provider,
52
+ identity_provider: identity_provider
53
+ )
54
+ end
55
+
41
56
  def identity_provider
42
57
  @identity_provider ||=
43
58
  IdentityProviderManager.find_by_name(idp_name)
@@ -47,12 +62,6 @@ module Spid
47
62
  @service_provider ||=
48
63
  Spid.configuration.service_provider
49
64
  end
50
-
51
- private
52
-
53
- def logout_request
54
- LogoutRequest.new
55
- end
56
65
  end
57
66
  end
58
67
  end