Module: SamlCamel::SamlHelpers

Extended by:
ActiveSupport::Concern
Included in:
SamlController
Defined in:
app/controllers/concerns/saml_camel/saml_helpers.rb

Constant Summary collapse

SP_SETTINGS =
JSON.parse(File.read("saml/#{Rails.env}/settings.json"))

Instance Method Summary collapse

Instance Method Details

#assign_permit_keyObject



130
131
132
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 130

def assign_permit_key
  session[:saml_session_id] = SecureRandom.base64.chomp.gsub( /\W/, '' )
end

#cache_available?(app_cache) ⇒ Boolean

Returns:

  • (Boolean)


112
113
114
115
116
117
118
119
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 112

def cache_available?(app_cache)
  if app_cache
    true
  else
    session[:sp_session] = nil
    false
  end
end

#duplicate_response_id?(response_id) ⇒ Boolean

ensures that a saml response can not be used more than once. stores response ids and checks to see if the response id has already been used.

Returns:

  • (Boolean)


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 26

def duplicate_response_id?(response_id)
  ids = Rails.cache.fetch("response_ids")
  if ids
    if ids.include?(response_id)
      session[:sp_session] = nil
      raise "SAML response ID already issued."
    else
      ids << response_id
      Rails.cache.fetch("response_ids", expires_in: 1.hours) do
        ids
      end
    end
  else
    Rails.cache.fetch("response_ids", expires_in: 1.hours) do
      []
    end
  end
end

#expired_session?Boolean

Make it so sp sessions only last 1 hour, sp_session is set on a succesfull saml response. We check that the session time is less than in hour if so we refresh, otherwise we delete the session

Returns:

  • (Boolean)


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 56

def expired_session?
  permit_key = session[:saml_session_id].to_sym
  user_cache = Rails.cache.fetch(permit_key)
  cache_available?(user_cache)

  sp_timeout = SP_SETTINGS["settings"]["sp_session_timeout"]
  sp_lifetime = SP_SETTINGS['settings']["sp_session_lifetime"]

  set_saml_session_lifetime(permit_key) if user_cache[:session_start_time].nil?
  sp_session_init_time = user_cache[:session_start_time]

  if session[:sp_session]
    #if the session has timed out remove session, otherwise refresh
    if (Time.now - Time.parse(session[:sp_session])) < sp_timeout.hour
      session[:sp_session] = Time.now
    else
      SamlCamel::Logging.expired_session(session[:saml_attributes])
      session[:sp_session] = nil
    end

    #if the session has exceeded the allowed lifetime, remove session
    if (Time.now - sp_session_init_time) > sp_lifetime.hour
      SamlCamel::Logging.expired_session(session[:saml_attributes])
      session[:sp_session] = nil
    end
  end
end

#saml_protectObject

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



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 97

def saml_protect
  user_cache = cache_available?(Rails.cache.fetch(session[:saml_session_id]))
  if session[:saml_session_id] && user_cache
    #sets session[:sp_session] to nil if expired, of if the ip adress changes

    expired_session?
    valid_ip?(request.remote_ip)
    saml_request(request) unless (session[:saml_response_success] || session[:sp_session])
  else
    saml_request(request)
  end
  session[:saml_response_success] = nil #keeps us from looping
end

#saml_request(host_request) ⇒ Object

this generates a call to the idp, which will then be returned to the consume action the in saml_contorller



8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 8

def saml_request(host_request)
  request = OneLogin::RubySaml::Authrequest.new
  assign_permit_key
  lifetime =  SP_SETTINGS['settings']["sp_session_lifetime"]
  permit_key = session[:saml_session_id].to_sym

  #store ip address and original url request in memory to be used for verification and redirect after response
  Rails.cache.fetch(permit_key, expires_in: lifetime.hours) do
    {ip_address: host_request.remote_ip,  redirect_url:  host_request.url }
  end

  saml_request_url = request.create(SamlCamel::Transaction.saml_settings)
  redirect_to(saml_request_url)
end

#set_saml_session_lifetime(permit_key) ⇒ Object



45
46
47
48
49
50
51
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 45

def set_saml_session_lifetime(permit_key)
  user_saml_cache = Rails.cache.fetch(permit_key)
  user_saml_cache[:session_start_time] = Time.now
  Rails.cache.fetch(permit_key, expires_in: 8.hours) do
    user_saml_cache
  end
end

#valid_ip?(remote_ip) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
90
91
92
93
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 85

def valid_ip?(remote_ip)
  permit_key = session[:saml_session_id].to_sym
  saml_cache =  Rails.cache.fetch(permit_key)
  cache_available?(saml_cache)
    unless remote_ip == saml_cache[:ip_address]
      SamlCamel::Logging.bad_ip(session[:saml_attributes], saml_cache[:ip_address], remote_ip)
      session[:sp_session] = nil
    end
end

#verify_sha_type(response) ⇒ Object



122
123
124
125
126
127
128
# File 'app/controllers/concerns/saml_camel/saml_helpers.rb', line 122

def verify_sha_type(response)
  raw_xml_string = response.decrypted_document.to_s
  attr_scan = raw_xml_string.scan(/<ds:SignatureMethod.*\/>/)
  is_sha1 = attr_scan[0].match("sha1")

  raise "SHA1 algorithm not supported " if is_sha1
end