pseudo_kiosk 0.1.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +94 -0
- data/Rakefile +27 -0
- data/app/assets/config/pseudo_kiosk_manifest.js +2 -0
- data/app/assets/javascripts/pseudo_kiosk/application.js +14 -0
- data/app/assets/javascripts/pseudo_kiosk/authentication.js +2 -0
- data/app/assets/stylesheets/pseudo_kiosk/application.css +15 -0
- data/app/assets/stylesheets/pseudo_kiosk/authentication.css +4 -0
- data/app/controllers/pseudo_kiosk/application_controller.rb +5 -0
- data/app/controllers/pseudo_kiosk/authentication_controller.rb +37 -0
- data/app/helpers/pseudo_kiosk/application_helper.rb +4 -0
- data/app/helpers/pseudo_kiosk/authentication_helper.rb +2 -0
- data/app/views/layouts/pseudo_kiosk/application.html.erb +16 -0
- data/app/views/pseudo_kiosk/authentication/process_submit.html.erb +2 -0
- data/app/views/pseudo_kiosk/authentication/unlock.html.erb +259 -0
- data/config/routes.rb +6 -0
- data/lib/pseudo_kiosk.rb +13 -0
- data/lib/pseudo_kiosk/config.rb +46 -0
- data/lib/pseudo_kiosk/controller.rb +88 -0
- data/lib/pseudo_kiosk/engine.rb +13 -0
- data/lib/pseudo_kiosk/test_helpers/internal.rb +79 -0
- data/lib/pseudo_kiosk/test_helpers/internal/rails.rb +22 -0
- data/lib/pseudo_kiosk/version.rb +3 -0
- data/lib/tasks/pseudo_kiosk_tasks.rake +4 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d55fa93717efba52a0ed428d4fc4ac23f25321792f7ab517fa5f25c6e516be3c
|
4
|
+
data.tar.gz: 36cad7483842d86cb7a394d06ff291d1ce96aa042bff143867721735fb1a1705
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4a9bb2ea15dee2086fe4f73e0df30e5ed1b912f8782ea870f18c60c83501c59d7e8d85d1c4215c76d31af6755aedfdea51bdae9dc086db4744da950d649773b2
|
7
|
+
data.tar.gz: 6f610505ff484a4f85aaae880eb0064767dceedad968e48877a14510b143352b3b34c29a136f0fb5566d120661788859875bac6264a3abe640aeec960612713a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2019
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# PseudoKiosk
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
PseudoKiosk is a play on words on the unix sudo command and the idea to provide a fake kiosk terminal. Instead of granting elevated privileges, PseudoKiosk gives the ability to limit access to the application during workflows which involve physically passing a device with elevated permissions to an unknown or untrusted user. PseudoKiosk provides the ability to lock down a rails application to only a specified whitelist of endpoints during a session. The kiosk can be quickly and easily unlocked by passing a kiosk passcode.
|
6
|
+
|
7
|
+
The motivating user scenario for this is a mobile, tablet, or kiosk device where data input must be received from an untrusted user while the device is mainly used by a privileged user (such as a cashier in a POS system). With PseudoKiosk, the device can be safely passed to the end customer to input his/her own information without fear of accidently or malicously utilizing the main user's elevated privileges.
|
8
|
+
|
9
|
+
After the form has been successfully submitted, a simple friendly unlock screen is provided for the privileged user to quickly unlock the kiosk and continue the workflow.
|
10
|
+
|
11
|
+
## Demo
|
12
|
+
|
13
|
+

