Class: WIKK::Web_Auth
- Inherits:
-
Object
- Object
- WIKK::Web_Auth
- Defined in:
- lib/wikk_web_auth.rb
Overview
Provides common authentication mechanism for all our cgis. Uses standard cgi parameters, unless overridden e.g. cgi?user=x&response=y Returns values imbedded as hidden fields in the login form @attr_reader [String] user , the remote user’s user name @attr_reader [String] session , the persistent Session record for this user
Constant Summary collapse
- VERSION =
Gem version
'0.1.6'
Instance Attribute Summary collapse
-
#challenge ⇒ Object
readonly
Returns the value of attribute challenge.
-
#response ⇒ Object
Returns the value of attribute response.
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Class Method Summary collapse
-
.authenticated?(cgi, pstore_config: nil) ⇒ Boolean
way of checking without doing a full login sequence.
-
.logout(cgi, pstore_config: nil) ⇒ Object
get the session reference and delete the session.
-
.session_config(extra_arguments = nil, pstore_config: nil) ⇒ Object
Generate the new Session’s config parameters, mixing in and/or overriding the preset values.
Instance Method Summary collapse
-
#authenticate(return_url = nil) ⇒ Object
Test to see if we are already authenticated, and if not, generate an HTML login page.
-
#authenticated? ⇒ Boolean
Test to see if user authenticated.
-
#close_session ⇒ Object
Ensure we don’t consume all file descriptors Call after last call (though most calls do close the session).
-
#gen_challenge ⇒ Object
Generate a challenge, as step 1 of a login If this is the only call, then follow this with close_session().
-
#gen_html_login_page(message: '') ⇒ Object
Used by calling cgi to generate a standard login page.
-
#html_logout_form(cgi_dir) ⇒ Object
Used by calling cgi to generate logout with this form.
-
#html_reload(url = nil) ⇒ Object
Used by calling cgi to inject a return URL into the html response.
-
#initialize(cgi, pwd_config = nil, return_url = nil, user: nil, response: nil, user_logout: false, pstore_config: nil, run_auth: true) ⇒ Web_Auth
constructor
Create new Web_Auth instance, and proceed through authentication process by creating a login web form, if the user isn’t authenticated.
-
#logout ⇒ Object
clean up the session, deleting the session state.
-
#session_id ⇒ String
expose the session_id.
-
#session_to_s ⇒ Object
Debug dump of session keys.
-
#valid_response? ⇒ Boolean
Test the response against the password file If this is the only call, then follow this with close_session().
Constructor Details
#initialize(cgi, pwd_config = nil, return_url = nil, user: nil, response: nil, user_logout: false, pstore_config: nil, run_auth: true) ⇒ Web_Auth
Create new Web_Auth instance, and proceed through authentication process by creating a login web form, if the user isn’t authenticated. @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data. @param pwd_config [WIKK::Configuration|Hash] the location of the password file is embedded here. @param user [String] overrides cgi[‘user’] @param response [String] overrides cgi[‘response’] @param user_logout [Boolean] overrides cgi[‘logout’] @param pstore_config [Hash] overrides default pstore settings @param return_url [String] If we successfully authenticate, return here. @return [WIKK::Web_Auth]
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/wikk_web_auth.rb', line 29 def initialize(cgi, pwd_config = nil, return_url = nil, user: nil, response: nil, user_logout: false, pstore_config: nil, run_auth: true) if pwd_config.instance_of?(Hash) sym = pwd_config.each_with_object({}) { |(k, v), h| h[k.to_sym] = v } @config = Struct.new(*(k = sym.keys)).new(*sym.values_at(*k)) else @pwd_config = pwd_config end @cgi = cgi @pstore_config = pstore_config # Set variables from the method's params, or alternately, from the CGI params @user = user.nil? ? cgi_param('Username') : user @response = response.nil? ? cgi_param('Response') : response @return_url = return_url.nil? ? cgi_param('ReturnURL') : return_url # Look for existing session, but don't start a new one. begin @session = CGI::Session.new(@cgi, Web_Auth.session_config( { 'new_session' => false }, pstore_config: @pstore_config )) rescue ArgumentError => _e # if no old session @session = nil rescue Exception => e # rubocop:disable Lint/RescueException In CGI, we want to handle every exception raise e.class, 'Authenticate, CGI::Session.new ' + e. end if @session.nil? @challenge = '' # there is no current challenge elsif @session['session_expires'].nil? || # Shouldn't be the case @session['session_expires'] < Time.now || # Session has expired @session['ip'] != @cgi.remote_addr || # Not coming from same IP address # @session['user'] != @user || # Not the same user cgi_param('logout') != '' || # Requested a logout user_logout # Alternate way to request a logout logout else # We ignore the cgi['Challenge'] value, and always get this from the pstore @challenge = @session['seed'] # Recover the challenge from the pstore entry. It may be '' end authenticate if run_auth # This generates html output, so it is now conditionally run. end |
Instance Attribute Details
#challenge ⇒ Object (readonly)
Returns the value of attribute challenge.
17 18 19 |
# File 'lib/wikk_web_auth.rb', line 17 def challenge @challenge end |
#response ⇒ Object
Returns the value of attribute response.
18 19 20 |
# File 'lib/wikk_web_auth.rb', line 18 def response @response end |
#user ⇒ Object (readonly)
Returns the value of attribute user.
17 18 19 |
# File 'lib/wikk_web_auth.rb', line 17 def user @user end |
Class Method Details
.authenticated?(cgi, pstore_config: nil) ⇒ Boolean
way of checking without doing a full login sequence. @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data. @param pstore_config [Hash] overrides default pstore settings @return [Boolean] authenticated == true.
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/wikk_web_auth.rb', line 95 def self.authenticated?(cgi, pstore_config: nil ) begin session = CGI::Session.new(cgi, Web_Auth.session_config( { 'new_session' => false }, pstore_config: pstore_config ) ) authenticated = (session != nil && !session['session_expires'].nil? && session['session_expires'] > Time.now && session['auth'] == true && session['ip'] == cgi.remote_addr) session.close # Tidy up, so we don't leak file descriptors return authenticated rescue ArgumentError => _e # if no old session to find. return false end end |
.logout(cgi, pstore_config: nil) ⇒ Object
get the session reference and delete the session. @param pstore_config [Hash] overrides default pstore settings @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data.
116 117 118 119 120 121 122 123 |
# File 'lib/wikk_web_auth.rb', line 116 def self.logout(cgi, pstore_config: nil) begin session = CGI::Session.new(cgi, Web_Auth.session_config( { 'new_session' => false }, pstore_config: pstore_config )) session.delete unless session.nil? # Also closes the session rescue ArgumentError => _e # if no old session # Not an error. end end |
.session_config(extra_arguments = nil, pstore_config: nil) ⇒ Object
Generate the new Session’s config parameters, mixing in and/or overriding the preset values. @param pstore_config [Hash] Override the default pstore configurations. Only changed keys need to be included @param extra_arguments [Hash] Extra arguments that get added to the hash. Will also override values with the same key. @return [Hash] The configuration hash.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/wikk_web_auth.rb', line 137 def self.session_config( extra_arguments = nil, pstore_config: nil ) instance_of?(Hash) session_conf = { 'database_manager' => CGI::Session::PStore, # use PStore 'session_key' => '_wikk_rb_sess_id', # custom session key 'session_expires' => (Time.now + 86400), # 1 day timeout 'prefix' => 'pstore_sid_', # Prefix for pstore file # 'suffix' => ? 'tmpdir' => '/tmp', # PStore option. Under Apache2, this is a private namespace /tmp 'session_path' => '/', # The cookie gets returned for URLs starting with this path # 'new_session' => true, # Default, is to create a new session if it doesn't already exist # 'session_domain' => ?, # 'session_secure' => ?, # 'session_id' => ?, # Created for new sessions. Merged in for existing sessions 'no_cookies' => false, # boolean. Do fill in cgi output_cookies array of Cookies 'no_hidden' => false # boolean fill in the cgi output_hidden Hash key=cookie, value=session_id } session_conf.merge!(pstore_config) if pstore_config.instance_of?(Hash) session_conf.merge!(extra_arguments) if extra_arguments.instance_of?(Hash) return session_conf end |
Instance Method Details
#authenticate(return_url = nil) ⇒ Object
Test to see if we are already authenticated, and if not, generate an HTML login page. @param return_url [String] We return here if we sucessfully login. Overrides initialize value
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/wikk_web_auth.rb', line 194 def authenticate(return_url = nil) @return_url = return_url unless return_url.nil? # Update the return url (Backward compatibility) # We have no session setup, or haven't sent the challenge yet. # So we are at step 1 of the authentication if @session.nil? || @challenge == '' gen_html_login_page(message: 'no current challenge') return end # We are now at step 2, expecting a response to the challenge begin # Might be a while since we initialized the class, so repeat this test @session['auth'] = false if @session['session_expires'].nil? || # Shouldn't ever happen, but has @session['session_expires'] < Time.now || # Session has expired @session['ip'] != @cgi.remote_addr # || # Not coming from same IP address # @session['user'] != @user # Username not the same as the session return if @session['auth'] == true # if this is true, then we have already authenticated this session. hold_challenge = @challenge unless valid_response? gen_html_login_page(message: "invalid response: '#{@user}' '#{hold_challenge}' '#{@response}'") end @session.close unless @session.nil? # Saves the session state. rescue Exception => e # rubocop:disable Lint/RescueException raise e.class, 'Authenticate, CGI::Session.new ' + e. end end |
#authenticated? ⇒ Boolean
Test to see if user authenticated. If this is the only call, then follow this with close_session() @return [Boolean] True, if this session is authenticated
109 110 111 |
# File 'lib/wikk_web_auth.rb', line 109 def authenticated? @session != nil && !@session['session_expires'].nil? && @session['session_expires'] > Time.now && @session['auth'] == true && @session['ip'] == @cgi.remote_addr end |
#close_session ⇒ Object
Ensure we don’t consume all file descriptors Call after last call (though most calls do close the session)
226 227 228 229 |
# File 'lib/wikk_web_auth.rb', line 226 def close_session @session.close unless @session.nil? @session = nil end |
#gen_challenge ⇒ Object
Generate a challenge, as step 1 of a login If this is the only call, then follow this with close_session()
161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/wikk_web_auth.rb', line 161 def gen_challenge # Short session, which gets replaced if we successfully authenticate new_session( { 'session_expires' => Time.now + 120 } ) raise 'gen_challenge: @session == nil' if @session.nil? @challenge = SecureRandom.base64(32) # Store the challenge in the pstore, ready for the 2nd login step, along with browser details session_state_init('auth' => false, 'seed' => @challenge, 'ip' => @cgi.remote_addr, 'user' => @user, 'session_expires' => @session_options['session_expires']) @session.update return @challenge end |
#gen_html_login_page(message: '') ⇒ Object
Used by calling cgi to generate a standard login page
232 233 234 235 236 237 238 239 240 241 |
# File 'lib/wikk_web_auth.rb', line 232 def gen_html_login_page(message: '') gen_challenge @cgi.header('type' => 'text/html') @cgi.out do @cgi.html do @cgi.head { @cgi.title { 'login' } + html_nocache + html_script } + @cgi.body { html_login_form(message: ) + "\n" } end end end |
#html_logout_form(cgi_dir) ⇒ Object
Used by calling cgi to generate logout with this form. @param cgi_dir [String] directory holding the login.rbx cgi. @return [String] Html logout form.
258 259 260 261 262 263 264 |
# File 'lib/wikk_web_auth.rb', line 258 def html_logout_form(cgi_dir) <<~HTML <form NAME="login" ACTION="#{cgi_dir}/login.rbx" METHOD="post"> <input TYPE="submit" NAME="logout" VALUE="logout" > </form> HTML end |
#html_reload(url = nil) ⇒ Object
Used by calling cgi to inject a return URL into the html response. Called by calling cgi, when constructing their html headers. @param url [String] URL to redirect to. @return [String] The HTML meta header, or “”, if url is empty.
247 248 249 250 251 252 253 |
# File 'lib/wikk_web_auth.rb', line 247 def html_reload(url = nil) if url != nil && url != '' "<meta http-equiv=\"Refresh\" content=\"0; URL=#{url}\">\n" else '' end end |
#logout ⇒ Object
clean up the session, deleting the session state.
126 127 128 129 130 131 |
# File 'lib/wikk_web_auth.rb', line 126 def logout @session.delete unless @session.nil? # Will close the existing session @session = nil @challenge = '' # no current session, so no challenge string end |
#session_id ⇒ String
expose the session_id. This is also returned by modifying the cgi instance passed in to initialize * The cgi.output_cookies Array of Cookies gets modified if no_cookies is false (the default) * And cgi.output_hidden Hash get modified if no_hidden is false (the default)
87 88 89 |
# File 'lib/wikk_web_auth.rb', line 87 def session_id @session.nil? ? '' : @session.session_id end |
#session_to_s ⇒ Object
Debug dump of session keys
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/wikk_web_auth.rb', line 72 def session_to_s return '' if @session.nil? s = '{' [ 'auth', 'seed', 'ip', 'user', 'session_expires' ].each do |k| s += "'#{k}':'#{@session[k]}', " end s += '}' return s end |
#valid_response? ⇒ Boolean
Test the response against the password file If this is the only call, then follow this with close_session()
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/wikk_web_auth.rb', line 176 def valid_response? if # We got a challenge string, so we are on step 2 of the authentication # And have passed the password check ( authorized?() ) new_session # regenerate the cookie with a longer lifetime. raise 'valid_response?: @session == nil' if @session.nil? session_state_init('auth' => true, 'seed' => '', 'ip' => @cgi.remote_addr, 'user' => @user, 'session_expires' => @session_options['session_expires']) @session.update # Should also update on close, which we probably do next return true else # Failed to authorize. The temporary challenge session cookie is no longer valid. logout return false end end |