saml_camel 0.1.4 → 0.1.5

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: 9587f9aa4978c5bfc1ff83fd93490b77cfada4a0
4
- data.tar.gz: d005f8ba385cd0443baeda86c668d002c7907857
3
+ metadata.gz: 60103dee96fb0b95caeb04551deac81568e5149a
4
+ data.tar.gz: eb15b9fc8c642e88bf0ff8be268f206de1be3b73
5
5
  SHA512:
6
- metadata.gz: 012d8b91632f47e689f385cb1de8f82e7e99696579e11491ce94e54de0823f0b56f727bd25b5e748cc1c288db6debaf57f2aa5df3d7b83a3ffdc1a20514b779a
7
- data.tar.gz: de3c6af5a1395e1a44502b9f85101f8e42d238362443ff7826037ec96d783712329ae028370365b2b514f4f338287e04247a3e906ae1b0dd04ac0e4bb8e2817e
6
+ metadata.gz: acda10e48b2cabdb24f187ffaf3fde6dbb5992efe032f56ca623098999b754c9094fe94271cbeac50097f00679a87710db95ebe96be7a22b0904607a7b62c165
7
+ data.tar.gz: 8ea077c92e5d0fcf0df9bcae2d086a5635152ceb3d37afbc7a0e7e4777a3d9dda32b651280d9bc54f12ecdf4f859c640c658e68ff972521d0473e25f9d465233
data/README.md CHANGED
@@ -14,6 +14,13 @@ $ bundle
14
14
 
15
15
 
16
16
  ## Usage
17
+ ### IMPORTANT!
18
+ 1. in your environments config (`config/development.rb` for example) ensure that you have caching configured as follows
19
+ ```ruby
20
+ config.action_controller.perform_caching = true
21
+ config.cache_store = :memory_store
22
+ ```
23
+
17
24
  1. run `rake saml_camel:generate_saml` to generate metadata files for each environment. you can also specify a specifc/custom environment like this `rake saml_camel:generate_saml environment=acceptance`
18
25
 
19
26
  **Note: these steps will use development as an example, if you use seperate metadata per environemtn, you will repeat each step for your chosed environement**
