saml_camel 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0e20021e2de63470880efb9945ec8b5c12753b5
4
- data.tar.gz: 052896eea287da266b34d697cf289395b0a8f005
3
+ metadata.gz: 10d5430e20fe2c208e8f6b5c23eb6619cab0c2a1
4
+ data.tar.gz: c41a7aca05f8d811e970d49cca9b46dafdbbe3a0
5
5
  SHA512:
6
- metadata.gz: b9647a4c8bb82f637ff715ddb3e06970d3e505bd3907c3d198ab5a2f4aa45664ec0323a732015d33a476d334b813a1f36c8c6266d4c9cdf4c6ece2777b0de671
7
- data.tar.gz: 8119f4a0ff3a33c5c0772599e38487d3598662119c2fb3effd5939a18d12df72552fe26c603a65607f76af1a248cd733aed6e92efb3465a89d0b918105353f8d
6
+ metadata.gz: c9ce2f9753fd6fe3dfc8ef4e3bd71b51d507ddc3b488f5c8a69312bc0ee739c71212e5d43e374bc603e1f0294a21a908bade01758e2609695a5550e64d4ebcd1
7
+ data.tar.gz: 710161acccafad9bd900433440881c96d6bca2f392cc0320a91e3225dfd85144d9a912ece2c084608f5d23d78b661870b5343469472073f9f6820537db6b3196
data/README.md CHANGED
@@ -83,8 +83,10 @@ Identity Provider(idp) to recognize your app. Typically it should take the form
83
83
  9. Logging is turned on by default. Logging is configured in `config/saml/development/settings.json`. To utilize logging saml_logging should be set to true (default), and primary_id must have a value. primary_id is the saml attribute you consider to be a primary identifier for a user
84
84
 
85
85
 
86
- 10. Users can go to http://localhost:3000/saml/attributes to view attributes being passed through
87
-
86
+ 10. Convenience Endpoints (assuming enginte is mounted to `saml` path):
87
+ - `/saml/attributes` view attributes being passed through
88
+ - `/saml/metadata` generate metadata for your sp
89
+ - `/saml/testAuthn` forces authentication and returns decrypted saml response, test auth path must be set to `true` in settings
88
90
 
89
91
  ## Example settings.json
90
92
  ```json
@@ -92,13 +94,16 @@ Identity Provider(idp) to recognize your app. Typically it should take the form
92
94
  "_comment": "note you will need to restart the application when you make changes to this file",
93
95
  "settings": {
94
96
  "acs": "http://localhost:3000/saml/consumeSaml",
95
- "entity_id": "https://samlCamel.com/doesNotResolve",
97
+ "raw_response_acs": "http://localhost:3000/saml/consumeSaml/rawResponse",
98
+ "entity_id": "https://samlCamel.com/doesNotHaveToResolve",
96
99
  "sso_url": "https://shib.oit.duke.edu/idp/profile/SAML2/Redirect/SSO",
97
100
  "logout_url": "https://shib.oit.duke.edu/cgi-bin/logout.pl",
98
101
  "primary_id": "eduPersonPrincipalName",
99
102
  "sp_session_timeout": 1,
100
103
  "sp_session_lifetime": 8,
101
- "saml_logging": true
104
+ "test_auth_path": true,
105
+ "saml_logging": true,
106
+ "debug": false
102
107
  },
103
108
  "attribute_map": {
104
109
  "urn:oid:1.3.6.1.4.1.5923.1.1.1.9": "eduPersonScopedAffiliation",
@@ -123,6 +128,13 @@ Identity Provider(idp) to recognize your app. Typically it should take the form
123
128
  }
124
129
  ```
125
130
 
131
+ ## Testing
132
+ If saml_protect is called during testing the app will more than likely hang as it is
133
+ waiting for a user to authenticate at the idp. To get around this you have two options:
134
+
135
+ 1. You can not call saml_protect in the test environment and mock your users
136
+ 2. Use `SamlCaml::ServiceProvider.mock_saml_cache(permit_key: 'some permit key', ip_address: 'enter your host ip here')`. This will setup a cache(note caching for test must be enabled). In addition you will need to mock to session variables. set `session[:sp_session]` to `Time.now` and set mock attributes in `session[:saml_attributes]`
137
+
126
138
 
127
139
  ## License
128
140
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -1,6 +1,9 @@
1
- require_dependency "saml_camel/application_controller"
1
+ # frozen_string_literal: true
2
2
 
3
- module SamlCamel::SamlService
3
+ require_dependency 'saml_camel/application_controller'
4
+
5
+ # Helper Methods for SamlController
6
+ module SamlCamel::SamlService # rubocop:disable Style/ClassAndModuleChildren
4
7
  extend ActiveSupport::Concern
5
8
 
6
9
  def cache_available?(app_cache)
@@ -12,24 +15,26 @@ module SamlCamel::SamlService
12
15
  end
13
16
  end
14
17
 