|
14
|
+
|
15
|
+
You can also clone this repository and run the test application.
|
16
|
+
|
17
|
+
```
|
18
|
+
git clone https://github.com/jonmchan/pseudo_kiosk.git
|
19
|
+
cd pseudo_kiosk
|
20
|
+
bundle install
|
21
|
+
bundle exec rails s
|
22
|
+
```
|
23
|
+
|
24
|
+
Open http://localhost:3000 and you should see the same demo above. Passcode is `abc`.
|
25
|
+
|
26
|
+
|
27
|
+
## Installation
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'pseudo_kiosk'
|
32
|
+
```
|
33
|
+
|
34
|
+
And then execute:
|
35
|
+
```bash
|
36
|
+
$ bundle
|
37
|
+
```
|
38
|
+
|
39
|
+
Or install it yourself as:
|
40
|
+
```bash
|
41
|
+
$ gem install pseudo_kiosk
|
42
|
+
```
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
|
46
|
+
Add the PseudoKiosk::Engine to your `config/routes.rb`:
|
47
|
+
|
48
|
+
```
|
49
|
+
mount PseudoKiosk::Engine => "/pseudo_kiosk"
|
50
|
+
```
|
51
|
+
|
52
|
+
Add the before_action script to check all requests if it is in protected or not by putting it in your `app/controller/application_controller.rb` file:
|
53
|
+
|
54
|
+
```
|
55
|
+
before_action :secure_pseudo_kiosk
|
56
|
+
```
|
57
|
+
|
58
|
+
Create an initializer to configure pseudo_kiosk in `config/initializers/pseudo_kiosk.rb`:
|
59
|
+
|
60
|
+
The unlock mechanism can be a simple unlock string such as:
|
61
|
+
```
|
62
|
+
PseudoKiosk::Config.configure do |config|
|
63
|
+
config.unlock_mechanism = "abc"
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
Or it can be a lambda function:
|
68
|
+
```
|
69
|
+
PseudoKiosk::Config.configure do |config|
|
70
|
+
config.unlock_mechanism = ->(controller_context, params) {
|
71
|
+
return Digest::SHA256.base64digest(params[:passcode]) == controller_context.current_user.passcode_hash ? true : false
|
72
|
+
}
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
The lambda function receives an instance of the controller which can be used to call functions such as current_user or session and all the params passed from the authentication unlock action. By default, the only parameter passed is `passcode`.
|
77
|
+
|
78
|
+
|
79
|
+
If you wish to override the default unlock screen, copy the [built in view](https://github.com/jonmchan/pseudo_kiosk/blob/master/app/views/pseudo_kiosk/authentication/unlock.html.erb) and put it in `app/views/pseudo_kiosk/authentication/unlock.html.erb`. Edit it to your heart's content.
|
80
|
+
|
81
|
+
### Core Functions
|
82
|
+
|
83
|
+
You can call any of these functions to go in and out of kiosk mode (refer to [code documentation](https://github.com/jonmchan/pseudo_kiosk/blob/master/lib/pseudo_kiosk/controller.rb))
|
84
|
+
|
85
|
+
* pseudo_kiosk_start(url_whitelist, unauthorized_endpoint_redirect_url)
|
86
|
+
* pseudo_kiosk_exit(unlock_redirect_url)
|
87
|
+
* clear_pseudo_kiosk_session
|
88
|
+
|
89
|
+
|
90
|
+
## Contributing
|
91
|
+
Feel free to submit PRs or Issues to the project's github page - https://github.com/jonmchan/pseudo_kiosk.
|
92
|
+
|
93
|
+
## License
|
94
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'PseudoKiosk'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rspec/core/rake_task'
|
25
|
+
RSpec::Core::RakeTask.new(:spec)
|
26
|
+
|
27
|
+
task default: :spec
|
@@ -0,0 +1,14 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require rails-ujs
|
14
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class PseudoKiosk::AuthenticationController < ApplicationController
|
2
|
+
skip_before_action :verify_authenticity_token
|
3
|
+
def unlock
|
4
|
+
unless session[:pseudo_kiosk_enabled]
|
5
|
+
redirect_back(fallback_location: root_path)
|
6
|
+
return
|
7
|
+
end
|
8
|
+
render :layout => false
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_submit
|
12
|
+
unless session[:pseudo_kiosk_enabled]
|
13
|
+
redirect_back(fallback_location: root_path)
|
14
|
+
return
|
15
|
+
end
|
16
|
+
if PseudoKiosk::Config.unlock_mechanism.nil?
|
17
|
+
raise "PseudoKiosk::Config.unlock_mechanism is missing!"
|
18
|
+
elsif PseudoKiosk::Config.unlock_mechanism.is_a? String
|
19
|
+
PseudoKiosk::Config.unlock_mechanism == params[:passcode] ? unlock_success : unlock_fail
|
20
|
+
elsif PseudoKiosk::Config.unlock_mechanism.is_a? Proc
|
21
|
+
PseudoKiosk::Config.unlock_mechanism.call(self, params) ? unlock_success : unlock_fail
|
22
|
+
else
|
23
|
+
raise "No clue how to use an PseudoKiosk::Config.unlock_mechanism that is a #{PseudoKiosk::Config.unlock_mechanism.class}!"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def unlock_success
|
29
|
+
redirect_url = session[:pseudo_kiosk_unlock_redirect_url]
|
30
|
+
clear_pseudo_kiosk_session
|
31
|
+
redirect_to redirect_url
|
32
|
+
end
|
33
|
+
|
34
|
+
def unlock_fail
|
35
|
+
redirect_to(PseudoKiosk::Engine.routes.url_helpers.pseudo_kiosk_authentication_unlock_path(failed: "true"))
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Kiosk lock</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= stylesheet_link_tag "pseudo_kiosk/application", media: "all" %>
|
9
|
+
<%= javascript_include_tag "pseudo_kiosk/application" %>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<%= yield %>
|
14
|
+
|
15
|
+
</body>
|
16
|
+
</html>
|
@@ -0,0 +1,259 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
|
6
|
+
<%= csrf_meta_tags %>
|
7
|
+
<%= csp_meta_tag %>
|
8
|
+
<title>Operator Unlock</title>
|
9
|
+
|
10
|
+
<meta name="viewport" content="width=device-width">
|
11
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
|
12
|
+
|
13
|
+
<style>
|
14
|
+
.pullee {
|
15
|
+
width: 10rem;
|
16
|
+
appearance: none;
|
17
|
+
}
|
18
|
+
.pullee:active::-webkit-slider-thumb {
|
19
|
+
appearance: none;
|
20
|
+
transform: scale(1.1);
|
21
|
+
cursor: -webkit-grabbing;
|
22
|
+
cursor: -moz-grabbing;
|
23
|
+
}
|
24
|
+
.pullee:active::-moz-range-thumb {
|
25
|
+
border: 0;
|
26
|
+
transform: scale(1.1);
|
27
|
+
cursor: -webkit-grabbing;
|
28
|
+
cursor: -moz-grabbing;
|
29
|
+
}
|
30
|
+
.pullee:active::-ms-thumb {
|
31
|
+
transform: scale(1.1);
|
32
|
+
cursor: -webkit-grabbing;
|
33
|
+
cursor: -moz-grabbing;
|
34
|
+
}
|
35
|
+
.pullee:focus {
|
36
|
+
outline: none;
|
37
|
+
}
|
38
|
+
.pullee::-webkit-slider-thumb {
|
39
|
+
appearance: none;
|
40
|
+
display: block;
|
41
|
+
width: 1rem;
|
42
|
+
height: 1rem;
|
43
|
+
border-radius: 50%;
|
44
|
+
background: #5990DD;
|
45
|
+
transform-origin: 50% 50%;
|
46
|
+
transform: scale(1);
|
47
|
+
transition: transform ease-out 100ms;
|
48
|
+
cursor: -webkit-grab;
|
49
|
+
cursor: -moz-grab;
|
50
|
+
}
|
51
|
+
.pullee::-moz-range-thumb {
|
52
|
+
border: 0;
|
53
|
+
display: block;
|
54
|
+
width: 1rem;
|
55
|
+
height: 1rem;
|
56
|
+
border-radius: 50%;
|
57
|
+
background: #5990DD;
|
58
|
+
transform-origin: 50% 50%;
|
59
|
+
transform: scale(1);
|
60
|
+
transition: transform ease-out 100ms;
|
61
|
+
cursor: -webkit-grab;
|
62
|
+
cursor: -moz-grab;
|
63
|
+
}
|
64
|
+
.pullee::-ms-thumb {
|
65
|
+
display: block;
|
66
|
+
width: 1rem;
|
67
|
+
height: 1rem;
|
68
|
+
border-radius: 50%;
|
69
|
+
background: #5990DD;
|
70
|
+
transform-origin: 50% 50%;
|
71
|
+
transform: scale(1);
|
72
|
+
transition: transform ease-out 100ms;
|
73
|
+
cursor: -webkit-grab;
|
74
|
+
cursor: -moz-grab;
|
75
|
+
}
|
76
|
+
.pullee::-webkit-slider-runnable-track {
|
77
|
+
height: 1rem;
|
78
|
+
padding: .25rem;
|
79
|
+
box-sizing: content-box;
|
80
|
+
border-radius: 1rem;
|
81
|
+
background-color: #DDE0E3;
|
82
|
+
}
|
83
|
+
.pullee::-moz-range-track {
|
84
|
+
height: 1rem;
|
85
|
+
padding: .25rem;
|
86
|
+
box-sizing: content-box;
|
87
|
+
border-radius: 1rem;
|
88
|
+
background-color: #DDE0E3;
|
89
|
+
}
|
90
|
+
.pullee::-moz-focus-outer {
|
91
|
+
border: 0;
|
92
|
+
}
|
93
|
+
.pullee::-ms-track {
|
94
|
+
border: 0;
|
95
|
+
height: 1rem;
|
96
|
+
padding: .25rem;
|
97
|
+
box-sizing: content-box;
|
98
|
+
border-radius: 1rem;
|
99
|
+
background-color: #DDE0E3;
|
100
|
+
color: transparent;
|
101
|
+
}
|
102
|
+
.pullee::-ms-fill-lower, .pullee::-ms-fill-upper {
|
103
|
+
background-color: transparent;
|
104
|
+
}
|
105
|
+
.pullee::-ms-tooltip {
|
106
|
+
display: none;
|
107
|
+
}
|
108
|
+
|
109
|
+
html {
|
110
|
+
font-size: 32px;
|
111
|
+
text-align: center;
|
112
|
+
}
|
113
|
+
|
114
|
+
h1 {
|
115
|
+
font-size: 0.8rem;
|
116
|
+
text-transform: uppercase;
|
117
|
+
letter-spacing: 1.25px;
|
118
|
+
}
|
119
|
+
|
120
|
+
h2 {
|
121
|
+
font-size: 0.6rem;
|
122
|
+
text-transform: uppercase;
|
123
|
+
letter-spacing: 1.25px;
|
124
|
+
}
|
125
|
+
|
126
|
+
input[type=submit] {
|
127
|
+
font-size: 0.8rem;
|
128
|
+
text-transform: uppercase;
|
129
|
+
letter-spacing: 1.25px;
|
130
|
+
margin: .65em;
|
131
|
+
}
|
132
|
+
|
133
|
+
.center-xy {
|
134
|
+
position: absolute;
|
135
|
+
top: 50%;
|
136
|
+
left: 50%;
|
137
|
+
transform: translate(-50%, -50%);
|
138
|
+
}
|
139
|
+
|
140
|
+
<% if params[:failed] %>
|
141
|
+
#pass_window {
|
142
|
+
display: initial;
|
143
|
+
}
|
144
|
+
#invalid {
|
145
|
+
display: initial;
|
146
|
+
font-size: 0.55rem;
|
147
|
+
color: red;
|
148
|
+
text-transform: uppercase;
|
149
|
+
letter-spacing: 1.25px;
|
150
|
+
}
|
151
|
+
|
152
|
+
#explanation_window {
|
153
|
+
display: none;
|
154
|
+
}
|
155
|
+
<% else %>
|
156
|
+
#pass_window {
|
157
|
+
display: none;
|
158
|
+
}
|
159
|
+
#invalid {
|
160
|
+
display: none;
|
161
|
+
}
|
162
|
+
|
163
|
+
#explanation_window {
|
164
|
+
display: initial;
|
165
|
+
}
|
166
|
+
<% end %>
|
167
|
+
</style>
|
168
|
+
<script>
|
169
|
+
window.console = window.console || function(t) {};
|
170
|
+
</script>
|
171
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
|
172
|
+
<script>
|
173
|
+
if (document.location.search.match(/type=embed/gi)) {
|
174
|
+
window.parent.postMessage("resize", "*");
|
175
|
+
}
|
176
|
+
</script>
|
177
|
+
</head>
|
178
|
+
|
179
|
+
<body translate="no">
|
180
|
+
<div id="explanation_window" class="center-xy">
|
181
|
+
<h1>Thank you for your input!</h1>
|
182
|
+
<h2>Please pass the device back to the operator</h2>
|
183
|
+
<h1>Slide to Unlock</h1>
|
184
|
+
<input type="range" value="0" class="pullee" />
|
185
|
+
</div>
|
186
|
+
<% # This is bad... I am having a problem using the named routes within the rails engine plugin %>
|
187
|
+
<%= form_tag 'process_submit', authenticity_token: true do %>
|
188
|
+
<div id="pass_window" class="center-xy">
|
189
|
+
<h1>Passcode:</h1>
|
190
|
+
<div id="invalid">Invalid Passcode! Please try again.</div>
|
191
|
+
<div><input type="password" name="passcode" style="text-align: center;"></div>
|
192
|
+
<div><input type="submit"></div>
|
193
|
+
<% end %>
|
194
|
+
</div>
|
195
|
+
<script id="rendered-js">
|
196
|
+
var inputRange = document.getElementsByClassName('pullee')[0],
|
197
|
+
maxValue = 150, // the higher the smoother when dragging
|
198
|
+
speed = 12, // thanks to @pixelass for this
|
199
|
+
currValue, rafID;
|
200
|
+
|
201
|
+
// set min/max value
|
202
|
+
inputRange.min = 0;
|
203
|
+
inputRange.max = maxValue;
|
204
|
+
|
205
|
+
// listen for unlock
|
206
|
+
function unlockStartHandler() {
|
207
|
+
// clear raf if trying again
|
208
|
+
window.cancelAnimationFrame(rafID);
|
209
|
+
|
210
|
+
// set to desired value
|
211
|
+
currValue = +this.value;
|
212
|
+
}
|
213
|
+
|
214
|
+
function unlockEndHandler() {
|
215
|
+
|
216
|
+
// store current value
|
217
|
+
currValue = +this.value;
|
218
|
+
|
219
|
+
// determine if we have reached success or not
|
220
|
+
if(currValue >= maxValue) {
|
221
|
+
successHandler();
|
222
|
+
}
|
223
|
+
else {
|
224
|
+
rafID = window.requestAnimationFrame(animateHandler);
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
// handle range animation
|
229
|
+
function animateHandler() {
|
230
|
+
|
231
|
+
// update input range
|
232
|
+
inputRange.value = currValue;
|
233
|
+
|
234
|
+
// determine if we need to continue
|
235
|
+
if(currValue > -1) {
|
236
|
+
window.requestAnimationFrame(animateHandler);
|
237
|
+
}
|
238
|
+
|
239
|
+
// decrement value
|
240
|
+
currValue = currValue - speed;
|
241
|
+
}
|
242
|
+
|
243
|
+
// handle successful unlock
|
244
|
+
function successHandler() {
|
245
|
+
document.getElementById("pass_window").style.display = "block";
|
246
|
+
document.getElementById("explanation_window").style.display = "none";
|
247
|
+
|
248
|
+
// reset input range
|
249
|
+
inputRange.value = 0;
|
250
|
+
};
|
251
|
+
|
252
|
+
// bind events
|
253
|
+
inputRange.addEventListener('mousedown', unlockStartHandler, false);
|
254
|
+
inputRange.addEventListener('mousestart', unlockStartHandler, false);
|
255
|
+
inputRange.addEventListener('mouseup', unlockEndHandler, false);
|
256
|
+
inputRange.addEventListener('touchend', unlockEndHandler, false);
|
257
|
+
</script>
|
258
|
+
</body>
|
259
|
+
</html>
|
data/config/routes.rb
ADDED
data/lib/pseudo_kiosk.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "pseudo_kiosk/engine"
|
2
|
+
|
3
|
+
module PseudoKiosk
|
4
|
+
autoload(:Config, 'pseudo_kiosk/config')
|
5
|
+
require 'pseudo_kiosk/controller'
|
6
|
+
module TestHelpers
|
7
|
+
require 'pseudo_kiosk/test_helpers/internal'
|
8
|
+
|
9
|
+
module Internal
|
10
|
+
require 'pseudo_kiosk/test_helpers/internal/rails'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module PseudoKiosk
|
2
|
+
module Config
|
3
|
+
class << self
|
4
|
+
# accepts a lambda function or string
|
5
|
+
# if lambda function is passed, instance of controller
|
6
|
+
# is passed to lambda for access to helper functions like
|
7
|
+
# current_user
|
8
|
+
attr_accessor :unlock_mechanism
|
9
|
+
|
10
|
+
def init!
|
11
|
+
@defaults = {
|
12
|
+
:@unlock_mechanism => nil,
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Resets all configuration options to their default values.
|
17
|
+
def reset!
|
18
|
+
@defaults.each do |k, v|
|
19
|
+
instance_variable_set(k, v)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def update!
|
24
|
+
@defaults.each do |k, v|
|
25
|
+
instance_variable_set(k, v) unless instance_variable_defined?(k)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def user_config(&blk)
|
30
|
+
block_given? ? @user_config = blk : @user_config
|
31
|
+
end
|
32
|
+
|
33
|
+
def configure(&blk)
|
34
|
+
@configure_blk = blk
|
35
|
+
@configure_blk.call(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def configure!
|
39
|
+
@configure_blk.call(self) if @configure_blk
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
init!
|
44
|
+
reset!
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module PseudoKiosk
|
2
|
+
module Controller
|
3
|
+
# code borrowed from Sorcery (https://github.com/Sorcery/sorcery/blob/ae83ac3181aed9696bf78ceabcce96bf735fd7ea/lib/sorcery/controller.rb)
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
end
|
8
|
+
#Config.update!
|
9
|
+
#Config.configure!
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
# To be used in before_action in the application_controller.
|
14
|
+
# If pseudo_kiosk enabled, all endpoints that are not in the kiosk_whitelist
|
15
|
+
# will not allowed to be accessed
|
16
|
+
def secure_pseudo_kiosk
|
17
|
+
# this needs to go to the unlock screen
|
18
|
+
# binding.pry if request.path_info == test_work_flow_complete_step3_privilege_path
|
19
|
+
if session[:pseudo_kiosk_enabled]
|
20
|
+
whitelist = session[:pseudo_kiosk_whitelist].is_a?(Array) ? session[:pseudo_kiosk_whitelist] : [session[:pseudo_kiosk_whitelist]]
|
21
|
+
|
22
|
+
return if session[:pseudo_kiosk_unauthorized_endpoint_redirect_url].nil? && (params['controller'] == 'pseudo_kiosk/authentication')
|
23
|
+
whitelist.each do |allowed_url|
|
24
|
+
next if allowed_url.nil?
|
25
|
+
if allowed_url.is_a? Regexp
|
26
|
+
return if allowed_url =~ request.path_info
|
27
|
+
elsif allowed_url.start_with? "(?-mix:"
|
28
|
+
return if Regexp.new(allowed_url) =~ request.path_info
|
29
|
+
else
|
30
|
+
return if allowed_url == request.path_info
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# need to either redirect to unauthorized_endpoint_redirect_url or allow user to break out
|
35
|
+
if session[:pseudo_kiosk_unauthorized_endpoint_redirect_url].nil?
|
36
|
+
session[:pseudo_kiosk_unlock_redirect_url] = request.path_info
|
37
|
+
|
38
|
+
redirect_to(pseudo_kiosk_engine.routes.url_helpers.pseudo_kiosk_authentication_unlock_path)
|
39
|
+
else
|
40
|
+
redirect_to(session[:pseudo_kiosk_unauthorized_endpoint_redirect_url])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Locks the session down for unprivileged usage
|
46
|
+
#
|
47
|
+
# Params:
|
48
|
+
# url_whitelist - an array of url strings or regex searches
|
49
|
+
# of endpoints allowed to be visited during kiosk lock mode
|
50
|
+
#
|
51
|
+
# unauthorized_endpoint_redirect - url to redirect to if user navigates
|
52
|
+
# to a url outside of the url_whitelist.
|
53
|
+
# If nil, the unlock screen will be shown when navigating to urls
|
54
|
+
# outside of the whitelist; upon successful authentication, the user
|
55
|
+
# will be redirected to current endpoint
|
56
|
+
def pseudo_kiosk_start(url_whitelist, unauthorized_endpoint_redirect_url)
|
57
|
+
session[:pseudo_kiosk_enabled] = true
|
58
|
+
session[:pseudo_kiosk_whitelist] = url_whitelist
|
59
|
+
session[:pseudo_kiosk_unauthorized_endpoint_redirect_url] = unauthorized_endpoint_redirect_url
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# redirects to pseudo_kiosk unlock screen. When successfully unlocked, the browser is
|
64
|
+
# redirected to the unlock_redirect_url
|
65
|
+
def pseudo_kiosk_exit(unlock_redirect_url)
|
66
|
+
session[:pseudo_kiosk_unlock_redirect_url] = unlock_redirect_url
|
67
|
+
|
68
|
+
# clear the whitelist here, because we want to only allow
|
69
|
+
# the session to be given back to the privileged user and
|
70
|
+
# for no further operations to be done in the whitelist area
|
71
|
+
session.delete(:pseudo_kiosk_whitelist)
|
72
|
+
session.delete(:pseudo_kiosk_unauthorized_endpoint_redirect_url)
|
73
|
+
|
74
|
+
redirect_to(PseudoKiosk::Engine.routes.url_helpers.pseudo_kiosk_authentication_unlock_path)
|
75
|
+
end
|
76
|
+
|
77
|
+
# clear all pseudo_kiosk session variables; this is an internal function
|
78
|
+
# Most likely pseudo_kiosk_exit is what should be used, unless there is some usecase
|
79
|
+
# where you want to instantly exit the pseudo_kiosk session without going through authentication
|
80
|
+
def clear_pseudo_kiosk_session
|
81
|
+
session.delete(:pseudo_kiosk_enabled)
|
82
|
+
session.delete(:pseudo_kiosk_whitelist)
|
83
|
+
session.delete(:pseudo_kiosk_unauthorized_endpoint_redirect_url)
|
84
|
+
session.delete(:pseudo_kiosk_unlock_redirect_url)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PseudoKiosk
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
#config.pseudo_kiosk = PseudoKiosk::Config
|
4
|
+
# Do we need this for what we're doing?
|
5
|
+
# isolate_namespace PseudoKiosk
|
6
|
+
|
7
|
+
initializer 'extend Controller with PseudoKiosk' do
|
8
|
+
if defined?(ActionController::Base)
|
9
|
+
ActionController::Base.send(:include, PseudoKiosk::Controller)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module PseudoKiosk
|
2
|
+
module TestHelpers
|
3
|
+
# Internal TestHelpers are used to test the gem, internally, and should not be used to test apps *using* sorcery.
|
4
|
+
# This file will be included in the spec_helper file.
|
5
|
+
module Internal
|
6
|
+
def self.included(_base)
|
7
|
+
# # reducing default cost for specs speed
|
8
|
+
# CryptoProviders::BCrypt.class_eval do
|
9
|
+
# class << self
|
10
|
+
# def cost
|
11
|
+
# 1
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
end
|
16
|
+
|
17
|
+
# a patch to fix a bug in testing that happens when you 'destroy' a session twice.
|
18
|
+
# After the first destroy, the session is an ordinary hash, and then when destroy
|
19
|
+
# is called again there's an exception.
|
20
|
+
class ::Hash # rubocop:disable Style/ClassAndModuleChildren
|
21
|
+
def destroy
|
22
|
+
clear
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_new_user(attributes_hash = nil)
|
27
|
+
user_attributes_hash = attributes_hash || { username: 'gizmo', email: 'bla@bla.com', password: 'secret' }
|
28
|
+
@user = User.new(user_attributes_hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_new_user(attributes_hash = nil)
|
32
|
+
@user = build_new_user(attributes_hash)
|
33
|
+
@user.sorcery_adapter.save(raise_on_failure: true)
|
34
|
+
@user
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_new_external_user(provider, attributes_hash = nil)
|
38
|
+
user_attributes_hash = attributes_hash || { username: 'gizmo' }
|
39
|
+
@user = User.new(user_attributes_hash)
|
40
|
+
@user.sorcery_adapter.save(raise_on_failure: true)
|
41
|
+
@user.authentications.create!(provider: provider, uid: 123)
|
42
|
+
@user
|
43
|
+
end
|
44
|
+
|
45
|
+
def custom_create_new_external_user(provider, authentication_class, attributes_hash = nil)
|
46
|
+
authentication_association = authentication_class.name.underscore.pluralize
|
47
|
+
|
48
|
+
user_attributes_hash = attributes_hash || { username: 'gizmo' }
|
49
|
+
@user = User.new(user_attributes_hash)
|
50
|
+
@user.sorcery_adapter.save(raise_on_failure: true)
|
51
|
+
@user.send(authentication_association).create!(provider: provider, uid: 123)
|
52
|
+
@user
|
53
|
+
end
|
54
|
+
|
55
|
+
def sorcery_model_property_set(property, *values)
|
56
|
+
User.class_eval do
|
57
|
+
sorcery_config.send(:"#{property}=", *values)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def update_model(&block)
|
62
|
+
User.class_exec(&block)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# reload user class between specs
|
68
|
+
# so it will be possible to test the different submodules in isolation
|
69
|
+
def reload_user_class
|
70
|
+
User && Object.send(:remove_const, 'User')
|
71
|
+
load 'user.rb'
|
72
|
+
|
73
|
+
return unless User.respond_to?(:reset_column_information)
|
74
|
+
|
75
|
+
User.reset_column_information
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module PseudoKiosk
|
2
|
+
module TestHelpers
|
3
|
+
module Internal
|
4
|
+
module Rails
|
5
|
+
def pseudo_kiosk_reload!
|
6
|
+
PseudoKiosk::Config.init!
|
7
|
+
PseudoKiosk::Config.reset!
|
8
|
+
|
9
|
+
ActionController::Base.send(:include, PseudoKiosk::Controller)
|
10
|
+
end
|
11
|
+
|
12
|
+
def pseudo_kiosk_config_property_set(property, value)
|
13
|
+
::PseudoKiosk::Config.send(:"#{property}=", value)
|
14
|
+
end
|
15
|
+
|
16
|
+
# def pseudo_kiosk_config_external_property_set(provider, property, value)
|
17
|
+
# ::PseudoKiosk::Config.send(provider).send(:"#{property}=", value)
|
18
|
+
# end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pseudo_kiosk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan Chan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-10-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sqlite3
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: PseudoKiosk gives the ability to limit access to the application during
|
70
|
+
workflows which involve physically passing a device with elevated permissions to
|
71
|
+
an unknown or untrusted user.
|
72
|
+
email:
|
73
|
+
- jc@jmccc.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- MIT-LICENSE
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- app/assets/config/pseudo_kiosk_manifest.js
|
82
|
+
- app/assets/javascripts/pseudo_kiosk/application.js
|
83
|
+
- app/assets/javascripts/pseudo_kiosk/authentication.js
|
84
|
+
- app/assets/stylesheets/pseudo_kiosk/application.css
|
85
|
+
- app/assets/stylesheets/pseudo_kiosk/authentication.css
|
86
|
+
- app/controllers/pseudo_kiosk/application_controller.rb
|
87
|
+
- app/controllers/pseudo_kiosk/authentication_controller.rb
|
88
|
+
- app/helpers/pseudo_kiosk/application_helper.rb
|
89
|
+
- app/helpers/pseudo_kiosk/authentication_helper.rb
|
90
|
+
- app/views/layouts/pseudo_kiosk/application.html.erb
|
91
|
+
- app/views/pseudo_kiosk/authentication/process_submit.html.erb
|
92
|
+
- app/views/pseudo_kiosk/authentication/unlock.html.erb
|
93
|
+
- config/routes.rb
|
94
|
+
- lib/pseudo_kiosk.rb
|
95
|
+
- lib/pseudo_kiosk/config.rb
|
96
|
+
- lib/pseudo_kiosk/controller.rb
|
97
|
+
- lib/pseudo_kiosk/engine.rb
|
98
|
+
- lib/pseudo_kiosk/test_helpers/internal.rb
|
99
|
+
- lib/pseudo_kiosk/test_helpers/internal/rails.rb
|
100
|
+
- lib/pseudo_kiosk/version.rb
|
101
|
+
- lib/tasks/pseudo_kiosk_tasks.rake
|
102
|
+
homepage: https://github.com/jonmchan/pseudo_kiosk
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata:
|
106
|
+
allowed_push_host: https://rubygems.org
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: 2.5.0
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubygems_version: 3.0.3
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: PseudoKiosk provides the ability to lock down a rails application to only
|
126
|
+
a specified whitelist of endpoints during a session.
|
127
|
+
test_files: []
|