hotspotlogin 0.1.2 → 1.0.0
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.
- data/README.rdoc +34 -13
- data/examples/etc/lighttpd/lighttpd.conf +31 -0
- data/examples/hotspotlogin.conf.yaml +6 -0
- data/lib/hotspotlogin/app.rb +74 -50
- data/lib/hotspotlogin/app/helpers.rb +17 -0
- data/lib/hotspotlogin/config.rb +25 -1
- data/lib/hotspotlogin/constants.rb +32 -2
- data/public/hotspotlogin/css/default.css +77 -0
- data/public/hotspotlogin/js/ChilliLibrary.js +844 -0
- data/public/hotspotlogin/js/UserStatus.js +229 -0
- data/views/_login_form.erb +5 -3
- data/views/hotspotlogin.erb +78 -18
- data/views/layout.erb +31 -43
- metadata +16 -11
data/README.rdoc
CHANGED
@@ -6,7 +6,13 @@ Hotspotlogin is a reimplementation in Ruby/Sinatra of the popular
|
|
6
6
|
hotspotlogin.cgi used with ChilliSpot and compatible Network Access
|
7
7
|
Controllers to provide a Captive Portal environment.
|
8
8
|
|
9
|
-
|
9
|
+
This release features displaying of detailed accounting info for the end user,
|
10
|
+
and is strongly based on CoovaChilli Network Access Controller and its
|
11
|
+
JSON interface.
|
12
|
+
|
13
|
+
* http://github.com/gderosa/hotspotlogin.rb
|
14
|
+
|
15
|
+
* http://coova.org/CoovaChilli/JSON
|
10
16
|
|
11
17
|
=== USAGE:
|
12
18
|
|
@@ -18,37 +24,52 @@ On the command-line:
|
|
18
24
|
|
19
25
|
$ hotspotlogin [options]
|
20
26
|
|
21
|
-
==== Options
|
27
|
+
==== General Options
|
28
|
+
|
29
|
+
--[no-]daemon daemonize [by default, executes in foreground]
|
30
|
+
|
31
|
+
--log log file (ignored when --no-daemon)
|
32
|
+
|
33
|
+
--pid pid file (ignored when --no-daemon)
|
34
|
+
|
35
|
+
--port [default 4990]
|
36
|
+
|
37
|
+
--listen-address [default 0.0.0.0]
|
38
|
+
|
39
|
+
--[no-]userpassword [default true]
|
40
|
+
|
41
|
+
--[no-]log-http [default false]
|
22
42
|
|
23
|
-
--
|
43
|
+
--uamsecret <PASS> optional, shared secret as in chilli.conf(5)
|
24
44
|
|
25
|
-
--
|
45
|
+
--conf <FILE> YAML configuration file, see examples/
|
26
46
|
|
27
|
-
|
47
|
+
==== End User Interface Options
|
28
48
|
|
29
|
-
--
|
49
|
+
--interval <SECONDS> autorefresh interval
|
30
50
|
|
31
|
-
--
|
51
|
+
--logo <IMAGE FILE> will be displayed in any page as
|
52
|
+
/hotspotlogin/logo.( png | jpg | etc... )
|
32
53
|
|
33
|
-
--
|
54
|
+
--custom-text <FRAGMENT.html>
|
34
55
|
|
35
|
-
--
|
56
|
+
--custom-footer <FRAGMENT.html>
|
36
57
|
|
37
|
-
--
|
58
|
+
--custom-headline <TEXT> will be put inside <h1></h1>
|
38
59
|
|
39
|
-
--
|
60
|
+
--favicon <FILE>
|
40
61
|
|
41
62
|
See also chilli.conf(5) manual page.
|
42
63
|
|
43
64
|
==== Unix Signals
|
44
65
|
|
45
|
-
USR1
|
66
|
+
USR1 sync/update log file (flush buffered I/O)
|
46
67
|
|
47
68
|
== LICENSE:
|
48
69
|
|
49
70
|
(The MIT License)
|
50
71
|
|
51
|
-
Copyright (c) 2010 Guido De Rosa
|
72
|
+
Copyright (c) 2010 Guido De Rosa (guido.derosa*vemarsas.it)
|
52
73
|
|
53
74
|
Permission is hereby granted, free of charge, to any person obtaining
|
54
75
|
a copy of this software and associated documentation files (the
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Example configuration snippet on how to setup a reverse HTTPS proxy
|
2
|
+
# with Lighty (http://www.lighttpd.net).
|
3
|
+
|
4
|
+
server.modules += (
|
5
|
+
"mod_proxy"
|
6
|
+
)
|
7
|
+
|
8
|
+
# enable SSL
|
9
|
+
$SERVER["socket"] == "0.0.0.0:443" {
|
10
|
+
ssl.engine = "enable"
|
11
|
+
ssl.pemfile = "/etc/lighttpd/server.pem"
|
12
|
+
ssl.ca-file = "/etc/lighttpd/ca.crt"
|
13
|
+
}
|
14
|
+
|
15
|
+
proxy.server = (
|
16
|
+
"/hotspotlogin" => (
|
17
|
+
(
|
18
|
+
"host" => "127.0.0.1",
|
19
|
+
"port" => 4990
|
20
|
+
)
|
21
|
+
),
|
22
|
+
# another application...
|
23
|
+
#"" => (
|
24
|
+
# (
|
25
|
+
# "host" => "127.0.0.1",
|
26
|
+
# "port" => 4567
|
27
|
+
# )
|
28
|
+
#
|
29
|
+
#)
|
30
|
+
)
|
31
|
+
|
@@ -4,3 +4,9 @@ port: 4991
|
|
4
4
|
uamsecret: uampass
|
5
5
|
userpassword: false
|
6
6
|
log-http: true
|
7
|
+
interval: 300
|
8
|
+
custom-headline: My Organization Name
|
9
|
+
custom-text: /some/path/fragment.html
|
10
|
+
custom-footer: /some/other/path/fragment.html
|
11
|
+
logo: /some/path/to/mylogo.png
|
12
|
+
favicon: /path/to/favicon.ico
|
data/lib/hotspotlogin/app.rb
CHANGED
@@ -3,6 +3,7 @@ require 'sinatra/base'
|
|
3
3
|
require 'erb'
|
4
4
|
require 'pp'
|
5
5
|
|
6
|
+
require 'hotspotlogin/constants'
|
6
7
|
require 'hotspotlogin/config'
|
7
8
|
require 'hotspotlogin/extensions/string'
|
8
9
|
|
@@ -22,39 +23,20 @@ module HotSpotLogin
|
|
22
23
|
|
23
24
|
result, titel, headline, bodytext = '', '', '', ''
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
before do
|
27
|
+
headers(
|
28
|
+
'X-HotSpotLoginRb-Version' => HotSpotLogin::VERSION
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
28
32
|
not_found do # Sinatra doesn't know this ditty ;-)
|
29
33
|
erb(
|
30
34
|
:"404",
|
31
|
-
:
|
32
|
-
:titel => 'Not Found',
|
33
|
-
:headline => headline,
|
34
|
-
:bodytext => bodytext,
|
35
|
-
:uamip => params['uamip'],
|
36
|
-
:uamport => params['uamport'],
|
37
|
-
:userurl => params['userurl'],
|
38
|
-
:redirurl => params['redirurl'],
|
39
|
-
:timeleft => params['timeleft'],
|
40
|
-
:result => nil
|
41
|
-
}
|
35
|
+
:layout => false
|
42
36
|
)
|
43
37
|
end
|
44
38
|
|
45
|
-
|
46
|
-
|
47
|
-
# possible Cases:
|
48
|
-
# attempt to login login=login
|
49
|
-
# 1: Login successful res=success
|
50
|
-
# 2: Login failed res=failed
|
51
|
-
# 3: Logged out res=logoff
|
52
|
-
# 4: Tried to login while already logged in res=already
|
53
|
-
# 5: Not logged in yet res=notyet
|
54
|
-
#11: Popup res=popup1
|
55
|
-
#12: Popup res=popup2
|
56
|
-
#13: Popup res=popup3
|
57
|
-
# 0: It was not a form request res=""
|
39
|
+
require 'hotspotlogin/app/helpers'
|
58
40
|
|
59
41
|
#Read query parameters which we care about
|
60
42
|
# params['res']
|
@@ -81,9 +63,40 @@ module HotSpotLogin
|
|
81
63
|
# params['redirurl']
|
82
64
|
|
83
65
|
|
84
|
-
#
|
85
|
-
|
66
|
+
# All paths should be under /hotspotlogin, to allow easier
|
67
|
+
# setup of "conditional" HTTPS reverse proxies
|
68
|
+
|
69
|
+
get '/' do
|
70
|
+
redirect '/hotspotlogin'
|
71
|
+
end
|
72
|
+
|
73
|
+
get '/hotspotlogin/favicon.ico' do
|
74
|
+
if HotSpotLogin.config['favicon']
|
75
|
+
if File.file? HotSpotLogin.config['favicon']
|
76
|
+
send_file HotSpotLogin.config['favicon']
|
77
|
+
else
|
78
|
+
not_found
|
79
|
+
end
|
80
|
+
else
|
81
|
+
not_found
|
82
|
+
end
|
83
|
+
end
|
86
84
|
|
85
|
+
get '/hotspotlogin/logo.:ext' do
|
86
|
+
if HotSpotLogin.config['logo']
|
87
|
+
if
|
88
|
+
File.file?( HotSpotLogin.config['logo']) and
|
89
|
+
File.extname( HotSpotLogin.config['logo']) == ".#{params[:ext]}"
|
90
|
+
send_file HotSpotLogin.config['logo']
|
91
|
+
else
|
92
|
+
not_found
|
93
|
+
end
|
94
|
+
else
|
95
|
+
not_found
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
get '/hotspotlogin/?' do
|
87
100
|
if HotSpotLogin.config['uamsecret'] and
|
88
101
|
HotSpotLogin.config['uamsecret'].length > 0
|
89
102
|
uamsecret = HotSpotLogin.config['uamsecret']
|
@@ -110,69 +123,80 @@ module HotSpotLogin
|
|
110
123
|
headline = 'Logging in to HotSpot'
|
111
124
|
|
112
125
|
#if uamsecret and userpassword
|
113
|
-
if userpassword
|
126
|
+
if userpassword # PAP
|
114
127
|
headers({
|
115
128
|
'Refresh' => "0;url=http://#{params['uamip']}:#{params['uamport']}/logon?username=#{params['UserName']}&password=#{pappassword}&userurl=#{params['userurl']}"
|
116
129
|
# NOTE: no userurl passed... why?
|
117
130
|
# NOTE: if you pass it, nothing changes
|
118
131
|
})
|
119
|
-
else
|
132
|
+
else # CHAP
|
120
133
|
headers({
|
121
134
|
'Refresh' => "0;url=http://#{params['uamip']}:#{params['uamport']}/logon?username=#{params['UserName']}&response=#{response}&userurl=#{params['userurl']}"
|
122
135
|
})
|
123
136
|
end
|
124
137
|
elsif params['res'] == 'success'
|
125
|
-
result =
|
138
|
+
result = Result::SUCCESS
|
126
139
|
titel = 'Logged in to HotSpot'
|
127
140
|
headline = 'Logged in to HotSpot'
|
128
141
|
bodytext = 'Welcome'
|
129
142
|
elsif params['res'] == 'failed'
|
130
|
-
result =
|
143
|
+
result = Result::FAILED
|
131
144
|
titel = 'HotSpot Login Failed'
|
132
145
|
headline = 'HotSpot Login Failed'
|
133
146
|
elsif params['res'] == 'logoff'
|
134
|
-
result =
|
147
|
+
result = Result::LOGOFF
|
135
148
|
titel = 'Logged out from HotSpot'
|
136
149
|
headline = 'Logged out from HotSpot'
|
137
150
|
elsif params['res'] == 'already'
|
138
|
-
result =
|
151
|
+
result = Result::ALREADY
|
139
152
|
titel = 'Already logged in to HotSpot'
|
140
153
|
headline = 'Already logged in to HotSpot'
|
141
154
|
elsif params['res'] == 'notyet'
|
142
|
-
result =
|
155
|
+
result = Result::NOTYET
|
143
156
|
titel = 'Logged out from HotSpot'
|
144
157
|
headline = 'Logged out from HotSpot'
|
145
158
|
elsif params['res'] == 'popup1'
|
146
|
-
result =
|
159
|
+
result = Result::PopUp::LOGGING_IN
|
147
160
|
titel = 'Logging into HotSpot'
|
148
161
|
headline = 'Logged in to HotSpot'
|
149
162
|
elsif params['res'] == 'popup2'
|
150
|
-
result =
|
163
|
+
result = Result::PopUp::LOGGED_IN
|
151
164
|
titel = 'Logged in to HotSpot'
|
152
165
|
headline = 'Logged in to HotSpot'
|
153
166
|
elsif params['res'] == 'popup3'
|
154
|
-
result=
|
167
|
+
result= Result::PopUp::LOGGED_OUT
|
155
168
|
titel = 'Logged out from HotSpot'
|
156
169
|
headline = 'Logged out from HotSpot'
|
157
170
|
elsif params['res'] == '' or !params['res'] # not a form request: err!
|
158
|
-
result =
|
171
|
+
result = Result::NONE
|
159
172
|
titel = 'What do you want here?'
|
160
173
|
headline = 'HotSpot Login Failed'
|
161
174
|
end
|
162
175
|
|
176
|
+
logoext = nil
|
177
|
+
logoext =
|
178
|
+
File.extname(HotSpotLogin.config['logo']) if
|
179
|
+
File.extname(HotSpotLogin.config['logo'])
|
180
|
+
|
163
181
|
erb(
|
164
182
|
:hotspotlogin,
|
165
183
|
:locals => {
|
166
|
-
:titel
|
167
|
-
:headline
|
168
|
-
:bodytext
|
169
|
-
:uamip
|
170
|
-
:uamport
|
171
|
-
:userurl
|
172
|
-
#:redirurl
|
173
|
-
:redirurl
|
174
|
-
:timeleft
|
175
|
-
:
|
184
|
+
:titel => titel,
|
185
|
+
:headline => headline, # like 'Logged out from HotSpot'
|
186
|
+
:bodytext => bodytext,
|
187
|
+
:uamip => params['uamip'],
|
188
|
+
:uamport => params['uamport'],
|
189
|
+
:userurl => params['userurl'],
|
190
|
+
#:redirurl => params['redirurl'],
|
191
|
+
:redirurl => params['userurl'],
|
192
|
+
:timeleft => params['timeleft'], # legacy...
|
193
|
+
:interval => HotSpotLogin.config['interval'],
|
194
|
+
:custom_headline =>
|
195
|
+
HotSpotLogin.config['custom-headline'], # like "MyOrg Name"
|
196
|
+
:custom_text => HotSpotLogin.config['custom-text'],
|
197
|
+
:custom_footer => HotSpotLogin.config['custom-footer'],
|
198
|
+
:logoext => logoext,
|
199
|
+
:result => result,
|
176
200
|
}
|
177
201
|
)
|
178
202
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'hotspotlogin/constants'
|
3
|
+
|
4
|
+
module HotSpotLogin
|
5
|
+
class App < Sinatra::Base
|
6
|
+
helpers do
|
7
|
+
def logged_in?(result)
|
8
|
+
[
|
9
|
+
HotSpotLogin::Result::ALREADY,
|
10
|
+
HotSpotLogin::Result::SUCCESS,
|
11
|
+
HotSpotLogin::Result::PopUp::SUCCESS,
|
12
|
+
].include? result
|
13
|
+
end
|
14
|
+
alias status_window? logged_in?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/hotspotlogin/config.rb
CHANGED
@@ -27,7 +27,31 @@ module HotSpotLogin
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Command line switches override configuration file.
|
30
|
+
|
31
|
+
opts.on('--interval SECONDS', 'autorefresh accounting/session data every SECONDS seconds') do |seconds|
|
32
|
+
@@config['interval'] = seconds.to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on('--custom-headline TEXT', 'display <h1>TEXT</h1> on top of the login page, tipically your Organization name') do |text|
|
36
|
+
@@config['custom-headline'] == text
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on('--custom-text FILE', 'display HTML fragment FILE before the user stats table/login form') do |file|
|
40
|
+
@@config['custom-text'] == file
|
41
|
+
end
|
30
42
|
|
43
|
+
opts.on('--custom-footer FILE', 'display HTML fragment FILE after the user stats table/login form') do |file|
|
44
|
+
@@config['custom-footer'] == file
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('--logo FILE', 'logo (of your Organization etc.)') do |file|
|
48
|
+
@@config['logo'] = file
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('--favicon FILE', 'well, favicon ;)') do |file|
|
52
|
+
@@config['favicon'] = file
|
53
|
+
end
|
54
|
+
|
31
55
|
opts.on('--[no-]daemon', 'become a daemon') do |daemonize|
|
32
56
|
@@config['daemon'] = daemonize
|
33
57
|
end
|
@@ -44,7 +68,7 @@ module HotSpotLogin
|
|
44
68
|
@@config['uamsecret'] = uamsecret
|
45
69
|
end
|
46
70
|
|
47
|
-
opts.on('--[no-]userpassword', 'like setting $userpassword=1 in hotspotlogin.cgi') do |userpassword|
|
71
|
+
opts.on('--[no-]userpassword', 'like setting $userpassword=1 in hotspotlogin.cgi (use PAP instead of CHAP)') do |userpassword|
|
48
72
|
@@config['userpassword'] = userpassword
|
49
73
|
end
|
50
74
|
|
@@ -1,12 +1,42 @@
|
|
1
1
|
module HotSpotLogin
|
2
2
|
|
3
|
-
VERSION = '0.
|
3
|
+
VERSION = '1.0.0'
|
4
4
|
|
5
5
|
DEFAULT_CONFIG = {
|
6
6
|
'listen-address' => '0.0.0.0',
|
7
7
|
'port' => 4990,
|
8
8
|
'log-http' => false,
|
9
|
-
'userpassword' => true # like $userpassword in hotpotlgin.(cgi|php)
|
9
|
+
'userpassword' => true, # like $userpassword in hotpotlgin.(cgi|php)
|
10
|
+
'interval' => 300
|
10
11
|
}
|
12
|
+
|
13
|
+
ROOTDIR = File.join(File.dirname(File.expand_path __FILE__), '../..')
|
14
|
+
|
15
|
+
# Corresponding GET parameters are res=success, res=failed, res=popup1, etc.
|
16
|
+
module Result
|
17
|
+
SUCCESS = 1
|
18
|
+
FAILED = 2
|
19
|
+
LOGOFF = 3 # logged out
|
20
|
+
ALREADY = 4
|
21
|
+
NOTYET = 5
|
22
|
+
POPUP1 = 11
|
23
|
+
POPUP2 = 12
|
24
|
+
POPUP3 = 13
|
25
|
+
NONE = 0 # It was not a form request
|
26
|
+
|
27
|
+
# More meaningful constants for popup windows.
|
28
|
+
module PopUp
|
29
|
+
LOGGING_IN = POPUP1
|
30
|
+
LOGGED_IN = POPUP2
|
31
|
+
LOGGED_OUT = POPUP3
|
32
|
+
|
33
|
+
# convenient aliases
|
34
|
+
LOGGING = LOGGING_IN
|
35
|
+
LOGGED = LOGGED_IN
|
36
|
+
SUCCESS = LOGGED_IN
|
37
|
+
LOGOFF = LOGGED_OUT
|
38
|
+
LOGOUT = LOGOFF
|
39
|
+
end
|
40
|
+
end
|
11
41
|
|
12
42
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
body {
|
2
|
+
font-size: 10pt;
|
3
|
+
}
|
4
|
+
h1, h2 {
|
5
|
+
text-align: center;
|
6
|
+
font-family: sans-serif;
|
7
|
+
}
|
8
|
+
h1 {
|
9
|
+
font-size: 160%;
|
10
|
+
margin: 0.1ex 0;
|
11
|
+
}
|
12
|
+
h2 {
|
13
|
+
font-size: 135%;
|
14
|
+
margin-top: 0.1ex;
|
15
|
+
font-style: italic;
|
16
|
+
}
|
17
|
+
#powered-by {
|
18
|
+
text-align: right;
|
19
|
+
font-style: italic;
|
20
|
+
margin-right: 3em;
|
21
|
+
margin-bottom: 1ex;
|
22
|
+
font-family: sans-serif;
|
23
|
+
font-size:smaller;
|
24
|
+
}
|
25
|
+
#logo-container {
|
26
|
+
text-align: center;
|
27
|
+
margin: 0 0;
|
28
|
+
}
|
29
|
+
#form-container {
|
30
|
+
text-align: center;
|
31
|
+
}
|
32
|
+
#form-container table {
|
33
|
+
margin: 1em auto 0.5em auto;
|
34
|
+
}
|
35
|
+
#form-container table tr th {
|
36
|
+
text-align: left;
|
37
|
+
}
|
38
|
+
#logInLogOut-container {
|
39
|
+
padding: 0;
|
40
|
+
margin: 0.5em 0;
|
41
|
+
text-align: center;
|
42
|
+
}
|
43
|
+
#status-container {
|
44
|
+
text-align: center;
|
45
|
+
margin: 0 0 1em 0;
|
46
|
+
}
|
47
|
+
#status-table {
|
48
|
+
margin: auto;
|
49
|
+
border: 1px blue solid;
|
50
|
+
padding: 0.5ex;
|
51
|
+
}
|
52
|
+
#status-table th {
|
53
|
+
text-align: left;
|
54
|
+
}
|
55
|
+
#status-table td {
|
56
|
+
text-align: right;
|
57
|
+
}
|
58
|
+
#status-table th.optinfo {
|
59
|
+
font-size: smaller;
|
60
|
+
font-style: italic;
|
61
|
+
}
|
62
|
+
#submit-container {
|
63
|
+
}
|
64
|
+
#submit-container button, #submit-container input[type="submit"] {
|
65
|
+
width: 5em;
|
66
|
+
}
|
67
|
+
#custom-text {
|
68
|
+
font-size: 94%;
|
69
|
+
padding: 0 1ex;
|
70
|
+
margin-bottom: 1.5em;
|
71
|
+
}
|
72
|
+
#custom-footer {
|
73
|
+
margin-top: 2em;
|
74
|
+
font-size: small;
|
75
|
+
font-family: sans-serif;
|
76
|
+
text-align: center;
|
77
|
+
}
|