saml_camel 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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-----