door_code 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,19 +1,38 @@
1
- ### DoorCode. Restrict access with a 5-digit PIN code.
1
+ Door Code
2
+ =========
3
+
4
+ ### Restrict access with a 3-6 digit PIN code.
2
5
 
3
6
  ## Installation
4
7
 
5
- Install with Bundler:
8
+ Rubygems:
9
+
10
+ (sudo) gem install door_code
11
+
12
+ Bundler:
13
+
14
+ gem 'door_code', '~> 0.0.3'
6
15
 
7
- gem 'door_code', '0.0.2'
16
+ ### Then
8
17
 
9
18
  In config.ru:
10
19
 
11
20
  use DoorCode::RestrictedAccess, :code => '12345'
21
+
22
+ # to use a custom salt for cookie encryption
23
+
24
+ use DoorCode::RestrictedAccess, :code => '12345', :salt => "my super secret code"
25
+
26
+
12
27
 
13
28
  In application.rb (Rails3) or environment.rb (Rails2):
14
29
 
15
30
  config.middleware.use DoorCode::RestrictedAccess, :code => '12345'
16
31
 
32
+ ## Demo
33
+
34
+ There is a simple demo application running on Heroku at [http://doorcodedemo.heroku.com](http://doorcodedemo.heroku.com). Log in using the default door code: `12345`
35
+
17
36
  ## Notes
18
37
 
19
38
  * The default code is '12345'
@@ -21,5 +40,5 @@ In application.rb (Rails3) or environment.rb (Rails2):
21
40
 
22
41
  ## To Do
23
42
 
24
- * Example app (heroku, sinatra)
25
43
  * Allow specifying domains and paths to restrict access conditionally
44
+ * Write more tests
data/Rakefile CHANGED
@@ -1,2 +1,12 @@
1
1
  require 'bundler'
2
+ require 'rake/testtask'
3
+
2
4
  Bundler::GemHelper.install_tasks
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test' << 'lib'
8
+ t.verbose = true
9
+ end
10
+
11
+ desc "Default Task"
12
+ task :default => [ :test ]
data/door_code.gemspec CHANGED
@@ -3,10 +3,10 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "door_code"
6
- s.version = '0.0.3'
6
+ s.version = '0.0.5'
7
7
  s.platform = Gem::Platform::RUBY
8
- s.authors = ["Mike Fulcher", "Alex Neill"]
9
- s.email = ["mike@plan9design.co.uk", "alex.neill@gmail.com"]
8
+ s.authors = ["Mike Fulcher", "Alex Neill", "Spencer Steffen"]
9
+ s.email = ["mike@plan9design.co.uk", "alex.neill@gmail.com", "spencer@citrusme.com"]
10
10
  s.homepage = "https://github.com/6twenty/door_code"
11
11
  s.summary = %q{Restrict access to your site with a 3-6 digit PIN code}
12
12
  s.description = %q{Rack middleware which requires that visitors to the site enter a 3-6 digit PIN code to gain access.}
@@ -20,4 +20,8 @@ Gem::Specification.new do |s|
20
20
 
21
21
  # Runtime
22
22
  s.add_runtime_dependency 'rack'
23
+
24
+ s.add_development_dependency 'shoulda', '2.11.3'
25
+ s.add_development_dependency 'rack-test', '0.5.7'
26
+
23
27
  end
data/lib/door_code.rb CHANGED
@@ -1,119 +1,2 @@
1
- module DoorCode
2
- class RestrictedAccess
3
-
4
- MIN_LENGTH = 3
5
- MAX_LENGTH = 6
6
-
7
- DEFAULT_CODE = '12345'
8
-
9
- def initialize app, options={}
10
- @app = app
11
- @code = parse_code(options[:code])
12
- end
13
-
14
- # Ensures the code is good & valid, otherwise
15
- # reverts to the default
16
- def parse_code code
17
- parsed_code = code.gsub(/\D/, '')
18
- if parsed_code == code
19
- # Means the supplied code contains only digits, which is good
20
- # Just need to check that the code length is valid
21
- parsed_code = DEFAULT_CODE if code.length < MIN_LENGTH || code.length > MAX_LENGTH
22
- else
23
- # Means the supplied code contained non-digits, so revert to default
24
- parsed_code = DEFAULT_CODE
25
- end
26
- parsed_code
27
- end
28
-
29
- def cookie_name
30
- 'door_code'
31
- end
32
-
33
- # Rack::Request wrapper around @env
34
- def request
35
- @request ||= Rack::Request.new(@env)
36
- end
37
-
38
- # Rack::Response object with which to respond with
39
- def response
40
- @response ||= Rack::Response.new
41
- end
42
-
43
- # Is the request verb POST?
44
- def post?
45
- request.request_method == 'POST'
46
- end
47
-
48
- # Was the request called via AJAX?
49
- def xhr?
50
- @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
51
- end
52
-
53
- # Code supplied from user
54
- def supplied_code
55
- request.params['code']
56
- end
57
-
58
- # Is the supplied code vaid for the current area
59
- def valid_code? code
60
- @code == code
61
- end
62
-
63
- # Check if the supplied code is valid;
64
- # Either sets a confirming cookie and Success message
65
- # or delete any door code cookie and set Failure message
66
- def validate_code!
67
- valid_code?(supplied_code) ? confirm! : unconfirm!
68
- end
69
-
70
- # Is there a valid code for the area set in the cookie
71
- def confirmed?
72
- request.cookies[cookie_name] && valid_code?(request.cookies[cookie_name])
73
- end
74
-
75
- # Set a cookie for the correct value (server value may change)
76
- # Also set up Success message
77
- def confirm!
78
- xhr? ? response.write('success') : response.redirect('/')
79
- response.set_cookie(cookie_name, {:value => supplied_code, :path => "/"})
80
- end
81
-
82
- # Delete and invalid cookies
83
- # Also set up Failure message
84
- def unconfirm!
85
- xhr? ? response.write('failure') : response.redirect('/')
86
- response.delete_cookie(supplied_code)
87
- end
88
-
89
- def code_length
90
- @code.length.to_s
91
- end
92
-
93
- def build_rack_objects
94
- @request = Rack::Request.new(@env)
95
- @response = Rack::Response.new
96
- end
97
-
98
- # Where the magic happens...
99
- def call env
100
- @env = env
101
- build_rack_objects
102
-
103
- return @app.call(env) if confirmed?
104
- p 'Loading DoorCode::RestrictedAccess'
105
-
106
- if post?
107
- response['Content-Type'] = 'text/javascript' if xhr?
108
- validate_code! # Validate the user's code and set a cookie if valid
109
- else
110
- index = ::File.read(::File.dirname(__FILE__) + '/index.html')
111
- index_with_code_length = index.gsub(/\{\{codeLength\}\}/, code_length)
112
- response.write index_with_code_length
113
- end
114
-
115
- # Render response
116
- return response.finish
117
- end
118
- end
119
- end
1
+ require 'digest/sha1'
2
+ require 'door_code/restricted_access'
@@ -283,7 +283,10 @@
283
283
 
284
284
  var interval;
285
285
  var code;
286
- var codeLength = {{codeLength}};
286
+ var minLength = 3;
287
+ var maxLength = 6;
288
+
289
+ var clones;
287
290
 
288
291
  var defaultClass = 'green';
289
292
  var successClass = 'blue';
@@ -321,7 +324,7 @@
321
324
  // Map clicking on the keys to showing the number in the display
322
325
  $('#keys a').click(function(e){
323
326
  e.preventDefault();
324
- if ($('.clone').length < codeLength) {
327
+ if ($('.clone').length <= maxLength) {
325
328
  var num = $(this).attr('rel');
326
329
  if (num === 'clear' || num === 'help') {
327
330
  if (num === 'clear') { reset(); } else
@@ -340,7 +343,7 @@
340
343
  if (charCode === backspace) { e.preventDefault(); }
341
344
  if (charCode === backspace && $('.clone').length > 0) {
342
345
  $('.clone').last().remove();
343
- } else if (validKeys.indexOf(charCode) >= 0 && $('.clone').length < codeLength) {
346
+ } else if (validKeys.indexOf(charCode) >= 0 && $('.clone').length <= maxLength) {
344
347
  var n = charCode;
345
348
  var num = '';
346
349
  if (zero.indexOf(n) >= 0) { num = 'zero' }
@@ -382,12 +385,11 @@
382
385
 
383
386
  function checkCode() {
384
387
  // Check that the code is the correct length, and check it via ajax
385
- if ($('.clone').length == codeLength) {
388
+ clones = $('.clone');
389
+ if (clones.length >= minLength && clones.length <= maxLength) {
386
390
  // Now we need to manually build the code based on the digits on display
387
391
  code = '';
388
- $('.clone').each(function(){
389
- code += $(this).attr('rel');
390
- });
392
+ $('.clone').each(function(){ code += $(this).attr('rel'); });
391
393
  ajax();
392
394
  }
393
395
  }
@@ -400,7 +402,7 @@
400
402
  dataType: "text",
401
403
  success: function(data, textStatus, jqXHR){
402
404
  if (data === 'success') { showSuccess(); } else
403
- if (data === 'failure') { showError(); }
405
+ if (data === 'failure' && clones.length == maxLength) { showError(); }
404
406
  }
405
407
  });
406
408
  }
@@ -0,0 +1,126 @@
1
+ module DoorCode
2
+ class RestrictedAccess
3
+
4
+ MIN_LENGTH = 3
5
+ MAX_LENGTH = 6
6
+
7
+ DEFAULT_CODE = '12345'
8
+
9
+ def initialize app, options={}
10
+ @app = app
11
+ @salt = parse_salt(options[:salt])
12
+ @code = parse_code(options[:code])
13
+ end
14
+
15
+ # Ensures the code is good & valid, otherwise
16
+ # reverts to the default
17
+ def parse_code(code)
18
+ parsed_code = code.to_s.gsub(/\D/, '')
19
+ if parsed_code == code
20
+ # Means the supplied code contains only digits, which is good
21
+ # Just need to check that the code length is valid
22
+ parsed_code = DEFAULT_CODE if code.length < MIN_LENGTH || code.length > MAX_LENGTH
23
+ else
24
+ # Means the supplied code contained non-digits, so revert to default
25
+ parsed_code = DEFAULT_CODE
26
+ end
27
+ Digest::SHA1.hexdigest("--#{@salt}--#{parsed_code}--")
28
+ end
29
+
30
+
31
+ # Ensures a salt is supplied, otherwise set to default
32
+ def parse_salt(salt)
33
+ if 0 < salt.to_s.length
34
+ salt = Digest::SHA1.hexdigest("Door Code Secret Key")
35
+ end
36
+ salt
37
+ end
38
+
39
+ # Name of the cookie
40
+ def cookie_name
41
+ 'door_code'
42
+ end
43
+
44
+ # Returns the value of the saved cookie
45
+ def cookied_code
46
+ request.cookies[cookie_name]
47
+ end
48
+
49
+
50
+ # Rack::Request wrapper around @env
51
+ def request
52
+ @request ||= Rack::Request.new(@env)
53
+ end
54
+
55
+ # Rack::Response object with which to respond with
56
+ def response
57
+ @response ||= Rack::Response.new
58
+ end
59
+
60
+ # Encrypted code supplied from user
61
+ def supplied_code
62
+ Digest::SHA1.hexdigest("--#{@salt}--#{request.params['code']}--")
63
+ end
64
+
65
+ # Is the supplied code valid for the current area
66
+ def valid_code?(code)
67
+ @code == code
68
+ end
69
+
70
+ # Check if the supplied code is valid;
71
+ # Either sets a confirming cookie and Success message
72
+ # or delete any door code cookie and set Failure message
73
+ def validate_code!
74
+ valid_code?(supplied_code) ? confirm! : unconfirm!
75
+ end
76
+
77
+ # Is there a valid code for the area set in the cookie
78
+ def confirmed?
79
+ cookied_code && valid_code?(cookied_code)
80
+ end
81
+
82
+ # Set a cookie for the correct value (server value may change)
83
+ # Also set up Success message
84
+ def confirm!
85
+ request.xhr? ? response.write('success') : response.redirect('/')
86
+ response.set_cookie(cookie_name, { :value => supplied_code, :path => "/", :expire_after => (24*60*60) })
87
+ end
88
+
89
+ # Delete and invalid cookies
90
+ # Also set up Failure message
91
+ def unconfirm!
92
+ request.xhr? ? response.write('failure') : response.redirect('/')
93
+ response.delete_cookie(supplied_code)
94
+ end
95
+
96
+ # Creates instances of Rack::Request and Rack::Response
97
+ def build_rack_objects
98
+ @request = Rack::Request.new(@env)
99
+ @response = Rack::Response.new
100
+ end
101
+
102
+ # Where the magic happens...
103
+ def call(env)
104
+ @env = env
105
+ build_rack_objects
106
+
107
+ return @app.call(env) if confirmed?
108
+ p 'Loading DoorCode::RestrictedAccess'
109
+
110
+ if request.post?
111
+ response['Content-Type'] = 'text/javascript' if request.xhr?
112
+ validate_code! # Validate the user's code and set a cookie if valid
113
+ else
114
+
115
+ # Set request status to Unauthorized
116
+ #response.status = 401
117
+
118
+ index = ::File.read(::File.dirname(__FILE__) + '/index.html')
119
+ response.write index
120
+ end
121
+
122
+ # Render response
123
+ return response.finish
124
+ end
125
+ end
126
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,31 @@
1
+ ENV["environment"] = "test"
2
+
3
+ require 'test/unit'
4
+ require 'rack/test'
5
+ require 'shoulda'
6
+ require 'door_code'
7
+ require 'sinatra/base'
8
+
9
+
10
+ module Rack
11
+ module Test
12
+ DEFAULT_HOST='localhost'
13
+ end
14
+ end
15
+
16
+ class TestingApp < Sinatra::Base
17
+
18
+ #set :sessions, false
19
+
20
+ use DoorCode::RestrictedAccess, :code => '12345'
21
+
22
+ get '/' do
23
+ 'Logged In!'
24
+ end
25
+
26
+ get '/logout' do
27
+ response.delete_cookie('door_code')
28
+ redirect '/'
29
+ end
30
+
31
+ end
@@ -0,0 +1,59 @@
1
+ require 'helper'
2
+
3
+ # '12345' encrypted with the default salt
4
+ DEFAULT_CODE = '9fa483ac55e30318a84f0046365a21021a409117'
5
+
6
+ class TestRestrictedAccess < Test::Unit::TestCase
7
+
8
+ include Rack::Test::Methods
9
+
10
+ def app
11
+ TestingApp.new
12
+ end
13
+
14
+ def setup
15
+ clear_cookies
16
+ end
17
+
18
+ should "require login" do
19
+ get "/"
20
+ assert_equal 200, last_response.status
21
+ assert last_response.body.include?("Authorized Personnel Only")
22
+ end
23
+
24
+ should "validate login" do
25
+ post "/", { "code" => "12345" }
26
+ assert_equal 302, last_response.status
27
+
28
+ follow_redirect!
29
+
30
+ assert_equal 200, last_response.status
31
+ assert last_response.body.include?("Logged In")
32
+ assert_equal DEFAULT_CODE, rack_mock_session.cookie_jar['door_code']
33
+ end
34
+
35
+
36
+ context "when cookie exists" do
37
+
38
+ setup do
39
+ set_cookie("door_code=#{DEFAULT_CODE}")
40
+ end
41
+
42
+ should "allow authorized cookies" do
43
+ get "/"
44
+ assert_equal 200, last_response.status
45
+ assert last_response.body.include?("Logged In")
46
+ end
47
+
48
+ should "logout" do
49
+ get "/logout"
50
+ assert_equal 302, last_response.status
51
+
52
+ follow_redirect!
53
+
54
+ assert_equal 200, last_response.status
55
+ assert last_response.body.include?("Authorized Personnel Only")
56
+ end
57
+
58
+ end
59
+ end
metadata CHANGED
@@ -1,17 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: door_code
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 21
4
5
  prerelease:
5
- version: 0.0.3
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 5
10
+ version: 0.0.5
6
11
  platform: ruby
7
12
  authors:
8
13
  - Mike Fulcher
9
14
  - Alex Neill
15
+ - Spencer Steffen
10
16
  autorequire:
11
17
  bindir: bin
12
18
  cert_chain: []
13
19
 
14
- date: 2011-02-20 00:00:00 +00:00
20
+ date: 2011-02-21 00:00:00 +00:00
15
21
  default_executable:
16
22
  dependencies:
17
23
  - !ruby/object:Gem::Dependency
@@ -22,13 +28,49 @@ dependencies:
22
28
  requirements:
23
29
  - - ">="
24
30
  - !ruby/object:Gem::Version
31
+ hash: 3
32
+ segments:
33
+ - 0
25
34
  version: "0"
26
35
  type: :runtime
27
36
  version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: shoulda
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 37
46
+ segments:
47
+ - 2
48
+ - 11
49
+ - 3
50
+ version: 2.11.3
51
+ type: :development
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rack-test
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - "="
60
+ - !ruby/object:Gem::Version
61
+ hash: 5
62
+ segments:
63
+ - 0
64
+ - 5
65
+ - 7
66
+ version: 0.5.7
67
+ type: :development
68
+ version_requirements: *id003
28
69
  description: Rack middleware which requires that visitors to the site enter a 3-6 digit PIN code to gain access.
29
70
  email:
30
71
  - mike@plan9design.co.uk
31
72
  - alex.neill@gmail.com
73
+ - spencer@citrusme.com
32
74
  executables: []
33
75
 
34
76
  extensions: []
@@ -42,7 +84,10 @@ files:
42
84
  - Rakefile
43
85
  - door_code.gemspec
44
86
  - lib/door_code.rb
45
- - lib/index.html
87
+ - lib/door_code/index.html
88
+ - lib/door_code/restricted_access.rb
89
+ - test/helper.rb
90
+ - test/test_restricted_access.rb
46
91
  has_rdoc: true
47
92
  homepage: https://github.com/6twenty/door_code
48
93
  licenses: []
@@ -57,19 +102,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
102
  requirements:
58
103
  - - ">="
59
104
  - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
60
108
  version: "0"
61
109
  required_rubygems_version: !ruby/object:Gem::Requirement
62
110
  none: false
63
111
  requirements:
64
112
  - - ">="
65
113
  - !ruby/object:Gem::Version
114
+ hash: 3
115
+ segments:
116
+ - 0
66
117
  version: "0"
67
118
  requirements: []
68
119
 
69
120
  rubyforge_project: door_code
70
- rubygems_version: 1.5.2
121
+ rubygems_version: 1.5.1
71
122
  signing_key:
72
123
  specification_version: 3
73
124
  summary: Restrict access to your site with a 3-6 digit PIN code
74
- test_files: []
75
-
125
+ test_files:
126
+ - test/helper.rb
127
+ - test/test_restricted_access.rb