wikk_web_auth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ecd4829760efaf1668d18a19881a30066d68a6c0
4
+ data.tar.gz: a9c80865749876369330677d7fdad881ac8376eb
5
+ SHA512:
6
+ metadata.gz: 6bcb14d568d41c9467dc5a844469f31115b28c09d9743ced5d9c856f61a0976e82f2b5dc07fc5cf4c36812f7a002450158068ca27b8ce7b88cec611b6fb3ec02
7
+ data.tar.gz: 726c057193a25b2cebc441853fd833ed2ef9be6c7075577c56110c9cd48cf46bc25437932487e60fcdb53da880c4989a042b2d3a5cdfef2441fd7bc6ca7870b7
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2016-06-19
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,5 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/wikk_web_auth.rb
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # wikk_web_auth
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
6
+
7
+ ## DESCRIPTION:
8
+
9
+ Gem provides common authentication framework for Wikarekare's Ruby CGIs.
10
+
11
+ ## FEATURES/PROBLEMS:
12
+
13
+ * In process of conversion from a library to a gem, and refactored to be more generic. Needs some real world testing.
14
+
15
+ ## SYNOPSIS:
16
+
17
+ ### Redirect to login scenario
18
+ * Logic flows from clients to the cgi;
19
+ * generating a login html page;
20
+ * client logs in, sending the form to the login.rbx Ruby cgi;
21
+ * If successful, the login.rbx cgi sends back a redirect to the original cgi.
22
+
23
+ ### Javascript Scenario, with separation of function.
24
+ * A separate login button is included on the HTML page, which uses the wikk_auth_js library
25
+ ```
26
+ <html>
27
+ <head>
28
+ <script src="wikk_web_auth.js"></script>
29
+ ...
30
+ <script>
31
+ init() {
32
+ //Check if we are authenticated, and fill in login span appropriately.
33
+ logged_in(true, '/admin/sites.html'); //(display lock/unlock image, return url after login page)
34
+ ...
35
+ }
36
+ ...
37
+ </script>
38
+ </head>
39
+ <body onload="init();">
40
+ ...
41
+ <span id="login_span"></span>
42
+ ...
43
+ ```
44
+ * The cgi simple calls the class level WIKK::Web_Auth.authenticate? call, for a true/false response.
45
+ ```
46
+ require 'wikk_web_auth'
47
+ @authenticated = Authenticated.authenticated?(@cgi)
48
+ ```
49
+
50
+ ## REQUIREMENTS:
51
+
52
+ * Used in conjunction with Ruby cgi login.rbx
53
+ * relies on gems wikk_password
54
+ * Could make use of wikk_configuration and wikk_aes_256
55
+
56
+ ## INSTALL:
57
+
58
+ * sudo gem install wikk_web_auth
59
+
60
+ ## LICENSE:
61
+
62
+ (The MIT License)
63
+
64
+ Derived from Wikarekare authentication.rb library.
65
+
66
+ Copyright (c) 2004-2016
67
+
68
+ Permission is hereby granted, free of charge, to any person obtaining
69
+ a copy of this software and associated documentation files (the
70
+ 'Software'), to deal in the Software without restriction, including
71
+ without limitation the rights to use, copy, modify, merge, publish,
72
+ distribute, sublicense, and/or sell copies of the Software, and to
73
+ permit persons to whom the Software is furnished to do so, subject to
74
+ the following conditions:
75
+
76
+ The above copyright notice and this permission notice shall be
77
+ included in all copies or substantial portions of the Software.
78
+
79
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
80
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
81
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
82
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
83
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
84
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
85
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ Hoe.plugin :yard
6
+
7
+ Hoe.spec 'wikk_web_auth' do
8
+ self.readme_file = "README.md"
9
+ self.developer( "Rob Burrowes","r.burrowes@auckland.ac.nz")
10
+ remote_rdoc_dir = '' # Release to root
11
+
12
+ self.yard_title = 'wikk_web_auth'
13
+ self.yard_options = ['--markup', 'markdown', '--protected']
14
+ end
15
+
16
+
17
+ #Validate manfest.txt
18
+ #rake check_manifest
19
+
20
+ #Local checking. Creates pkg/
21
+ #rake gem
22
+
23
+ #create doc/
24
+ #rake docs
25
+
26
+ #Copy up to rubygem.org
27
+ #rake release VERSION=1.0.1
@@ -0,0 +1,263 @@
1
+ module WIKK
2
+ require 'cgi'
3
+ require 'cgi/session'
4
+ require 'cgi/session/pstore' # provides CGI::Session::PStore
5
+ require 'digest/sha2'
6
+ require 'syslog/logger'
7
+ require "wikk_aes_256"
8
+ require 'wikk_password'
9
+
10
+ #Provides common authentication mechanism for all our cgis.
11
+ # @attr_reader [String] user , the remote user's user name
12
+ # @attr_reader [String] session , the persistent Session record for this user
13
+ class Web_Auth
14
+ VERSION = "0.1.0" #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.
19
+ # @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.
21
+ # @param return_url [String] If we successfully authenticate, return here.
22
+ # @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 }
26
+ @config = Struct.new(*(k = sym.keys)).new(*sym.values_at(*k))
27
+ 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")
37
+ end
38
+ authenticate(return_url)
39
+ end
40
+
41
+ #way of checking without doing a full login sequence.
42
+ # @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data.
43
+ # @return [Boolean] authenticated == true.
44
+ def self.authenticated?(cgi)
45
+ 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)
57
+ return false
58
+ end
59
+ end
60
+
61
+ #get the session reference and delete the session.
62
+ # @param cgi [CGI] Which carries the client data, cookies, and PUT/POST form data.
63
+ def self.logout(cgi)
64
+ 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)
74
+ end
75
+ 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.err("authorized?(#{user}): " + error.message)
87
+ return false
88
+ rescue Exception => error #Something else
89
+ @log.err("authorized?(#{user}): " + error.message)
90
+ return false
91
+ end
92
+ end
93
+
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.
96
+ # @return [Hash] The configuration hash.
97
+ def self.session_config(extra_arguments = {})
98
+ return {
99
+ 'database_manager' => CGI::Session::PStore, # use PStore
100
+ 'session_key' => '_wikk_rb_sess_id', # custom session key
101
+ #'session_id' => ?,
102
+ '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)
113
+ end
114
+
115
+ def session_state_init(session_options = {})
116
+ session_options.each { |k,v| @session[k] = v }
117
+ end
118
+
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
121
+ 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
129
+ 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
147
+ end
148
+ 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
+ end
154
+
155
+ #clean up the session, setting @authenticated to false and deleting the session state.
156
+ def logout
157
+ @session.delete if @session != nil
158
+ end
159
+
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' => "10.2.2.193", 'session_expires' => session_options['session_expires'])
175
+ @cgi.header("type"=>"text/html")
176
+ @cgi.out do
177
+ @cgi.html do
178
+ @cgi.head{ @cgi.title{"login"} + html_nocache + html_script() } +
179
+ @cgi.body { html_login_form(user, challenge, return_url) + "\n" }
180
+ end
181
+ end
182
+ @session.update
183
+ end
184
+
185
+ #Used by calling cgi to inject a return URL into the html response.
186
+ #Called by calling cgi, when constructing their html headers.
187
+ # @param url [String] URL to redirect to.
188
+ # @return [String] The HTML meta header, or "", if url is empty.
189
+ def html_reload(url = nil)
190
+ if url != nil && url != ''
191
+ "<meta http-equiv=\"Refresh\" content=\"0; URL=#{url}\">\n"
192
+ else
193
+ ""
194
+ end
195
+ end
196
+
197
+ #Used by calling cgi to generate logout with this form.
198
+ # @param cgi_dir [String] directory holding the login.rbx cgi.
199
+ # @return [String] Html logout form.
200
+ 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
206
+ end
207
+
208
+ private
209
+ #Login form javascript helper to SHA256 Hash a password and the challenge string sent by the server.
210
+ # @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
227
+ end
228
+
229
+ #Generate html login form.
230
+ # @param user [String] user's login name.
231
+ # @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.
233
+ # @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="submit" VALUE="Submit">
245
+ <input TYPE="button" NAME="Cancel" VALUE=" Cancel "
246
+ ONCLICK="document.login.Username.value='';document.login.Password.value=''">
247
+ </td></tr>
248
+ </table>
249
+ </form>
250
+ <script LANGUAGE="javascript" TYPE="text/javascript">
251
+ document.login.Username.focus();
252
+ </script>
253
+ EOHTMLF
254
+ end
255
+
256
+ #Generate no cache metadata header record.
257
+ # @return [String] Html no-cache meta tag
258
+ def html_nocache
259
+ "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
260
+ end
261
+ end
262
+ end
263
+
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wikk_web_auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Burrowes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hoe-yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.2
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: hoe
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.15'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.15'
41
+ description: Gem provides common authentication framework for Wikarekare's Ruby CGIs.
42
+ email:
43
+ - r.burrowes@auckland.ac.nz
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files:
47
+ - History.txt
48
+ - Manifest.txt
49
+ - README.md
50
+ files:
51
+ - History.txt
52
+ - Manifest.txt
53
+ - README.md
54
+ - Rakefile
55
+ - lib/wikk_web_auth.rb
56
+ homepage: http://wikarekare.github.com/wikk_web_auth/
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options:
62
+ - "--markup"
63
+ - markdown
64
+ - "--protected"
65
+ - "--title"
66
+ - wikk_web_auth
67
+ - "--quiet"
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.5.1
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Gem provides common authentication framework for Wikarekare's Ruby CGIs.
86
+ test_files: []