15
- def saml_protect
18
+ # TODO: refactor
19
+ def saml_protect # rubocop:disable Metrics/MethodLength, Metrics/AbcSize:
16
20
  user_cache = cache_available?(Rails.cache.fetch(session[:saml_session_id])) if session[:saml_session_id]
17
21
  if session[:saml_session_id] && user_cache
18
- sp = SamlCamel::ServiceProvider.new(cache_permit_key: session[:saml_session_id].to_sym, saml_attributes: session[:saml_attributes])
19
- session[:sp_session] = sp.validate_sp_session(session[:sp_session],request.remote_ip)
20
- unless (session[:saml_response_success] || session[:sp_session])
22
+ sp = SamlCamel::ServiceProvider.new(
23
+ cache_permit_key: session[:saml_session_id].to_sym,
24
+ saml_attributes: session[:saml_attributes]
25
+ )
26
+ session[:sp_session] = sp.validate_sp_session(session[:sp_session], request.remote_ip)
27
+ unless session[:saml_response_success] || session[:sp_session]
21
28
  saml_request_url = sp.generate_saml_request(request)
22
29
  redirect_to(saml_request_url)
23
30
  end
24
-
25
31
  else
26
32
  session[:saml_session_id] = SamlCamel::ServiceProvider.generate_permit_key
27
- saml_request_url = SamlCamel::ServiceProvider.new(cache_permit_key: session[:saml_session_id].to_sym).generate_saml_request(request)
33
+ saml_request_url = SamlCamel::ServiceProvider.new(
34
+ cache_permit_key: session[:saml_session_id].to_sym
35
+ ).generate_saml_request(request)
28
36
  redirect_to(saml_request_url)
29
37
  end
30
- session[:saml_response_success] = nil #keeps us from looping
38
+ session[:saml_response_success] = nil # keeps us from looping
31
39
  end
32
-
33
-
34
-
35
40
  end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SamlCamel
4
+ # application controller
2
5
  class ApplicationController < ActionController::Base
3
6
  protect_from_forgery with: :exception
4
7
  end
@@ -1,53 +1,54 @@
1
- require_dependency "saml_camel/application_controller"
1
+ # frozen_string_literal: true
2
2
 
3
+ require_dependency 'saml_camel/application_controller'
3
4
  module SamlCamel
5
+ # handles SamlCamel SP
4
6
  class SamlController < ApplicationController
5
7
  include SamlCamel::SamlService
6
- skip_before_action :verify_authenticity_token ,only: [:consume,:logout]
8
+ skip_before_action :verify_authenticity_token, only: %i[consume logout raw_response]
7
9
  before_action :saml_protect, only: [:attr_check]
8
10
 
11
+ def attr_check; end
12
+ # consumes the saml response from the IDP
13
+ # TODO break out into separate methods if possible
14
+ # TODO rubocop also suggests to many assignments going on in consume
9
15
 
10
-
11
- #convinence route to see attributes that are coming through
12
- def index
13
- @attributes = session[:saml_attributes]
14
- end
15
-
16
-
17
- #consumes the saml response from the IDP
18
- def consume
16
+ def consume # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
19
17
  permit_key = session[:saml_session_id].to_sym
20
- user_cache = Rails.cache.fetch(permit_key)
21
- raise "Unable to access cache. Ensure cache is configrued according to documentation." unless cache_available?(user_cache)
18
+ user_cache = Rails.cache.fetch(permit_key)
19
+ unless cache_available?(user_cache)
20
+ raise 'Unable to access cache. Ensure cache is configured according to documentation.'
21
+ end
22
22
  redirect_path = user_cache[:redirect_url]
23
- sp = SamlCamel::ServiceProvider.new(cache_permit_key: permit_key, saml_attributes: session[:saml_attributes])
23
+ sp = SamlCamel::ServiceProvider.new(
24
+ cache_permit_key: permit_key, saml_attributes: session[:saml_attributes]
25
+ )
24
26
  ol_response = SamlCamel::ServiceProvider.ol_response(params[:SAMLResponse])
25
27
 
26
- if sp.validate_idp_response(ol_response,request.remote_ip)
28
+ if sp.validate_idp_response(ol_response, request.remote_ip)
27
29
  # authorize_success, log the user
28
30
  session[:saml_response_success] = true
29
31
  sp.set_saml_session_lifetime
30
32
  session[:sp_session] = Time.now
31
-
32
33
  session[:saml_attributes] = SamlCamel::Transaction.map_attributes(ol_response.attributes)
33
34
  SamlCamel::Logging.successful_auth(session[:saml_attributes])
34
35
 
35
36
  redirect_to redirect_path
36
37
  else # otherwise list out the errors in the response
37
- if session[:saml_session_id]
38
+ if session[:saml_session_id]
38
39
  permit_key = session[:saml_session_id].to_sym
39
40
  Rails.cache.delete(permit_key)
40
41
  session[:saml_session_id] = nil
