Class: DoorCode::RestrictedAccess

Inherits:
Object
  • Object
show all
Defined in:
lib/door_code/restricted_access.rb

Constant Summary collapse

MIN_LENGTH =
3
MAX_LENGTH =
6
DEFAULT_CODE =
'12345'

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ RestrictedAccess

Returns a new instance of RestrictedAccess.



25
26
27
28
29
30
# File 'lib/door_code/restricted_access.rb', line 25

def initialize app, options={}
  @app = app
  # The code or codes can be supplied as either a single string or an array using either
  # the ":code" or ":codes" key. ":codes" trumps ":code" if both are supplied
  @codes = options[:codes] ? parse_codes(options[:codes]) : parse_codes(options[:code])
end

Instance Method Details

#build_rack_objectsObject

Creates instances of Rack::Request and Rack::Response



124
125
126
127
# File 'lib/door_code/restricted_access.rb', line 124

def build_rack_objects
  @request = Rack::Request.new(@env)
  @response = Rack::Response.new
end

#call(env) ⇒ Object

Where the magic happens…



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/door_code/restricted_access.rb', line 130

def call(env)
  @env = env
  build_rack_objects
  
  return @app.call(env) if confirmed?
  p 'DoorCode: Unauthorized personnel detected'
  
  if request.post?
    response['Content-Type'] = 'text/javascript' if request.xhr?
    validate_code! # Validate the user's code and set a cookie if valid
  else
    
    # Set request status to Unauthorized
    #response.status = 401

    index = ::File.read(::File.dirname(__FILE__) + '/index.html')
    response.write index
  end
        
  # Render response
  return response.finish
end

#confirm!Object

Set a cookie for the correct value (server value may change) Also set up Success message



111
112
113
114
# File 'lib/door_code/restricted_access.rb', line 111

def confirm!
  request.xhr? ? response.write('success') : response.redirect('/')
  response.set_cookie(cookie_name, { :value => supplied_code, :path => "/", :expire_after => (24*60*60) })
end

#confirmed?Boolean

Is there a valid code for the area set in the cookie

Returns:

  • (Boolean)


105
106
107
# File 'lib/door_code/restricted_access.rb', line 105

def confirmed?
  cookied_code && valid_code?(cookied_code)
end

Name of the cookie



67
68
69
# File 'lib/door_code/restricted_access.rb', line 67

def cookie_name
  'door_code'
end

#cookied_codeObject

Returns the value of the saved cookie



72
73
74
# File 'lib/door_code/restricted_access.rb', line 72

def cookied_code
  request.cookies[cookie_name]
end

#parse_code(code) ⇒ Object

Checks that the code provided is valid, returning nil if not



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/door_code/restricted_access.rb', line 46

def parse_code(code)
  parsed_code = code.to_s.gsub(/\D/, '')
  if parsed_code == code && (code.length < MIN_LENGTH || code.length > MAX_LENGTH)
    # Means the supplied code contains only digits, which is good
    # Just need to check that the code length is valid
    parsed_code = nil
    p "DoorCode: invalid PIN code detected"
  elsif parsed_code != code
    # Means the supplied code contained non-digits, so revert to default
    parsed_code = nil
    p "DoorCode: invalid PIN code detected"
  end
  parsed_code
end

#parse_codes(codes) ⇒ Object

Filters the supplied codes to ensure they are valid, and sets the DEFAULT_CODE if no valid codes are detected



34
35
36
37
38
39
40
41
42
43
# File 'lib/door_code/restricted_access.rb', line 34

def parse_codes(codes)
  parsed_codes = codes.respond_to?(:any?) ? codes.map { |c| parse_code(c) } : [parse_code(codes)]
  # If there are any valid codes supplied which are unique and valid, 
  # strip the default code out in order to circumvent a security hole
  if parsed_codes.compact.uniq.empty?
    parsed_codes << DEFAULT_CODE
    p "DoorCode: no valid codes detected - activating default code"
  end
  parsed_codes.compact.uniq.map { |c| Digest::SHA1.hexdigest("--#{salt}--#{c}--") }
end

#requestObject

Rack::Request wrapper around @env



78
79
80
# File 'lib/door_code/restricted_access.rb', line 78

def request
  @request ||= Rack::Request.new(@env)
end

#responseObject

Rack::Response object with which to respond with



83
84
85
# File 'lib/door_code/restricted_access.rb', line 83

def response
  @response ||= Rack::Response.new
end

#saltObject

Returns the salt or creates one



62
63
64
# File 'lib/door_code/restricted_access.rb', line 62

def salt
  @salt ||= DoorCode.salt
end

#supplied_codeObject

Encrypted code supplied from user



88
89
90
# File 'lib/door_code/restricted_access.rb', line 88

def supplied_code
  Digest::SHA1.hexdigest("--#{salt}--#{request.params['code']}--")
end

#unconfirm!Object

Delete and invalid cookies Also set up Failure message



118
119
120
121
# File 'lib/door_code/restricted_access.rb', line 118

def unconfirm!
  request.xhr? ? response.write('failure') : response.redirect('/')
  response.delete_cookie(supplied_code)
end

#valid_code?(code) ⇒ Boolean

Is the supplied code valid for the current area

Returns:

  • (Boolean)


93
94
95
# File 'lib/door_code/restricted_access.rb', line 93

def valid_code?(code)
  @codes.include?(code)
end

#validate_code!Object

Check if the supplied code is valid; Either sets a confirming cookie and Success message or delete any door code cookie and set Failure message



100
101
102
# File 'lib/door_code/restricted_access.rb', line 100

def validate_code!
  valid_code?(supplied_code) ? confirm! : unconfirm!
end