wikk_web_auth 0.1.3 → 0.1.6

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.
Files changed (6) hide show
  1. checksums.yaml +5 -5
  2. data/History.txt +66 -0
  3. data/README.md +3 -3
  4. data/Rakefile +1 -2
  5. data/lib/wikk_web_auth.rb +300 -191
  6. metadata +15 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a3a11d0ad1eb25e5922bf01b2a1a82ac24c7cd11
4
- data.tar.gz: 3bac1582e8ee3f9ba72d7368e43174a16342aa3c
2
+ SHA256:
3
+ metadata.gz: 671e30332d7611b59813c740329597c67743e6dfb1d14f5624b0d2f048f735ed
4
+ data.tar.gz: 370ae2207160bb1d6b7304c2451cdf9de71ea0f0da2e760e60642c9f3a8c822b
5
5
  SHA512:
6
- metadata.gz: 8fe5be8ee402f1e9c5b0070feac195be961d2b4081a0c2acc314bac7a576d603cbd8565b16a88b9b584d54f2b238f8baa9f2e2f9459e24261d18f521fcf99781
7
- data.tar.gz: d53acd59750c5479de0ef32b04e46c2b93d35cf63170d2c15204b31ada6381248b8036dce9ae1ce15b1f96e17010e42d29a3918ff127d84a9a565a6e4d2e2d29
6
+ metadata.gz: 80f9e4d1c65fdd6d96f35399ba86fa2a11ff09a505bfa8154fc6f0f3f464bb1ec9b4f5697655822439e7523cde7da2c4dddc210e78aeaeca690d499c04231cfa
7
+ data.tar.gz: 874afd4511eae12ece2931f7bce38bcb991ae6ae76660d30c53e3aa2254660fa778d3d31eed573e33b66d9dd23d3ed7cf19da263ea1f52c554ba4647c0b75cc8
data/History.txt CHANGED
@@ -1,3 +1,69 @@
1
+ robertburrowes Sat Apr 15 16:18:34 2023 +1200
2
+ deleted an end in error
3
+ robertburrowes Sat Apr 15 16:05:26 2023 +1200
4
+ removed the syslogs. Getting odd errors, depending on the context.
5
+ robertburrowes Sat Apr 15 15:53:04 2023 +1200
6
+ factor out code into new_session() Clean up old cookie entries in cgi instance.
7
+ robertburrowes Sat Apr 15 15:49:37 2023 +1200
8
+ more output from the tests
9
+ robertburrowes Wed Apr 12 18:31:54 2023 +1200
10
+ Add test for authenticated on a reconnect Add test for trying to reauthenticate, by sending a previous response.
11
+ robertburrowes Wed Apr 12 18:30:25 2023 +1200
12
+ Initialize instance vars earlier @log.error now failing. Replaced with full call, using LOG_NOTICE session and @session mix up Explicitly set no_cookies and no_hidden to false (should be default)
13
+ robertburrowes Tue Apr 11 17:56:27 2023 +1200
14
+ bumped the version
15
+ robertburrowes Tue Apr 11 17:56:01 2023 +1200
16
+ Added test/direct_test.rb Cleaned up test, moving conf files into test/conf Created test/conf/pstore for the pstore temp files (which need cleaning out after the test)
17
+ robertburrowes Tue Apr 11 15:06:47 2023 +1200
18
+ refactor: Bump version Set return_url globally Initialize @session globally Separate generating challenge into its own method (for new rpc) Optionally call to authencate in initialize (for new rpc) Added check for the username changing during login. Fail authorized step, if challenge not set (i.e. we jumped straight to step 2. nil cgi params get converted to '' seed now reset, if authorization fails. Authorized? should have been private session_state_init should have been private
19
+ robertburrowes Sun Apr 9 17:57:33 2023 +1200
20
+ not used
21
+ robertburrowes Wed Mar 29 22:03:06 2023 +1300
22
+ Test against the new lib version, not the gem
23
+ robertburrowes Wed Mar 29 22:02:44 2023 +1300
24
+ Put back lines deleted from previous version. Added nil? check on session_expires, which we are now getting.
25
+ robertburrowes Wed Mar 29 18:07:39 2023 +1300
26
+ consistent use of args.
27
+ robertburrowes Wed Mar 29 17:58:25 2023 +1300
28
+ make init more compatible with previous version
29
+ robertburrowes Wed Mar 29 16:27:00 2023 +1300
30
+ better naming for config
31
+ robertburrowes Wed Mar 29 16:13:58 2023 +1300
32
+ mixed my config files up. Need to do this more cleanly.
33
+ robertburrowes Wed Mar 29 13:17:17 2023 +1300
34
+ Change self.session_config to have a config_override: param, and to use named arguments
35
+ robertburrowes Tue Mar 28 09:28:06 2023 +1300
36
+ Give option to pass in config to class methods (and use the config passed into initialize)
37
+ robertburrowes Mon Mar 27 17:22:31 2023 +1300
38
+ Moved pstore default location, so we can test against new thin Rack version of rpc
39
+ robertburrowes Mon Jun 13 17:53:13 2022 +1200
40
+ rubcop'd
41
+ robertburrowes Mon Jun 13 17:51:24 2022 +1200
42
+ Merge branch 'master' of github.com:wikarekare/wikk_web_auth
43
+ robertburrowes Mon Jun 13 13:55:32 2022 +1200
44
+ rubocop Scripts to bash
45
+ robertburrowes Mon Jun 13 12:20:35 2022 +1200
46
+ rubocop'd
47
+ robertburrowes Sun Oct 25 21:36:01 2020 +1300
48
+ Tidy up the yard comments to fix formatting
49
+ robertburrowes Sun Oct 25 21:22:21 2020 +1300
50
+ mode change
51
+ robertburrowes Sun Oct 25 21:22:11 2020 +1300
52
+ new Hoe format
53
+ robertburrowes Sun Oct 25 21:21:50 2020 +1300
54
+ Improve dependencies to remove warning
55
+ robertburrowes Sun Oct 25 21:21:29 2020 +1300
56
+ include in repo
57
+ robertburrowes Sun Oct 25 21:21:18 2020 +1300
58
+ mv dev scripts to sbin
59
+ robertburrowes Mon Apr 13 23:14:27 2020 +1200
60
+ bump version
61
+ robertburrowes Mon Apr 13 23:14:14 2020 +1200
62
+ change logging name to match gem name
63
+ robertburrowes Mon Apr 13 23:13:37 2020 +1200
64
+ rename js to json
65
+ robertburrowes Mon Apr 13 23:13:19 2020 +1200
66
+ qualify dir for passwd.json
1
67
  robertburrowes Fri May 26 09:38:55 2017 +1200
2
68
  Bug fix: @log.err -> @log.error
3
69
  robertburrowes Mon Jun 27 12:23:02 2016 +1200
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # wikk_web_auth
2
2
 
3
- * http://wikarekare.github.com/wikk_web_auth/
4
- * Source https://github.com/wikarekare/wikk_web_auth
5
- * Gem https://rubygems.org/gems/wikk_web_auth
3
+ * Docs :: https://wikarekare.github.io/wikk_web_auth/
4
+ * Source :: https://github.com/wikarekare/wikk_web_auth
5
+ * Gem :: https://rubygems.org/gems/wikk_web_auth
6
6
 
7
7
  ## DESCRIPTION:
8
8
 
data/Rakefile CHANGED
@@ -1,5 +1,4 @@
1
1
  # -*- ruby -*-
2
-
3
2
  require 'rubygems'
4
3
  require 'hoe'
5
4
  Hoe.plugin :yard
@@ -12,7 +11,7 @@ Hoe.spec 'wikk_web_auth' do
12
11
  self.yard_title = 'wikk_web_auth'
13
12
  self.yard_options = ['--markup', 'markdown', '--protected']
14
13
 
15
- self.dependency "wikk_password", [">= 0.1.0"]
14
+ self.dependency "wikk_password", ['~> 0.1', '>= 0.1.0']
16
15
  end
17
16
 
18
17
 
data/lib/wikk_web_auth.rb CHANGED
@@ -1,263 +1,372 @@
1
- module WIKK
2
- require 'cgi'
3
- require 'cgi/session'
1
+ module WIKK
2
+ require 'cgi'
3
+ require 'cgi/session'
4
4
  require 'cgi/session/pstore' # provides CGI::Session::PStore
5
5
  require 'digest/sha2'
6
- require 'syslog/logger'
7
- require "wikk_aes_256"
6
+ require 'securerandom'
8
7
  require 'wikk_password'
9
8
 
10
- #Provides common authentication mechanism for all our cgis.
11
- # @attr_reader [String] user , the remote user's user name
9
+ # Provides common authentication mechanism for all our cgis.
10
+ # Uses standard cgi parameters, unless overridden e.g. cgi?user=x&response=y
11
+ # Returns values imbedded as hidden fields in the login form
12
+ # @attr_reader [String] user , the remote user's user name
12
13
  # @attr_reader [String] session , the persistent Session record for this user
13
14
  class Web_Auth
14
- VERSION = "0.1.3" #Gem version
15
-
16
- attr_reader :user, :session
17
-
18
- #Create new Web_Auth instance, and proceed through authentication process by creating a login web form, if the user isn't authenticated.
15
+ VERSION = '0.1.6' # Gem version
16
+
17
+ attr_reader :user, :challenge
18
+ attr_accessor :response
19
+
20
+ # Create new Web_Auth instance, and proceed through authentication process by creating a login web form, if the user isn't authenticated.
19
21
  # @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data.
20
- # @param config [WIKK::Configuration|Hash] the location of the password file is embedded here.
22
+ # @param pwd_config [WIKK::Configuration|Hash] the location of the password file is embedded here.
23
+ # @param user [String] overrides cgi['user']
24
+ # @param response [String] overrides cgi['response']
25
+ # @param user_logout [Boolean] overrides cgi['logout']
26
+ # @param pstore_config [Hash] overrides default pstore settings
21
27
  # @param return_url [String] If we successfully authenticate, return here.
22
28
  # @return [WIKK::Web_Auth]
23
- def initialize(cgi, config, return_url = nil)
24
- if config.class == Hash
25
- sym = config.each_with_object({}) { |(k,v),h| h[k.to_sym] = v }
29
+ def initialize(cgi, pwd_config = nil, return_url = nil, user: nil, response: nil, user_logout: false, pstore_config: nil, run_auth: true)
30
+ if pwd_config.instance_of?(Hash)
31
+ sym = pwd_config.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
26
32
  @config = Struct.new(*(k = sym.keys)).new(*sym.values_at(*k))
27
33
  else
28
- @config = config
29
- end
30
- @cgi = cgi
31
- @user = ''
32
- @session = nil
33
- begin
34
- @log = Syslog::Logger.syslog
35
- rescue
36
- @log = Syslog::Logger.new("authlib.rbx")
34
+ @pwd_config = pwd_config
35
+ end
36
+
37
+ @cgi = cgi
38
+ @pstore_config = pstore_config
39
+
40
+ # Set variables from the method's params, or alternately, from the CGI params
41
+ @user = user.nil? ? cgi_param('Username') : user
42
+ @response = response.nil? ? cgi_param('Response') : response
43
+ @return_url = return_url.nil? ? cgi_param('ReturnURL') : return_url
44
+
45
+ # Look for existing session, but don't start a new one.
46
+ begin
47
+ @session = CGI::Session.new(@cgi, Web_Auth.session_config( { 'new_session' => false }, pstore_config: @pstore_config ))
48
+ rescue ArgumentError => _e # if no old session
49
+ @session = nil
50
+ rescue Exception => e # rubocop:disable Lint/RescueException In CGI, we want to handle every exception
51
+ raise e.class, 'Authenticate, CGI::Session.new ' + e.message
52
+ end
53
+
54
+ if @session.nil?
55
+ @challenge = '' # there is no current challenge
56
+ elsif @session['session_expires'].nil? || # Shouldn't be the case
57
+ @session['session_expires'] < Time.now || # Session has expired
58
+ @session['ip'] != @cgi.remote_addr || # Not coming from same IP address
59
+ # @session['user'] != @user || # Not the same user
60
+ cgi_param('logout') != '' || # Requested a logout
61
+ user_logout # Alternate way to request a logout
62
+ logout
63
+ else
64
+ # We ignore the cgi['Challenge'] value, and always get this from the pstore
65
+ @challenge = @session['seed'] # Recover the challenge from the pstore entry. It may be ''
37
66
  end
38
- authenticate(return_url)
67
+
68
+ authenticate if run_auth # This generates html output, so it is now conditionally run.
69
+ end
70
+
71
+ # Debug dump of session keys
72
+ def session_to_s
73
+ return '' if @session.nil?
74
+
75
+ s = '{'
76
+ [ 'auth', 'seed', 'ip', 'user', 'session_expires' ].each do |k|
77
+ s += "'#{k}':'#{@session[k]}', "
78
+ end
79
+ s += '}'
80
+ return s
81
+ end
82
+
83
+ # expose the session_id. This is also returned by modifying the cgi instance passed in to initialize
84
+ # * The cgi.output_cookies Array of Cookies gets modified if no_cookies is false (the default)
85
+ # * And cgi.output_hidden Hash get modified if no_hidden is false (the default)
86
+ # @return [String] random session id
87
+ def session_id
88
+ @session.nil? ? '' : @session.session_id
39
89
  end
40
90
 
41
- #way of checking without doing a full login sequence.
91
+ # way of checking without doing a full login sequence.
42
92
  # @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data.
93
+ # @param pstore_config [Hash] overrides default pstore settings
43
94
  # @return [Boolean] authenticated == true.
44
- def self.authenticated?(cgi)
95
+ def self.authenticated?(cgi, pstore_config: nil )
45
96
  begin
46
- session = CGI::Session.new(cgi, Web_Auth.session_config({'new_session' => false}) )
47
- authenticated = (session != nil && session['session_expires'] > Time.now && session['auth'] == true && session['ip'] == cgi.remote_addr)
48
- session.close #Writes back the session data
49
- return authenticated
50
- rescue ArgumentError => error # if no old session to find.
51
- begin
52
- @log = Syslog::Logger.syslog
53
- rescue
54
- @log = Syslog::Logger.new("authlib.rbx")
55
- end
56
- @log.error(error.message)
97
+ session = CGI::Session.new(cgi, Web_Auth.session_config( { 'new_session' => false }, pstore_config: pstore_config ) )
98
+ authenticated = (session != nil && !session['session_expires'].nil? && session['session_expires'] > Time.now && session['auth'] == true && session['ip'] == cgi.remote_addr)
99
+ session.close # Tidy up, so we don't leak file descriptors
100
+ return authenticated
101
+ rescue ArgumentError => _e # if no old session to find.
57
102
  return false
58
103
  end
59
104
  end
60
105
 
61
- #get the session reference and delete the session.
106
+ # Test to see if user authenticated.
107
+ # If this is the only call, then follow this with close_session()
108
+ # @return [Boolean] True, if this session is authenticated
109
+ def authenticated?
110
+ @session != nil && !@session['session_expires'].nil? && @session['session_expires'] > Time.now && @session['auth'] == true && @session['ip'] == @cgi.remote_addr
111
+ end
112
+
113
+ # get the session reference and delete the session.
114
+ # @param pstore_config [Hash] overrides default pstore settings
62
115
  # @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data.
63
- def self.logout(cgi)
116
+ def self.logout(cgi, pstore_config: nil)
64
117
  begin
65
- session = CGI::Session.new(cgi, Web_Auth.session_config({'new_session' => false}))
66
- session.delete if session != nil
67
- rescue ArgumentError => error # if no old session
68
- begin
69
- @log = Syslog::Logger.syslog
70
- rescue
71
- @log = Syslog::Logger.new("authlib.rbx")
72
- end
73
- @log.error(error.message)
118
+ session = CGI::Session.new(cgi, Web_Auth.session_config( { 'new_session' => false }, pstore_config: pstore_config ))
119
+ session.delete unless session.nil? # Also closes the session
120
+ rescue ArgumentError => _e # if no old session
121
+ # Not an error.
74
122
  end
75
123
  end
76
-
77
- #Checks password file to see if the response from the user matches generating a hash from the password locally.
78
- # @param user [String] Who the remote user claims to be
79
- # @param challenge [String] Random string we sent to this user, and they used in hashing their password.
80
- # @param received_hash [String] The hex_SHA256(password + challenge) string that the user sent back.
81
- # @return [Boolean] True for authorization test suceeded.
82
- def authorized?(user, challenge, received_hash)
83
- begin
84
- return WIKK::Password.valid_sha256_response?(user, @config, challenge, received_hash)
85
- rescue IndexError => error #User didn't exist
86
- @log.error("authorized?(#{user}): " + error.message)
87
- return false
88
- rescue Exception => error #Something else
89
- @log.error("authorized?(#{user}): " + error.message)
90
- return false
91
- end
124
+
125
+ # clean up the session, deleting the session state.
126
+ def logout
127
+ @session.delete unless @session.nil? # Will close the existing session
128
+ @session = nil
129
+ @challenge = '' # no current session, so no challenge string
130
+ clear_cgi_cookies
92
131
  end
93
132
 
94
- #Generate the new Session's config parameters, mixing in and/or overriding the preset values.
95
- # @param extra_arguments [Hash] Extra arguments that get added to the hash, or override values with the same key.
133
+ # Generate the new Session's config parameters, mixing in and/or overriding the preset values.
134
+ # @param pstore_config [Hash] Override the default pstore configurations. Only changed keys need to be included
135
+ # @param extra_arguments [Hash] Extra arguments that get added to the hash. Will also override values with the same key.
96
136
  # @return [Hash] The configuration hash.
97
- def self.session_config(extra_arguments = {})
98
- return {
137
+ def self.session_config( extra_arguments = nil, pstore_config: nil )
138
+ instance_of?(Hash)
139
+ session_conf = {
99
140
  'database_manager' => CGI::Session::PStore, # use PStore
100
- 'session_key' => '_wikk_rb_sess_id', # custom session key
101
- #'session_id' => ?,
141
+ 'session_key' => '_wikk_rb_sess_id', # custom session key
102
142
  'session_expires' => (Time.now + 86400), # 1 day timeout
103
- 'prefix' => 'pstore_sid_', # PStore option
104
- 'tmpdir' => '/tmp', # PStore option
105
- #new_session => ?,#boolean
106
- #no_hidden => ?,
107
- #session_domain => ?,
108
- #session_secure => ?,
109
- #session_path => ?,
110
- #no_cookies => ?, #boolean
111
- #suffix => ?
112
- }.merge(extra_arguments)
143
+ 'prefix' => 'pstore_sid_', # Prefix for pstore file
144
+ # 'suffix' => ?
145
+ 'tmpdir' => '/tmp', # PStore option. Under Apache2, this is a private namespace /tmp
146
+ 'session_path' => '/', # The cookie gets returned for URLs starting with this path
147
+ # 'new_session' => true, # Default, is to create a new session if it doesn't already exist
148
+ # 'session_domain' => ?,
149
+ # 'session_secure' => ?,
150
+ # 'session_id' => ?, # Created for new sessions. Merged in for existing sessions
151
+ 'no_cookies' => false, # boolean. Do fill in cgi output_cookies array of Cookies
152
+ 'no_hidden' => false # boolean fill in the cgi output_hidden Hash key=cookie, value=session_id
153
+ }
154
+ session_conf.merge!(pstore_config) if pstore_config.instance_of?(Hash)
155
+ session_conf.merge!(extra_arguments) if extra_arguments.instance_of?(Hash)
156
+ return session_conf
157
+ end
158
+
159
+ # Generate a challenge, as step 1 of a login
160
+ # If this is the only call, then follow this with close_session()
161
+ def gen_challenge
162
+ # Short session, which gets replaced if we successfully authenticate
163
+ new_session( { 'session_expires' => Time.now + 120 } )
164
+ raise 'gen_challenge: @session == nil' if @session.nil?
165
+
166
+ @challenge = SecureRandom.base64(32)
167
+ # Store the challenge in the pstore, ready for the 2nd login step, along with browser details
168
+ session_state_init('auth' => false, 'seed' => @challenge, 'ip' => @cgi.remote_addr, 'user' => @user, 'session_expires' => @session_options['session_expires'])
169
+ @session.update
170
+ return @challenge
113
171
  end
114
-
115
- def session_state_init(session_options = {})
116
- session_options.each { |k,v| @session[k] = v }
172
+
173
+ # Test the response against the password file
174
+ # If this is the only call, then follow this with close_session()
175
+ # @return [Boolean] We got authorized
176
+ def valid_response?
177
+ if authorized?
178
+ # We got a challenge string, so we are on step 2 of the authentication
179
+ # And have passed the password check ( authorized?() )
180
+ new_session # regenerate the cookie with a longer lifetime.
181
+ raise 'valid_response?: @session == nil' if @session.nil?
182
+
183
+ session_state_init('auth' => true, 'seed' => '', 'ip' => @cgi.remote_addr, 'user' => @user, 'session_expires' => @session_options['session_expires'])
184
+ @session.update # Should also update on close, which we probably do next
185
+ return true
186
+ else # Failed to authorize. The temporary challenge session cookie is no longer valid.
187
+ logout
188
+ return false
189
+ end
117
190
  end
118
191
 
119
- #Test to see if we are already authenticated, and if not, generate an HTML login page.
120
- # @param return_url [String] We return here if we sucessfully login
192
+ # Test to see if we are already authenticated, and if not, generate an HTML login page.
193
+ # @param return_url [String] We return here if we sucessfully login. Overrides initialize value
121
194
  def authenticate(return_url = nil)
122
- begin
123
- @session = CGI::Session.new(@cgi, Web_Auth.session_config({'new_session' => false})) #Look for existing session.
124
- return gen_html_login_page(return_url) if @session == nil
125
- rescue ArgumentError => error # if no old session
126
- return gen_html_login_page(return_url)
127
- rescue Exception => error
128
- raise Exception, "Authenticate, CGI::Session.new " + error.message
195
+ @return_url = return_url unless return_url.nil? # Update the return url (Backward compatibility)
196
+
197
+ # We have no session setup, or haven't sent the challenge yet.
198
+ # So we are at step 1 of the authentication
199
+ if @session.nil? || @challenge == ''
200
+ gen_html_login_page(message: 'no current challenge')
201
+ return
129
202
  end
130
-
131
- @session['auth'] = false if @session['session_expires'] < Time.now || #Session has expired
132
- @session['ip'] != @cgi.remote_addr || #Not coming from same IP address
133
- CGI::escapeHTML(@cgi['logout']) != '' #Are trying to logout
134
-
135
- return if(@session['auth'] == true) #if this is true, then we have already authenticated this session.
136
-
137
- if (challenge = @session['seed']) != '' #see if we are looking at a login response.
138
- @user = CGI::escapeHTML(@cgi['Username'])
139
- response = CGI::escapeHTML(@cgi['Response'])
140
- if @user != '' && response != '' && authorized?(@user, challenge, response)
141
- @session['auth'] = true #Response valid.
142
- @session['user'] = @user
143
- @session['ip'] = @cgi.remote_addr
144
- @session['seed'] = '' #Don't use the same one twice.
145
- @session.close
146
- return
203
+
204
+ # We are now at step 2, expecting a response to the challenge
205
+ begin
206
+ # Might be a while since we initialized the class, so repeat this test
207
+ @session['auth'] = false if @session['session_expires'].nil? || # Shouldn't ever happen, but has
208
+ @session['session_expires'] < Time.now || # Session has expired
209
+ @session['ip'] != @cgi.remote_addr # || # Not coming from same IP address
210
+ # @session['user'] != @user # Username not the same as the session
211
+
212
+ return if @session['auth'] == true # if this is true, then we have already authenticated this session.
213
+
214
+ unless valid_response?
215
+ gen_html_login_page(message: 'invalid response:')
147
216
  end
217
+ @session.close unless @session.nil? # Saves the session state.
218
+ rescue Exception => e # rubocop:disable Lint/RescueException
219
+ raise e.class, 'Authenticate, CGI::Session.new ' + e.message
148
220
  end
149
-
150
- @session.delete #Start a new session.
151
- gen_html_login_page(return_url)
152
- @session.close if @session != nil #Saves the session state.
153
221
  end
154
222
 
155
- #clean up the session, setting @authenticated to false and deleting the session state.
156
- def logout
157
- @session.delete if @session != nil
223
+ # Ensure we don't consume all file descriptors
224
+ # Call after last call (though most calls do close the session)
225
+ def close_session
226
+ @session.close unless @session.nil?
227
+ @session = nil
158
228
  end
159
229
 
160
- #Test to see if user authenticated,
161
- # @return [Boolean] i.e @authenticated's value.
162
- def authenticated?
163
- @session != nil && @session['session_expires'] > Time.now && @session['auth'] == true && session['ip'] == @cgi.remote_addr
164
- end
165
-
166
-
167
- #Used by calling cgi to generate a standard login page
168
- # @param return_url [String] We return here if we sucessfully login
169
- def gen_html_login_page(return_url = nil)
170
- session_options = Web_Auth.session_config()
171
- @session = CGI::Session.new(@cgi, session_options) #Start a new session for future authentications.
172
- raise "gen_html_login_page: @session == nil" if @session == nil
173
- challenge = WIKK::AES_256.gen_key_to_s
174
- session_state_init('auth' => false, 'seed' => challenge, 'ip' => @cgi.remote_addr, 'session_expires' => session_options['session_expires'])
175
- @cgi.header("type"=>"text/html")
230
+ # Used by calling cgi to generate a standard login page
231
+ def gen_html_login_page(message: '')
232
+ gen_challenge
233
+ @cgi.header('type' => 'text/html')
176
234
  @cgi.out do
177
235
  @cgi.html do
178
- @cgi.head{ @cgi.title{"login"} + html_nocache + html_script() } +
179
- @cgi.body { html_login_form(user, challenge, return_url) + "\n" }
236
+ @cgi.head { @cgi.title { 'login' } + html_nocache + html_script } +
237
+ @cgi.body { html_login_form(message: message) + "\n" }
180
238
  end
181
239
  end
182
- @session.update
183
240
  end
184
241
 
185
- #Used by calling cgi to inject a return URL into the html response.
186
- #Called by calling cgi, when constructing their html headers.
242
+ # Used by calling cgi to inject a return URL into the html response.
243
+ # Called by calling cgi, when constructing their html headers.
187
244
  # @param url [String] URL to redirect to.
188
245
  # @return [String] The HTML meta header, or "", if url is empty.
189
246
  def html_reload(url = nil)
190
247
  if url != nil && url != ''
191
248
  "<meta http-equiv=\"Refresh\" content=\"0; URL=#{url}\">\n"
192
249
  else
193
- ""
250
+ ''
194
251
  end
195
252
  end
196
253
 
197
- #Used by calling cgi to generate logout with this form.
254
+ # Used by calling cgi to generate logout with this form.
198
255
  # @param cgi_dir [String] directory holding the login.rbx cgi.
199
256
  # @return [String] Html logout form.
200
257
  def html_logout_form(cgi_dir)
201
- <<-EOHTMLF2
202
- <form NAME="login" ACTION="#{cgi_dir}/login.rbx" METHOD="post">
203
- <input TYPE="submit" NAME="logout" VALUE="logout" >
204
- </form>
205
- EOHTMLF2
258
+ <<~HTML
259
+ <form NAME="login" ACTION="#{cgi_dir}/login.rbx" METHOD="post">
260
+ <input TYPE="submit" NAME="logout" VALUE="logout" >
261
+ </form>
262
+ HTML
206
263
  end
207
-
208
- private
209
- #Login form javascript helper to SHA256 Hash a password and the challenge string sent by the server.
264
+
265
+ # Get a CGI param
266
+ # @param key [String] name of the CGI param
267
+ # @return [String] Either the value, or ''
268
+ private def cgi_param(key)
269
+ value = @cgi[key]
270
+ return value.nil? ? '' : CGI.escapeHTML(value)
271
+ end
272
+
273
+ # Short hand for set up of the pstore session entry
274
+ # @param session_options [Hash] key pairs for the pstore session
275
+ private def session_state_init(session_options = {})
276
+ session_options.each { |k, v| @session[k] = v }
277
+ end
278
+
279
+ # Blat the session cookies, that would have been sent back to the server
280
+ private def clear_cgi_cookies
281
+ # Update the cgi record, removing the cookies
282
+ @cgi.instance_eval do
283
+ @output_hidden = {}
284
+ @output_cookies = []
285
+ end
286
+ end
287
+
288
+ # Create a new session in the pstore, having deleted any existing session.
289
+ # @param session_params [Hash] Optionally alter the session params
290
+ private def new_session(session_params = nil)
291
+ @session.delete unless @session.nil? # Closes and Deletes the existing session
292
+ clear_cgi_cookies
293
+ # Start a new session for future authentications.
294
+ # This resets the expiry timestamp
295
+ @session_options = Web_Auth.session_config( session_params, pstore_config: @pstore_config )
296
+ @session = CGI::Session.new(@cgi, @session_options )
297
+ end
298
+
299
+ # Checks password file to see if the response from the user matches generating a hash from the password locally.
300
+ # @param user [String] Who the remote user claims to be
301
+ # @param challenge [String] Random string we sent to this user, and they used in hashing their password.
302
+ # @param response [String] The hex_SHA256(password + challenge) string that the user sent back.
303
+ # @return [Boolean] True for authorization test suceeded.
304
+ private def authorized?
305
+ begin
306
+ unless @session.nil? ||
307
+ @challenge.nil? || @challenge == '' ||
308
+ @user.nil? || @user.empty? ||
309
+ @response.nil? || @response.empty?
310
+ return WIKK::Password.valid_sha256_response?(@user, @pwd_config, @challenge, @response)
311
+ end
312
+ rescue IndexError => _e # User didn't exist
313
+ # Not an error.
314
+ end
315
+ return false
316
+ end
317
+
318
+ # Login form javascript helper to SHA256 Hash a password and the challenge string sent by the server.
210
319
  # @return [String] Javascript to embed in html response.
211
- def html_script
212
- <<-EOHTML
213
- <script type="text/javascript" src="/js/sha256.js"></script>
214
-
215
- <script language="JavaScript">
216
- function sendhash() {
217
- str = document.login.Password.value +
218
- document.login.Challenge.value;
219
-
220
- document.login.Response.value = hex_sha256(str);
221
- document.login.Password.value = "";
222
- document.login.Challenge.value = "";
223
- document.login.submit();
224
- }
225
- </script>
226
- EOHTML
320
+ private def html_script
321
+ <<~HTML
322
+ <script type="text/javascript" src="/js/sha256.js"></script>
323
+
324
+ <script language="JavaScript">
325
+ function sendhash() {
326
+ str = document.login.Password.value +
327
+ document.login.Challenge.value;
328
+
329
+ document.login.Response.value = hex_sha256(str);
330
+ document.login.Password.value = "";
331
+ document.login.Challenge.value = "";
332
+ document.login.submit();
333
+ }
334
+ </script>
335
+ HTML
227
336
  end
228
337
 
229
- #Generate html login form.
338
+ # Generate html login form.
230
339
  # @param user [String] user's login name.
231
340
  # @param challenge [String] Random bytes to add to password, before sending back to server.
232
- # @param return_url [String] Pass the url we want to return to if the login succeeds.
341
+ # @param return_url [String] We return here if we sucessfully login. Overrides initialize value
233
342
  # @return [String] Login form to embed in html response to user.
234
- def html_login_form(user, challenge, return_url='')
235
- <<-EOHTMLF
236
- <form NAME="login" ACTION="/ruby/login.rbx" METHOD="post">
237
- <input TYPE="hidden" NAME="Challenge" VALUE="#{challenge}">
238
- <input TYPE="hidden" NAME="Response" VALUE="">
239
- <input TYPE="hidden" NAME="ReturnURL" VALUE="#{return_url}">
240
- <table>
241
- <tr><th>User name</th><td><input TYPE="text" NAME="Username" VALUE="#{user}" SIZE="32" MAXLENGTH="32"></td></tr>
242
- <tr><th>Password</th><td><input TYPE="password" NAME="Password" VALUE="" SIZE="32" MAXLENGTH="32"></td></tr>
243
- <tr><td>&nbsp;</td><td>
244
- <input ONCLICK="sendhash(); return false;" TYPE="submit" NAME="login" VALUE="Login">
245
- <input TYPE="button" NAME="Cancel" VALUE=" Cancel "
246
- ONCLICK="document.login.Username.value='';document.login.Password.value=';return false;'">
247
- </td></tr>
248
- </table>
249
- </form>
250
- <script LANGUAGE="javascript" TYPE="text/javascript">
251
- document.login.Username.focus();
252
- </script>
253
- EOHTMLF
343
+ private def html_login_form(message: '')
344
+ <<~HTML
345
+ <form NAME="login" ACTION="/ruby/login.rbx" METHOD="post">
346
+ <input TYPE="hidden" NAME="Challenge" VALUE="#{@challenge}">
347
+ <input TYPE="hidden" NAME="Response" VALUE="">
348
+ <input TYPE="hidden" NAME="ReturnURL" VALUE="#{@return_url}">
349
+ <span hidden>#{message}</span>
350
+ <table>
351
+ <tr><th>User name</th><td><input TYPE="text" NAME="Username" VALUE="#{@user}" SIZE="32" MAXLENGTH="32"></td></tr>
352
+ <tr><th>Password</th><td><input TYPE="password" NAME="Password" VALUE="" SIZE="32" MAXLENGTH="32"></td></tr>
353
+ <tr><td>&nbsp;</td><td>
354
+ <input ONCLICK="sendhash(); return false;" TYPE="submit" NAME="login" VALUE="Login">
355
+ <input TYPE="button" NAME="Cancel" VALUE=" Cancel "
356
+ ONCLICK="document.login.Username.value='';document.login.Password.value=';return false;'">
357
+ </td></tr>
358
+ </table>
359
+ </form>
360
+ <script LANGUAGE="javascript" TYPE="text/javascript">
361
+ document.login.Username.focus();
362
+ </script>
363
+ HTML
254
364
  end
255
365
 
256
- #Generate no cache metadata header record.
366
+ # Generate no cache metadata header record.
257
367
  # @return [String] Html no-cache meta tag
258
- def html_nocache
259
- "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
368
+ private def html_nocache
369
+ '<META HTTP-EQUIV="Pragma" CONTENT="no-cache">'
260
370
  end
261
371
  end
262
372
  end
263
-
metadata CHANGED
@@ -1,19 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wikk_web_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Burrowes
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-25 00:00:00.000000000 Z
11
+ date: 2023-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: wikk_password
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
17
20
  - - ">="
18
21
  - !ruby/object:Gem::Version
19
22
  version: 0.1.0
@@ -21,6 +24,9 @@ dependencies:
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.1'
24
30
  - - ">="
25
31
  - !ruby/object:Gem::Version
26
32
  version: 0.1.0
@@ -44,14 +50,14 @@ dependencies:
44
50
  requirements:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '3.16'
53
+ version: '3.25'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '3.16'
60
+ version: '3.25'
55
61
  description: Gem provides common authentication framework for Wikarekare's Ruby CGIs.
56
62
  email:
57
63
  - r.burrowes@auckland.ac.nz
@@ -67,11 +73,11 @@ files:
67
73
  - README.md
68
74
  - Rakefile
69
75
  - lib/wikk_web_auth.rb
70
- homepage: http://wikarekare.github.com/wikk_web_auth/
76
+ homepage: https://wikarekare.github.io/wikk_web_auth/
71
77
  licenses:
72
78
  - MIT
73
79
  metadata: {}
74
- post_install_message:
80
+ post_install_message:
75
81
  rdoc_options:
76
82
  - "--markup"
77
83
  - markdown
@@ -92,9 +98,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
98
  - !ruby/object:Gem::Version
93
99
  version: '0'
94
100
  requirements: []
95
- rubyforge_project:
96
- rubygems_version: 2.6.8
97
- signing_key:
101
+ rubygems_version: 3.3.7
102
+ signing_key:
98
103
  specification_version: 4
99
104
  summary: Gem provides common authentication framework for Wikarekare's Ruby CGIs.
100
105
  test_files: []