41
42
  end
42
43
  session[:sp_session] = nil
43
44
  session[:saml_response_success] = false
44
- response.errors
45
+ # response.errors
45
46
  SamlCamel::Logging.auth_failure(ol_response.errors)
46
47
 
47
- redirect_to action: "failure", locals:{errors: ol_response.errors}
48
+ redirect_to action: 'failure', locals: { errors: ol_response.errors }
48
49
  end
49
- rescue => e
50
- if session[:saml_session_id]
50
+ rescue StandardError => e
51
+ if session[:saml_session_id]
51
52
  permit_key = session[:saml_session_id].to_sym
52
53
  Rails.cache.delete(permit_key)
53
54
  end
@@ -56,35 +57,52 @@ module SamlCamel
56
57
  session[:sp_session] = nil
57
58
 
58
59
  SamlCamel::Logging.auth_failure(e)
59
- redirect_to action: "failure", locals:{errors: e}
60
+ redirect_to action: 'failure', locals: { errors: e }
60
61
  end
61
62
 
62
-
63
- #route to show saml failures
63
+ # route to show saml failures
64
64
  def failure
65
65
  @error = params[:locals][:errors]
66
66
  end
67
67
 
68
+ # convinence route to see attributes that are coming through
69
+ def index
70
+ @attributes = session[:saml_attributes]
71
+ end
68
72
 
69
- #kills SP session and redirects to IDP to kill idp session
73
+ # kills SP session and redirects to IDP to kill idp session
70
74
  def logout
71
75
  SamlCamel::Logging.logout(session[:saml_attributes])
72
76
  session[:saml_attributes] = nil
73
77
  session[:sp_session] = nil
74
78
 
75
- # return_url = SamlCamel::Transaction.logout #this methods logs the user out of the IDP, and returns a url to be redirected to
76
- redirect_to SP_SETTINGS["settings"]["logout_url"]
79
+ # return_url = SamlCamel::Transaction.logout #this methods logs the user
80
+ # out of the IDP, and returns a url to be redirected to
81
+ redirect_to SP_SETTINGS['settings']['logout_url']
77
82
  end
78
83
 
84
+ # generate a metadata page that you may need to share with an IDP
85
+ def metadata
86
+ settings = SamlCamel::Transaction.saml_settings
87
+ meta = OneLogin::RubySaml::Metadata.new
88
+ render xml: meta.generate(settings)
89
+ end
79
90
 
80
- def attr_check
91
+ # meant to force authn and then be redirected to raw response to view raw decrypted response
92
+ def force_authn
93
+ saml_request_url = SamlCamel::ServiceProvider.new.generate_saml_request(request, force_authn: true)
94
+ redirect_to(saml_request_url)
81
95
  end
82
96
 
97
+ def raw_response
98
+ ol_response = SamlCamel::ServiceProvider.ol_response(params[:SAMLResponse], raw_response: true)
99
+ render xml: ol_response.decrypted_document.to_s if ol_response.is_valid?
100
+ end
83
101
 
84
102
  private
103
+
85
104
  def saml_settings
86
105
  SamlCamel::Transaction.saml_settings
87
106
  end
88
-
89
107
  end
90
108
  end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SamlCamel
2
4
  class ApplicationRecord
3
-
4
5
  end
5
6
  end
@@ -1,121 +1,143 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SamlCamel
4
+ # class serves as core buisness logic for SamlCamle SP
2
5
  class ServiceProvider
3
- attr_reader :cache_permit_key, :user_cache, :saml_attributes
6
+ attr_reader :cache_permit_key, :saml_attributes
4
7
 
5
- def initialize(cache_permit_key: nil, user_cache: nil, saml_attributes: nil)
6
- @cache_permit_key = cache_permit_key.to_sym
8
+ def initialize(cache_permit_key: nil, saml_attributes: nil)
9
+ @cache_permit_key = cache_permit_key.try(:to_sym)
7
10
  @saml_attributes = saml_attributes
8
11
  @user_cache = Rails.cache.fetch(@cache_permit_key)
9
12
  end
10
13
 
11
-
12
14
  def self.generate_permit_key
13
- secure_random = SecureRandom.base64.chomp.gsub( /\W/, '' )
15
+ secure_random = SecureRandom.base64.chomp.gsub(/\W/, '')
14
16
  "samlCamel#{secure_random}"
15
17
  end
16
18
 
17
- def self.ol_response(idp_response)
18
- response = OneLogin::RubySaml::Response.new(idp_response, :settings => SamlCamel::Transaction.saml_settings)
19
- response.settings = SamlCamel::Transaction.saml_settings
19
+ def self.mock_saml_cache(permit_key: nil, ip_address: nil)
20
+ lifetime = SP_SETTINGS['settings']['sp_session_lifetime']
21
+ Rails.cache.fetch(@cache_permit_key, expires_in: lifetime.hours) do
22
+ { ip_address: host_request.remote_ip }
23
+ end
24
+ end
25
+
26
+ # ol OneLogin
27
+ def self.ol_response(idp_response, raw_response: false)
28
+ settings = SamlCamel::Transaction.saml_settings(raw_response: raw_response)
29
+ response = OneLogin::RubySaml::Response.new(idp_response, settings: settings)
30
+ response.settings = settings
20
31
 