@@ -57,7 +64,7 @@ end
57
64
  ```
58
65
 
59
66
 
60
- 7. to logout simply make a post to `localhost:3000/saml/logout`. This will kill the local saml session, and the session with the identity provider. You can specify a return url in `saml/development/settings.json`
67
+ 7. to logout simply make a post to `localhost:3000/saml/logout`. This will kill the local saml session, and the session with the identity provider.
61
68
 
62
69
  7. response attributes found in `session[:saml_attributes]`
63
70
 
@@ -8,39 +8,48 @@ module SamlCamel::SamlHelpers
8
8
  #this generates a call to the idp, which will then be returned to the consume action the in saml_contorller
9
9
  def saml_request(host_request)
10
10
  relay_state = SecureRandom.base64.chomp.gsub( /\n/, '' ) #set relay state to secure against replay attack
11
- session[:relay_state] = relay_state
12
11
  request = OneLogin::RubySaml::Authrequest.new
13
12
 
14
- secure_cookie = (Rails.env == "development" || Rails.env == "test") ? false : true
15
- cookies.encrypted[:saml_camel_redirect] = {
16
- value: host_request.url,
17
- secure: secure_cookie,
18
- httponly: true
19
- }
20
-
21
- cookies.encrypted[:saml_camel_relay] = {
22
- value: relay_state,
23
- secure: secure_cookie,
24
- httponly: true
25
- }
26
-
27
- saml_request = request.create(SamlCamel::Transaction.saml_settings) + "&RelayState=#{relay_state}"
28
- redirect_to(saml_request)
13
+ #store relay state, ip address and original url request in memory to be used for verification and redirect after response
14
+ permit_key = session[:session_id].to_sym
15
+ Rails.cache.fetch(permit_key, expires_in: 5.minutes) do
16
+ {ip_address: host_request.remote_ip, relay_state: relay_state, redirect_url: host_request.url }
17
+ end
18
+ saml_request_url = request.create(SamlCamel::Transaction.saml_settings) + "&RelayState=#{Rails.cache.fetch(permit_key)[:relay_state]}"
19
+ redirect_to(saml_request_url)
29
20
  end
30
21
 
31
22
 
32
- def valid_relay_state(param_relay_state)
33
- stored_relay = cookies.encrypted[:saml_camel_relay]
34
- cookies.delete :saml_camel_relay
35
- param_relay_state == stored_relay
23
+ #validates the user ip address and relay state given to IDP. Prevents Replay Attacks.
24
+ def valid_state(param_relay_state, remote_ip)
25
+ permit_key = session[:session_id].to_sym
26
+ saml_cache = Rails.cache.fetch(permit_key)
27
+ stored_relay = saml_cache[:relay_state]
28
+ stored_ip = saml_cache[:ip_address]
29
+
30
+ SamlCamel::Logging.saml_state({stored_relay: stored_relay, request_relay: param_relay_state, stored_ip: stored_ip, remote_ip: remote_ip})
31
+ param_relay_state == stored_relay && remote_ip == stored_ip
36
32
  end
37
33
 
38
34
 
39
- def saml_protect
40
- saml_request(request) unless (session[:saml_success] || session[:sp_session]) #keeps us from looping, and maintains sp session
41
- session[:saml_success] = nil
35
+ #Make it so sp sessions only last 1 hour, sp_session is set on a succesfull saml response.
36
+ #We check that the session time is less than in hour if so we refresh, otherwise we delete the session
37
+ def expired_session?
38
+ if session[:sp_session]
39
+ if (Time.now - Time.parse(session[:sp_session])) < 1.hour
40
+ session[:sp_session] = Time.now
41
+ return true
42
+ end
43
+ end
42
44
  end
43
45
 
44
46
 
47
+ #saml_protect is what is called in the app. it initiates the saml request if there is no active session, or if a user has been idle for over an hour
48
+ #TODO re-factor in future
49
+ def saml_protect
50
+ not_expired = expired_session?
51
+ saml_request(request) unless (session[:saml_success] || session[:sp_session] || not_expired) #keeps us from looping, and maintains sp session
52
+ session[:saml_success] = nil
53
+ end
45
54
 
46
55
  end
@@ -7,30 +7,34 @@ module SamlCamel
7
7
  before_action :saml_protect, only: [:attr_check]
8
8
 
9
9
 
10
- #TODO ROUTABLE STUFF GOES IN THE SHIB CONTROLLER, METHODS CALLED BUT NOT ROUTED GO TO SAML_CONTROLLER
10
+ #convinence route to see attributes that are coming through
11
11
  def index
12
12
  @attributes = session[:saml_attributes]
13
13
  end
14
14
 
15
- def consume
16
- raise "Invalid RelayState" unless valid_relay_state(params[:RelayState])
17
- redirect_path = cookies.encrypted[:saml_camel_redirect]
18
15
 
19
- cookies.delete :saml_camel_redirect
16
+ #consumes the saml response from the IDP
17
+ def consume
18
+ raise "Invalid RelayState" unless valid_state(params[:RelayState], request.remote_ip)
19
+ permit_key = session[:session_id].to_sym
20
+ redirect_path = Rails.cache.fetch(permit_key)[:redirect_url]
21
+ Rails.cache.delete(permit_key) #we no longer need cache at this stage
20
22
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :settings => saml_settings)
21
23
  response.settings = saml_settings
22
-
23
24
  if response.is_valid? # validate the SAML Response
24
25
  # authorize_success, log the user
25
26
  session[:saml_success] = true
26
- session[:sp_session] = true
27
+ session[:sp_session] = Time.now
28
+
27
29
  session[:saml_attributes] = SamlCamel::Transaction.map_attributes(response.attributes)
28
30
  SamlCamel::Logging.successfull_auth(session[:saml_attributes])
29
31
 
30
32
  #TODO account for nil redirect
31
33
  redirect_to redirect_path
32
34
  else # otherwise list out the errors in the response
33
- #TODO how do we handle errors?
35
+ permit_key = session[:session_id].to_sym
36
+ Rails.cache.delete(permit_key)
37
+
34
38
  session[:saml_success] = false
35
39
  response.errors
36
40
  SamlCamel::Logging.auth_failure(response.errors)
@@ -38,25 +42,33 @@ module SamlCamel
38
42
  redirect_to main_app.try('root_path')
39
43
  end
40
44
  rescue => e
45
+ permit_key = session[:session_id].to_sym
46
+ Rails.cache.delete(permit_key)
41
47
  session[:saml_success] = false
48
+
42
49
  SamlCamel::Logging.auth_failure(e)
43
50
  redirect_to action: "failure", locals:{errors: e}
44
51
  end
45
52
 
53
+
54
+ #route to show saml failures
46
55
  def failure
47
56
  @error = params[:locals][:errors]
48
- # byebug
49
57
  end
50
58
 
59
+
60
+ #kills SP session and redirects to IDP to kill idp session
51
61
  def logout
52
62
  SamlCamel::Logging.logout(session[:saml_attributes])
53
63
  session[:saml_attributes] = nil
54
64
  session[:sp_session] = nil
65
+ cookies.delete :saml_camel_timestamp
55
66
 
56
67
  # return_url = SamlCamel::Transaction.logout #this methods logs the user out of the IDP, and returns a url to be redirected to
57
68
  redirect_to "https://shib.oit.duke.edu/cgi-bin/logout.pl"
58
69
  end
59
70
 
71
+
60
72
  def attr_check
61
73
  end
62
74
 
@@ -25,5 +25,12 @@ module SamlCamel
25
25
  logger.debug("Unknown error logging user logout. Most likely anonymous user clicked a logout button.")
26
26
  end
27
27
 
28
+ def self.saml_state(data)
29
+ logger = Logger.new("log/saml.log")
30
+ logger.info("Stored Relay: #{data[:stored_relay]} | RequestRelay: #{data[:request_relay]} | Stored IP: #{data[:stored_ip]} RemoteIP: #{data[:remote_ip]}")
31
+ rescue
32
+ logger.debug("Unknown Error During relay state logging.")
33
+ end
34
+
28
35
  end
29
36
  end
data/config/routes.rb CHANGED
@@ -5,3 +5,5 @@ SamlCamel::Engine.routes.draw do
5
5
  post "/consumeSaml" => "saml#consume"
6
6
  post "/logout" => "saml#logout"
7
7
  end
8
+ #TODO check ip_address stored by the IDP
9
+ #TODO chceck ip address on every check ra
@@ -1,3 +1,3 @@
1
1
  module SamlCamel
2
- VERSION = '0.1.4'
2
+ VERSION = '0.1.5'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saml_camel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - 'Danai Adkisson '
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-10 00:00:00.000000000 Z
11
+ date: 2018-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails