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.
- checksums.yaml +5 -5
- data/History.txt +66 -0
- data/README.md +3 -3
- data/Rakefile +1 -2
- data/lib/wikk_web_auth.rb +300 -191
- metadata +15 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 671e30332d7611b59813c740329597c67743e6dfb1d14f5624b0d2f048f735ed
|
4
|
+
data.tar.gz: 370ae2207160bb1d6b7304c2451cdf9de71ea0f0da2e760e60642c9f3a8c822b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
*
|
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", [
|
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 '
|
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
|
-
#
|
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 =
|
15
|
-
|
16
|
-
attr_reader :user, :
|
17
|
-
|
18
|
-
|
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
|
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
|
-
|
24
|
-
if
|
25
|
-
sym =
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@
|
32
|
-
@
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|
-
|
95
|
+
def self.authenticated?(cgi, pstore_config: nil )
|
45
96
|
begin
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
rescue ArgumentError =>
|
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
|
-
#
|
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
|
-
|
66
|
-
|
67
|
-
rescue ArgumentError =>
|
68
|
-
|
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
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
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
|
-
|
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',
|
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_',
|
104
|
-
'
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
|
112
|
-
|
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
|
-
|
116
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
#
|
156
|
-
|
157
|
-
|
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
|
-
#
|
161
|
-
|
162
|
-
|
163
|
-
@
|
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{
|
179
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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
|
-
|
209
|
-
#
|
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
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
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]
|
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(
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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> </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
|
-
|
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.
|
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:
|
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.
|
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.
|
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:
|
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
|
-
|
96
|
-
|
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: []
|