21
32
  response
22
33
  end
23
34
 
24
-
25
- def check_expired_session(sp_session)
26
- # cache_available?(user_cache) TODO come back to this
27
- sp_timeout = SP_SETTINGS["settings"]["sp_session_timeout"]
28
- sp_lifetime = SP_SETTINGS['settings']["sp_session_lifetime"]
35
+ # TODO: method too complex
36
+ def check_expired_session(sp_session) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/LineLength
37
+ sp_timeout = SP_SETTINGS['settings']['sp_session_timeout']
38
+ sp_lifetime = SP_SETTINGS['settings']['sp_session_lifetime']
29
39
 
30
40
  set_saml_session_lifetime if @user_cache[:session_start_time].nil?
31
41
  sp_session_init_time = @user_cache[:session_start_time]
32
42
 
33
-
43
+ SamlCamel::Logging.debug('Checking if session expired') if SP_DEBUG
34
44
  ######## set session[:sp_session] maybe a seperate method
35
45
  if sp_session
36
-
37
- #if the session has timed out remove session, otherwise refresh
38
- if (Time.now - Time.parse(sp_session)) < sp_timeout.hour
39
- return Time.now
40
- else
46
+ # if the session has exceeded the allowed lifetime, remove session
47
+ if (Time.now - sp_session_init_time) > sp_lifetime.hour
41
48
  SamlCamel::Logging.expired_session(@saml_attributes)
42
49
  return nil
43
50
  end
44
51
 
45
- #if the session has exceeded the allowed lifetime, remove session
46
- if (Time.now - sp_session_init_time) > sp_lifetime.hour
52
+ # if the session has timed out remove session, otherwise refresh
53
+ if (Time.now - Time.parse(sp_session)) < sp_timeout.hour
54
+ SamlCamel::Logging.debug('Session within timeout, session renewed') if SP_DEBUG
55
+ Time.now
56
+ else
47
57
  SamlCamel::Logging.expired_session(@saml_attributes)
48
- return nil
58
+ return nil
49
59
  end
50
- else #if no sp session return nil
60
+ else # if no sp session return nil
61
+ SamlCamel::Logging.debug('No session found when checking expiration') if SP_DEBUG
51
62
  return nil
52
63
  end
53
64
  end
54
65
 
66
+ # TODO: method too complex
67
+ def duplicate_response_id?(response_id, count: 3) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/LineLength
68
+ SamlCamel::Logging.debug('Checking uniqueness of response id') if SP_DEBUG
55
69
 
56
- def duplicate_response_id?(response_id, count: 3)
57
- #use semaphore to only allow 1 thread at a time to access
70
+ # use semaphore to only allow 1 thread at a time to access
58
71
  @semaphore ||= Mutex.new
