door_code 0.0.3 → 0.0.5

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.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