59
- @semaphore.synchronize {
60
- ids = Rails.cache.fetch("saml_camel_response_ids")
72
+ @semaphore.synchronize { # rubocop:disable Style/BlockDelimiters
73
+ ids = Rails.cache.fetch('saml_camel_response_ids')
61
74
  if ids
62
75
  if ids.include?(response_id)
63
- session[:sp_session] = nil
64
- raise "SAML response ID already issued."
76
+ SamlCamel::Logging.debug('Response id has already been used') if SP_DEBUG
77
+ raise 'SAML response ID already issued.'
65
78
  else
79
+ SamlCamel::Logging.debug("Unique Response ID #{response_id}") if SP_DEBUG
66
80
  ids << response_id
67
- Rails.cache.fetch("saml_camel_response_ids", expires_in: 1.hours) do
81
+ Rails.cache.fetch('saml_camel_response_ids', expires_in: 1.hours) do
68
82
  ids
69
83
  end
70
84
  end
71
85
  else
72
- Rails.cache.fetch("saml_camel_response_ids", expires_in: 1.hours) do
86
+ SamlCamel::Logging.debug("Unique Response ID #{response_id}") if SP_DEBUG
87
+ Rails.cache.fetch('saml_camel_response_ids', expires_in: 1.hours) do
73
88
  [response_id]
74
89
  end
75
90
  end
76
91
  }
77
92
  rescue ThreadError
78
- puts "locked "* 50
79
- if count > 0
93
+ puts 'locked ' * 50
94
+ # SamlCamel::Logging.debug('Response ID check locked, trying again') if SP_DEBUG
95
+ if count.positive? # rubocop:disable Style/GuardClause
80
96
  sleep(0.1)
81
97
  duplicate_response_id?(response_id, count: count - 1)
82
- else raise "Resposne ID Validation Error"
98
+ else raise 'Resposne ID Validation Error'
83
99
  end
84
100
  end
85
101
 
86
-
87
-
88
- #generates a saml requests and establishes a cache for the user
89
- def generate_saml_request(host_request)
102
+ # generates a saml requests and establishes a cache for the user
103
+ def generate_saml_request(host_request, force_authn: false)
104
+ SamlCamel::Logging.debug("Creating request for #{host_request.remote_ip}") if SP_DEBUG
90
105
  request = OneLogin::RubySaml::Authrequest.new
91
- lifetime = SamlCamel::SP_SETTINGS['settings']["sp_session_lifetime"]
106
+ lifetime = SP_SETTINGS['settings']['sp_session_lifetime']
92
107
 
93
- #store ip address and original url request in memory to be used for verification and redirect after response
108
+ # store ip address and original url request in memory to be used for
109
+ # verification and redirect after response
94
110
  Rails.cache.fetch(@cache_permit_key, expires_in: lifetime.hours) do
95
- {ip_address: host_request.remote_ip, redirect_url: host_request.url }
111
+ { ip_address: host_request.remote_ip, redirect_url: host_request.url }
96
112
  end
97
- request.create(SamlCamel::Transaction.saml_settings)
113
+ request.create(SamlCamel::Transaction.saml_settings(raw_response: force_authn))
98
114
  end
99
115
 
100
- #set saml_session lifetime, called if none set
116
+
117
+ # set saml_session lifetime, called if none set
118
+ # TODO: this may need to be renamed, it's not really setting the lifetime
119
+ # it's refreshing the last time a user authenticated
101
120
  def set_saml_session_lifetime
102
121
  user_saml_cache = Rails.cache.fetch(@cache_permit_key)
103
122
  user_saml_cache[:session_start_time] = Time.now
104
- sp_lifetime = SP_SETTINGS['settings']["sp_session_lifetime"]
123
+ sp_lifetime = SP_SETTINGS['settings']['sp_session_lifetime']
124
+
125
+ SamlCamel::Logging.debug("Setting lifetime of session. Lifetime of #{sp_lifetime} hours") if SP_DEBUG
105
126
  Rails.cache.fetch(@cache_permit_key, expires_in: sp_lifetime.hours) do
106
- user_saml_cache #not sure if this can use @user_cache
127
+ user_saml_cache
107
128
  end
108
129
  end
109
130
 
110
- #NOTE these methods will raise errors if not a valid response_id
111
- # which in turn will trigger a resuce that kills the sp session
112
- def validate_idp_response(response,remote_ip)
131
+ # NOTE these methods will raise errors if not a valid response_id
132
+ # which in turn will trigger a resuce that kills the sp session
133
+ def validate_idp_response(response, remote_ip)
134
+ SamlCamel::Logging.debug('Validating IDP response') if SP_DEBUG
113
135
  if response.is_valid?
114
- #validate not sha1
136
+ # validate not sha1
115
137
  verify_sha_type(response)
116
138
 
117
- #validate IP address
118
- raise "IP mismatch error" unless validate_ip(remote_ip)
139
+ # validate IP address
140
+ raise 'IP mismatch error' unless validate_ip(remote_ip)
119
141
 
120
142
  response_id = response.id(response.document)
121
143
  duplicate_response_id?(response_id)
@@ -126,36 +148,42 @@ module SamlCamel
126
148
  end
127
149
  end
128
150
 
129
- #validate that ip address has not changed
151
+ # validate that ip address has not changed
130
152
  def validate_ip(remote_ip)
131
- # cache_available?(saml_cache) TODO come back to this
153
+ if SP_DEBUG
154
+ SamlCamel::Logging.debug(
155
+ "Validating IP consistancy. IP @ request = #{@user_cache[:ip_address]}
156
+ | current IP = #{remote_ip}"
157
+ )
158
+ end
159
+
132
160
  return true if remote_ip == @user_cache[:ip_address]
133
- SamlCamel::Logging.bad_ip(@saml_attributes, @user_cache[:ip_address], remote_ip)
134
- return nil
161
+ SamlCamel::Logging.bad_ip(@saml_attributes,
162
+ @user_cache[:ip_address],
163
+ remote_ip)
164
+ nil
135
165
  end
136
166
 
137
- #validates a the sp timestamps and ip. returns a new timestamp if everything is good
138
- # otherwise returns nil
139
- def validate_sp_session(sp_session,remote_ip)
167
+ # validates a the sp timestamps and ip. returns a new timestamp if
168
+ # everything is good otherwise returns nil
169
+
170
+ def validate_sp_session(sp_session, remote_ip)
140
171
  new_session = check_expired_session(sp_session)
141
- if new_session
142
- has_valid_ip = validate_ip(remote_ip)
143
- else
144
- return nil
145
- end
146
- return new_session if has_valid_ip
172
+ valid_ip = validate_ip(remote_ip) if new_session
173
+ return new_session if new_session && valid_ip
147
174
  end
148
175
 
149
-
150
176
  def verify_sha_type(response)
151
- #was suggested to use an xml parser, however was having great difficulyt with nokogiri
152
- #open to trying a different parser or advice on nokogiri as it's not reacting as I would typically expect it to
177
+ SamlCamel::Logging.debug('Verify response is not SHA1') if SP_DEBUG
178
+
179
+ # was suggested to use an xml parser, however was having great difficulyt
180
+ # with nokogiri open to trying a different parser or advice on nokogiri as
181
+ # it's not reacting as I would typically expect it to
153
182
  raw_xml_string = response.decrypted_document.to_s
154
- attr_scan = raw_xml_string.scan(/<ds:SignatureMethod.*\/>/)
155
- is_sha1 = attr_scan[0].match("sha1")
183
+ attr_scan = raw_xml_string.scan(%r{<ds:SignatureMethod.*\/>})
184
+ is_sha1 = attr_scan[0].match('sha1')
156
185
 
157
- raise "SHA1 algorithm not supported " if is_sha1
186
+ raise 'SHA1 algorithm not supported' if is_sha1
158
187
  end
159
-
160
188
  end
161
189
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SamlCamel
4
+ # handle shib attributes
2
5
  class Shib
3
-
4
- ATTRIBUTE_MAP = JSON.parse(File.read("config/saml/shibboleth.json"))
6
+ ATTRIBUTE_MAP = JSON.parse(File.read('config/saml/shibboleth.json'))
5
7
 
6
8
  def self.attributes(request)
7
9
  attrs = {}
@@ -11,16 +13,9 @@ module SamlCamel
11
13
  attrs
12
14
  end
13
15
 
14
- def self.create_identity(values_hash)
15
- identity = {}
16
- values_hash.each {|k,v| identity[k] = v}
17
- identity
16
+ # sets shib headers directly
17
+ def self.set_headers(request: nil, identity: nil)
18
+ identity.each { |k, v| request.env[k] = v }
18
19
  end
19
-
20
- #sets shib headers directly
21
- def self.set_headers(request: nil,identity: nil)
22
- identity.each {|k,v| request.env[k] = v }
23
- end
24
-
25
20
  end
26
21
  end
data/config/routes.rb CHANGED
@@ -1,9 +1,15 @@
1
+ settings = JSON.parse(File.read("config/saml/#{Rails.env}/settings.json"))
2
+ # NOTE there are two saml dirs, 1 in config and 1 in dummy. They need to be in
3
+ # both places so users can run their tests, but the gem can develop with it's
4
+ # own tests as well
5
+
1
6
  SamlCamel::Engine.routes.draw do
2
7
  get "/" => "saml#index"
3
8
  get "/attributes" => 'saml#attr_check'
4
9
  get "/failure" => 'saml#failure'
10
+ get "/metadata" => 'saml#metadata'
11
+ get "/testAuthn" => 'saml#force_authn' if settings.dig('settings', 'test_auth_path')
5
12
  post "/consumeSaml" => "saml#consume"
13
+ post "/consumeSaml/rawResponse" => "saml#raw_response"
6
14
  post "/logout" => "saml#logout"
7
15
  end
8
- #TODO check ip_address stored by the IDP
9
- #TODO chceck ip address on every check ra
@@ -0,0 +1,25 @@
1
+ MIIEWjCCA0KgAwIBAgIJAP1rB/FjRgy6MA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNV
2
+ BAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEPMA0GA1UEBxMGRHVyaGFt
3
+ MRgwFgYDVQQKEw9EdWtlIFVuaXZlcnNpdHkxDDAKBgNVBAsTA09JVDEaMBgGA1UE
4
+ AxMRc2hpYi5vaXQuZHVrZS5lZHUwHhcNMTAwOTA5MTI0NDU1WhcNMjgwOTA0MTI0
5
+ NDU1WjB7MQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzAN
6
+ BgNVBAcTBkR1cmhhbTEYMBYGA1UEChMPRHVrZSBVbml2ZXJzaXR5MQwwCgYDVQQL
7
+ EwNPSVQxGjAYBgNVBAMTEXNoaWIub2l0LmR1a2UuZWR1MIIBIjANBgkqhkiG9w0B
8
+ AQEFAAOCAQ8AMIIBCgKCAQEAt+hnl6gSRi0Y8VuNl6PCPYejj7VfVs/y8bRa5zAY
9
+ RHwb75+vBSs2j1yeUcSore9Ba5Ni7v947V34afRMGRPOqr4TEDZxU+1Bg0zAvSrR
10
+ n4Y8B+zyJuhtOpmOZzTwE9o/Oc+CB4kYV/K0woKZdcoxHJm8TbqBqdxU4fFYUlNU
11
+ o4Dr5jRdCSr9MHBOqGWXtQMg16qYNB7StNk4twY29FNnpZwkVTfsE76uVsRMkG8i
12
+ 6/RiHpXZ/ioOOqndptbEGdsOIE3ivAJOZdvYwnDe5NnTH06P01HsxH3OOnYqhuG2
13
+ J6qdhqoelGeHRG+jfl8YkYXCcKQvja2tJ5G+6iqSN7DP6QIDAQABo4HgMIHdMB0G
14
+ A1UdDgQWBBQHYXwB6otkfyMOmUI59j8823hFRDCBrQYDVR0jBIGlMIGigBQHYXwB
15
+ 6otkfyMOmUI59j8823hFRKF/pH0wezELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5v
16
+ cnRoIENhcm9saW5hMQ8wDQYDVQQHEwZEdXJoYW0xGDAWBgNVBAoTD0R1a2UgVW5p
17
+ dmVyc2l0eTEMMAoGA1UECxMDT0lUMRowGAYDVQQDExFzaGliLm9pdC5kdWtlLmVk
18
+ dYIJAP1rB/FjRgy6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAG7q
19
+ wJpiSLJbx2gj/cGDYeuBW/CeRGNghjQ/mb076P3WXsRNPAimcXulSUbQkS6eDH4t
20
+ Ifvsa0jf4FRsEOwH/x8354/0wyv4RwuavX25kjpmoFn3O+eKokyzsc7/Q2gsm0mv
21
+ V8XQo+5b+4we8AFYlAVp26nLeIqAiJM8xZJ9yHuzVL1O4yxIWIKECWHLqY5+1nas
22
+ XNiLURrHhsK5pZUPLuhzJFgZuJT62TtnrjJXlrRhJ389VSkh6R64C6ncjNkg6/Cu
23
+ tA6SX0infqNRyPRNJK+bnQd1yOP4++tjD/lAPE+5tiD/waI3fArt43ZE/qp7pYMS
24
+ 9TEfyQ5QpfRYAUFWXBc=
25
+
@@ -0,0 +1,28 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEuTCCA6GgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMCVVMx
3
+ FzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQHDAZEdXJoYW0xGDAWBgNV
4
+ BAoMD0R1a2UgVW5pdmVyc2l0eTEMMAoGA1UECwwDT0lUMR0wGwYDVQQDDBRzYW1s
5
+ IGNhbWVsIGR1bW15IGFwcDEdMBsGCSqGSIb3DQEJARYOZGExMjlAZHVrZS5lZHUw
6
+ HhcNMTgwNTIyMTcxMDMwWhcNMTkwNTIyMTcxMDMwWjCBnTELMAkGA1UEBhMCVVMx
7
+ FzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQHDAZEdXJoYW0xGDAWBgNV
8
+ BAoMD0R1a2UgVW5pdmVyc2l0eTEMMAoGA1UECwwDT0lUMR0wGwYDVQQDDBRzYW1s
9
+ IGNhbWVsIGR1bW15IGFwcDEdMBsGCSqGSIb3DQEJARYOZGExMjlAZHVrZS5lZHUw
10
+ ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+OHs74gT5AmdSsLgHETvX
11
+ 50+S0NgWp5dcovfuMYFV+1CFX1MhgjhBQSwkA9U/0pfKf/eoU18O2gI2y46OK8j2
12
+ e5oyUuKv1UQWe2RHKvxvNrwvvUVcLY4mJDZf0d4q6EyTVo2aWHwoskxnQpjbusgp
13
+ Vq178Jfaeu/QaiBtq82vPlu0tfCeOXIyEdyRiOyc2bQvS5MW6FvzWtgatiNUnJJe
14
+ sBM/JUiFOvf3qG7LHEzpaIBmoHBwxG5b3yjrGgGTdw+5gyXdPEwEeiTddMvYlXWM
15
+ t+VMoTmsaBxrXRJBvpLxGWHZRb0VcoVTqWjcKVD/hR0A7H6ogaoOatHDWM41b3ZL
16
+ AgMBAAGjggEAMIH9MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGh/Y36w7wcL
17
+ nLXFC0dUpboAAV+ZMIHKBgNVHSMEgcIwgb+AFGh/Y36w7wcLnLXFC0dUpboAAV+Z
18
+ oYGjpIGgMIGdMQswCQYDVQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmEx
19
+ DzANBgNVBAcMBkR1cmhhbTEYMBYGA1UECgwPRHVrZSBVbml2ZXJzaXR5MQwwCgYD
20
+ VQQLDANPSVQxHTAbBgNVBAMMFHNhbWwgY2FtZWwgZHVtbXkgYXBwMR0wGwYJKoZI
21
+ hvcNAQkBFg5kYTEyOUBkdWtlLmVkdYIBADANBgkqhkiG9w0BAQsFAAOCAQEAFE/X
22
+ DPipapLFDnu2jCMR4lhDeEF2Pm1DIibiy6ZvmzCstj++MYOI7gKkUgeUUhFTEQIV
23
+ fZIo5gIWkyoPVOwGALLTme01Tdk3Mul4pV0iqMn4k3F9NsC9wRy4WR2yPF9GYa/e
24
+ ktK+ZBYt/2SZA4vS5q63jsMC0TjkrTGJokXohwScWDc4kIFfvU6biWW7zBCVfpaa
25
+ YfsLYNBTbZ7VqEVFzcpYv8LBTOYoToAS5+yuAwrIdPEfqx3R4tIwGCik4tSByQFO
26
+ i/VvEL5rTWhmUrKPh1hriPVYZ9gW2Mk87Snlyswsqv5d8+ITVgF+RL+cutUA29C+
27
+ moSLPLaWINlhqvuRXw==
28
+ -----END CERTIFICATE-----
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEogIBAAKCAQEAvjh7O+IE+QJnUrC4BxE71+dPktDYFqeXXKL37jGBVftQhV9T
3
+ IYI4QUEsJAPVP9KXyn/3qFNfDtoCNsuOjivI9nuaMlLir9VEFntkRyr8bza8L71F
4
+ XC2OJiQ2X9HeKuhMk1aNmlh8KLJMZ0KY27rIKVate/CX2nrv0GogbavNrz5btLXw
5
+ njlyMhHckYjsnNm0L0uTFuhb81rYGrYjVJySXrATPyVIhTr396huyxxM6WiAZqBw
6
+ cMRuW98o6xoBk3cPuYMl3TxMBHok3XTL2JV1jLflTKE5rGgca10SQb6S8Rlh2UW9
7
+ FXKFU6lo3ClQ/4UdAOx+qIGqDmrRw1jONW92SwIDAQABAoIBAHZpuKU9fPT5/xHl
8
+ upmDq+oqL0nowivQJhRfytE3dhjtOmHcRma8poJQrMa6sBxr31wKr0PUqn8XTXuI
9
+ 2fQ843w003dyS3VD4H/STklTRBODUkCxpSTNowixUDvz7EZvl4O8xKeJX7kBzTgW
10
+ qAtYydOaBqL50b4K+5CVEBzVb1Qf/DKhCbBeYvnwAcUVT+t5lDGUh+54pLTHmeGZ
11
+ 2as+1MeBWLMR/ynMDziVVR3XIM02+pHPEwiI9ZTazUAKRJnskb5gBpHqtGiZSijC
12
+ zQq+GSnnBPvvc0gtjqf+KF/6NLy/zDGmpF1e+blCnnLPUQGPTkClq59EHdn8jedO
13
+ YyRrWmkCgYEA9VqRMziTAi79yP2rLqE7cMKPDtrOilHK8fDk5N2xxzEsVoKUsotq
14
+ x384sfmrA3oVSNQsPi/DF16eH1cLaQL86rTaUKl4DqO6rLBPhQVjrmuwdWgnKKGn
15
+ 9XMEp8lBC7KwAnaQKP7c83WarU/FbF08BbPkHob1wuAyMrD7wRv2XDcCgYEAxnl8
16
+ SuHwIooIyiW2/oDjoqCrdtgOLXzdOK2OSDcY+jARVkOA8N0ingPOb18RLOTmjGk5
17
+ KZDHa8xZzdd0Bt7xz3WV2FipYxnkkY7sJosJpMrY8k/QUip9i2D04uLypwVBfT7P
18
+ q3GOgOrP+nvRya8HLHKm0rf7+sU2mGIsSrVYtI0CgYBzQUIoL5FPW0e4XQFG/FJx
19
+ 29NcBQk1DMsq8CB2KnZSvhS35st3O+rDIE4/vKrLDVRmS9UkuUcJ+VaKHler0s2A
20
+ a8iKT7GoHt2YNZKFSEzVKJ1R6cVLXvUJZihvsSivGBd6cLuzplWgwEQS2gBBsWJ6
21
+ w1CLzpYwHyU1jtIUmtAV7QKBgCtC3bnAx8PvjHzrfZi55WRUWyt7apO1rM6m3eWV
22
+ xOb7xTulWRynRt1kfQG/mhHMDwi6AtCxkxZHI6f/d3Xr8I9E1RWkNb+5LB4iJg08
23
+ ryxxXppqlUDjrBvOVXKC/1syhRTUtRVsmiA1joHNrWulsA2bLAuwOMdvZzgN5hOe
24
+ tagdAoGAP7kdbprmkT/7xX8puX6WD4MXQ+dgyb3FvpCIfQT8x0t/ndMI2wMc4keg
25
+ woD2L56tjtVyFH8LQz1sU7LroSc8XF2joZOdQePrnyTVUISoMiTqaXMPIO6l6pez
26
+ x7g1PP3ey5LOoX7LG5ule/6qNMtRhVOFok0vA9ZuuIIkkmYSo1c=
27
+ -----END RSA PRIVATE KEY-----