touchpass 0.0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +38 -0
- data/Guardfile +9 -0
- data/Rakefile +1 -0
- data/app/controllers/touchpass/verifications_controller.rb +30 -0
- data/bin/tpcli.rb +248 -0
- data/config/routes.rb +8 -0
- data/lib/engine.rb +4 -0
- data/lib/generators/templates/config/initializers/devise.rb +204 -0
- data/lib/generators/templates/config/initializers/touchpass.rb +24 -0
- data/lib/generators/templates/config/locales/devise.en.yml +53 -0
- data/lib/generators/touchpass/install_generator.rb +36 -0
- data/lib/touchpass/client.rb +431 -0
- data/lib/touchpass/crypt.rb +51 -0
- data/lib/touchpass/device.rb +18 -0
- data/lib/touchpass/key_file_creator.rb +56 -0
- data/lib/touchpass/prp.rb +256 -0
- data/lib/touchpass/verification.rb +112 -0
- data/lib/touchpass/version.rb +3 -0
- data/lib/touchpass.rb +53 -0
- data/spec/bounding_box_spec.rb +26 -0
- data/spec/geocode_spec.rb +22 -0
- data/spec/helpers/client_spec_helper.rb +1 -0
- data/spec/helpers/gtp_spec_helper.rb +98 -0
- data/spec/key_file_creator_spec.rb +30 -0
- data/spec/proximity_spec.rb +283 -0
- data/spec/prp_spec.rb +86 -0
- data/spec/resolution_spec.rb +46 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/touchpass_client_spec.rb +319 -0
- data/touchpass.gemspec +27 -0
- metadata +146 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module Touchpass
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path("../../templates", __FILE__)
|
9
|
+
|
10
|
+
desc "Install Touchpass files (migrations, initializers, locales)."
|
11
|
+
|
12
|
+
def create_migration_file
|
13
|
+
# no db config for relying partiues
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_initializer_files
|
17
|
+
copy_file "config/initializers/devise.rb", "config/initializers/devise.rb"
|
18
|
+
copy_file "config/initializers/touchpass.rb", "config/initializers/touchpass.rb"
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy_locale_files
|
22
|
+
copy_file "config/locales/devise.en.yml", "config/locales/devise.en.yml"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def self.next_migration_number(dirname)
|
27
|
+
if ActiveRecord::Base.timestamped_migrations
|
28
|
+
Time.new.utc.strftime("%Y%m%d%H%M%S")
|
29
|
+
else
|
30
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,431 @@
|
|
1
|
+
# Geodica Touchpass
|
2
|
+
# (C) Copyright 2009-2012 Geodica, a Carpadium Pty Ltd Venture
|
3
|
+
# All rights reserved
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'thor'
|
7
|
+
require 'httparty'
|
8
|
+
require 'openssl'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'base64'
|
11
|
+
require 'pp'
|
12
|
+
require 'cgi'
|
13
|
+
require 'json'
|
14
|
+
|
15
|
+
module Touchpass
|
16
|
+
|
17
|
+
# Provide a simple Ruby interface to a Touchpass verification server
|
18
|
+
class Client
|
19
|
+
|
20
|
+
attr_accessor :hostname, :debug, :api_key
|
21
|
+
|
22
|
+
API_KEY_HEADER = "X-TouchPass-ApiKey"
|
23
|
+
ENVIRONMENT = ENV['RAILS_ENV']
|
24
|
+
|
25
|
+
## Host and port to use for service invocations, default to the main Touchpass server
|
26
|
+
DFT_HOSTNAME = ENVIRONMENT == 'production' ? "https://touchpass.geodica.com" : "https://localhost:3000"
|
27
|
+
|
28
|
+
# Default response output to json
|
29
|
+
DFT_OUTPUT = "json"
|
30
|
+
|
31
|
+
# Default debug state (on: dev|test, off: prod)
|
32
|
+
DFT_DEBUG = ENV['TPC_DEBUG'] ? true : false
|
33
|
+
|
34
|
+
# Create a new Touchpass client instance and default the hostname and output
|
35
|
+
def initialize(hostname=DFT_HOSTNAME, debug=DFT_DEBUG)
|
36
|
+
@hostname = hostname.nil? ? DFT_HOSTNAME : hostname
|
37
|
+
@debug = debug
|
38
|
+
@output = DFT_OUTPUT # always use json internally
|
39
|
+
end
|
40
|
+
|
41
|
+
# Register a new relying party with the Touchpass server
|
42
|
+
# needs: :email, :username, :password
|
43
|
+
def register_party(params)
|
44
|
+
http_options = standard_http_options(params, %W{email username password})
|
45
|
+
response = submit_request("post", "#{@hostname}/parties.#{@output}", http_options)
|
46
|
+
set_api_key_from_response(response)
|
47
|
+
response
|
48
|
+
end
|
49
|
+
|
50
|
+
# Authenticate a party to obtain the API key
|
51
|
+
# needs: :login, :password
|
52
|
+
def authenticate_party(params)
|
53
|
+
http_options = standard_http_options(params, %W{login password})
|
54
|
+
|
55
|
+
response = submit_request(
|
56
|
+
"post",
|
57
|
+
"#{@hostname}/parties/authenticate.#{@output}",
|
58
|
+
http_options)
|
59
|
+
set_api_key_from_response(response)
|
60
|
+
response
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get details for a party
|
64
|
+
# needs: :api_key; uses: :id OR :username
|
65
|
+
def get_party(params)
|
66
|
+
http_options = standard_http_options(params)
|
67
|
+
|
68
|
+
if !params[:username].nil?
|
69
|
+
response = submit_request("get", "#{@hostname}/#{params[:username]}.#{@output}", http_options)
|
70
|
+
elsif !params[:id].nil?
|
71
|
+
response = submit_request("get", "#{@hostname}/parties/#{params[:id]}.#{@output}", http_options)
|
72
|
+
else
|
73
|
+
response = format_response({"error" => "Either username or id must be provided."})
|
74
|
+
end
|
75
|
+
response
|
76
|
+
end
|
77
|
+
|
78
|
+
# Update details for a party
|
79
|
+
# needs: :api_key; uses: :username, :id, :email, :first_name, :last_name
|
80
|
+
def update_party(params)
|
81
|
+
id = params[:id]
|
82
|
+
username = params[:username]
|
83
|
+
http_options = standard_http_options(params, %W{username email first_name last_name})
|
84
|
+
|
85
|
+
if !username.nil?
|
86
|
+
response = submit_request("put", "#{@hostname}/#{username}.#{@output}", http_options)
|
87
|
+
elsif !id.nil?
|
88
|
+
response = submit_request("put", "#{@hostname}/parties/#{id}.#{@output}", http_options)
|
89
|
+
else
|
90
|
+
response = format_response({"error" => "Either username or id must be provided."})
|
91
|
+
end
|
92
|
+
response
|
93
|
+
end
|
94
|
+
|
95
|
+
# Register a new device for a party
|
96
|
+
# needs: :api_key, :username, :udid, :devicename; uses: :messaging_type, :messaging_value
|
97
|
+
def register_device(params)
|
98
|
+
|
99
|
+
# Generate private & public key for this device
|
100
|
+
crypt = KeyFileCreator.new(params[:udid])
|
101
|
+
crypt.generate_keys
|
102
|
+
|
103
|
+
# Collect attributes of new device
|
104
|
+
device_attributes = params.merge({
|
105
|
+
:udid => params[:udid],
|
106
|
+
:name => params[:name],
|
107
|
+
:pub_key => crypt.public_key,
|
108
|
+
:messaging_type => params[:messaging_type],
|
109
|
+
:messaging_value => params[:messaging_value],
|
110
|
+
})
|
111
|
+
http_options = standard_http_options(device_attributes, %W{udid name pub_key messaging_type messaging_value})
|
112
|
+
|
113
|
+
# Provision the new device with the Touchpass server
|
114
|
+
new_device = submit_request("post", "#{@hostname}/#{params[:username]}/devices.#{@output}", http_options)
|
115
|
+
|
116
|
+
# We made the key files for the new device earlier (see
|
117
|
+
# KeyFileCreator above) but at that stage we didn't know the
|
118
|
+
# :deviceid of the newly provisioned device. So now we need to
|
119
|
+
# move the cert directory around based on the :deviceid returned
|
120
|
+
# from TouchPass.
|
121
|
+
begin
|
122
|
+
devices = submit_request("post", "#{@hostname}/#{params[:username]}/devices.json", http_options)
|
123
|
+
raise devices["error"] if devices["error"]
|
124
|
+
raise "unknown device id" unless devices["id"]
|
125
|
+
device_id = devices["id"].to_s
|
126
|
+
|
127
|
+
orig_file = crypt.keys_path
|
128
|
+
crypt.id = device_id
|
129
|
+
new_file = crypt.keys_path
|
130
|
+
puts "Renaming directory #{orig_file} to #{new_file}" if @debug
|
131
|
+
result = File.rename(orig_file, new_file)
|
132
|
+
rescue Exception => e
|
133
|
+
puts "Exception: #{e.message}"
|
134
|
+
puts "error: updating key file location based on device id: udid:#{params[:udid]}, device_id:#{device_id}"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Return details of the newly provisioned device
|
138
|
+
new_device
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get all devices for a party
|
142
|
+
# needs: :username, :api_key
|
143
|
+
def get_devices(params)
|
144
|
+
# Just return the device list for the specified user
|
145
|
+
http_options = standard_http_options(params)
|
146
|
+
devices = submit_request("get", "#{@hostname}/#{params[:username]}/devices.#{@output}", http_options)
|
147
|
+
|
148
|
+
# Look for errors, and just build an empty device array hashed from 'devices'
|
149
|
+
if !devices['devices'].nil? or !devices['error'].nil?
|
150
|
+
devices
|
151
|
+
else
|
152
|
+
{ 'devices' => [] }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Get details for a specific device, returns an 'errors' hash if the device cannot be found
|
157
|
+
# needs: :username, :api_key, :id
|
158
|
+
def get_device(params)
|
159
|
+
# Return the device for the specified user and device id
|
160
|
+
http_options = standard_http_options(params)
|
161
|
+
submit_request("get", "#{@hostname}/#{params[:username]}/devices/#{params[:id]}.#{@output}", http_options)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Update details of a device, returns an 'errors' hash if the device cannot be found
|
165
|
+
# needs: :username, :id, api_key; uses: :name, :messaging_type, :messaging_value, :update_pub_key
|
166
|
+
def update_device(params)
|
167
|
+
update_pub_key = params[:update_pub_key] # TODO: Regenerate and update pub_key
|
168
|
+
|
169
|
+
http_options = standard_http_options(params, %W{name messaging_type messaging_value})
|
170
|
+
|
171
|
+
submit_request("put", "#{@hostname}/#{params[:username]}/devices/#{params[:id]}.#{@output}", http_options)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Remove device, returns an 'errors' hash if the device cannot be found
|
175
|
+
# needs: :username, :id, :api_key
|
176
|
+
def remove_device(params)
|
177
|
+
http_options = standard_http_options(params)
|
178
|
+
submit_request("delete", "#{@hostname}/#{params[:username]}/devices/#{params[:id]}.#{@output}", http_options)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Create a new verification
|
182
|
+
# needs: :to_party, :api_key; uses: :message, :address (for LV), :resolution (for LV)
|
183
|
+
def create_verification(params)
|
184
|
+
http_options = standard_http_options(params, %W{to_party})
|
185
|
+
message = params[:message]
|
186
|
+
address = params[:address]
|
187
|
+
|
188
|
+
# Get the list of verifying party devices so we can encrypt data for them using each device's public key
|
189
|
+
response = submit_request("get", "#{@hostname}/#{params[:to_party]}/devices.json",
|
190
|
+
standard_http_options(params))
|
191
|
+
vp_devices = response["devices"]
|
192
|
+
|
193
|
+
# Generate token
|
194
|
+
token = Crypt.salt
|
195
|
+
hashed_token = Crypt.encrypt(token)
|
196
|
+
http_options[:body][:claimed_token] = hashed_token
|
197
|
+
|
198
|
+
if !vp_devices.nil? and (vp_devices.is_a? Array and !vp_devices.empty?)
|
199
|
+
|
200
|
+
# Encrypt message using each verifying party's (to_party) device public keys
|
201
|
+
if !message.nil?
|
202
|
+
vp_devices.each_with_index do |device, index|
|
203
|
+
device_id = device["id"]
|
204
|
+
pub_key_str = device["pub_key"]
|
205
|
+
public_key = Crypt.read_key(pub_key_str)
|
206
|
+
crypted_message = Base64.encode64(public_key.public_encrypt(message))
|
207
|
+
http_options[:body]["crypted_messages[#{index}][device_id]"] = device_id
|
208
|
+
http_options[:body]["crypted_messages[#{index}][value]"] = crypted_message
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Encrypt token using each verifying party's (to_party) device public keys
|
213
|
+
crypted_tokens = []
|
214
|
+
vp_devices.each_with_index do |device, index|
|
215
|
+
device_id = device["id"]
|
216
|
+
pub_key_str = device["pub_key"]
|
217
|
+
public_key = Crypt.read_key(pub_key_str)
|
218
|
+
crypted_token = Base64.encode64(public_key.public_encrypt(token)) # per-transaction token generated by Crypt.salt above
|
219
|
+
http_options[:body]["crypted_tokens[#{index}][device_id]"] = device_id
|
220
|
+
http_options[:body]["crypted_tokens[#{index}][value]"] = crypted_token
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Encrypt prp-hash using verifying_party (to_party) devices
|
225
|
+
if !address.nil? # For location verification
|
226
|
+
http_options[:body][:location_verification] = true
|
227
|
+
|
228
|
+
# Convert address to PRP
|
229
|
+
resolution = options[:resolution] || "LOCAL" # Resolution used to calculate PRP
|
230
|
+
http_options[:body][:resolution] = resolution
|
231
|
+
prp = Proximity::PRP.new(:address => params[:address], :resolution => resolution)
|
232
|
+
# puts prp.print_bbox # for debugging
|
233
|
+
|
234
|
+
# Generate random salt
|
235
|
+
salt = Crypt.salt
|
236
|
+
# puts "Using salt: #{salt}" # for debugging
|
237
|
+
|
238
|
+
# Encrypt PRP using salt
|
239
|
+
claimed_prp = prp.encrypt(salt)
|
240
|
+
http_options[:body][:claimed_prp] = claimed_prp
|
241
|
+
|
242
|
+
# Encrypt salt using verifiying_party (to_party) devices
|
243
|
+
crypted_salts = []
|
244
|
+
vp_devices.each_with_index do |device, index|
|
245
|
+
device_id = device["id"]
|
246
|
+
pub_key_str = device["pub_key"]
|
247
|
+
public_key = Crypt.read_key(pub_key_str)
|
248
|
+
crypted_salt = Base64.encode64(public_key.public_encrypt(salt))
|
249
|
+
http_options[:body]["crypted_salts[#{index}][device_id]"] = device_id
|
250
|
+
http_options[:body]["crypted_salts[#{index}][value]"] = crypted_salt
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
submit_request("post", "#{@hostname}/verifications.#{@output}", http_options)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Get a page of verifications for a particular party
|
258
|
+
# needs: :api_key; uses: :username, :party_id
|
259
|
+
def get_verifications(params)
|
260
|
+
http_options = standard_http_options(params)
|
261
|
+
response = nil
|
262
|
+
if !params[:username].nil?
|
263
|
+
response = submit_request("get", "#{@hostname}/#{params[:username]}/verifications.#{@output}", http_options)
|
264
|
+
elsif !params[:party_id].nil?
|
265
|
+
http_options[:body][:party_id] = params[:party_id]
|
266
|
+
response = submit_request("get", "#{@hostname}/verifications.#{@output}", http_options)
|
267
|
+
else
|
268
|
+
response = {"error" => "Either username or id must be provided."}
|
269
|
+
end
|
270
|
+
response
|
271
|
+
end
|
272
|
+
|
273
|
+
# Get details of a specific verification, will include decrypted message if :device_id is provided
|
274
|
+
# needs: :api_key, :id; uses: :device_id
|
275
|
+
def get_verification(params)
|
276
|
+
http_options = standard_http_options(params)
|
277
|
+
response = submit_request("get", "#{@hostname}/verifications/#{params[:id]}.json", http_options)
|
278
|
+
|
279
|
+
# TODO: as this stands, it will not decrypt the message unless a :device_id is provided. However,
|
280
|
+
# there is currently no way to pass in the keys to use - it will just try to use the local keys
|
281
|
+
# stored in #{local_key_path}/:device_id. So, we need to fix this up at some point.
|
282
|
+
decrypted_message = nil
|
283
|
+
if response.has_key?("crypted_messages") and !response["crypted_messages"].empty?
|
284
|
+
if !params[:device_id].nil?
|
285
|
+
decrypted_message = decrypt_salt(response["crypted_messages"], params[:device_id], self.api_key)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
# return verification plus decrypted message (if available)
|
289
|
+
response[:decrypted_message] = decrypted_message
|
290
|
+
response
|
291
|
+
end
|
292
|
+
|
293
|
+
# Update Verification
|
294
|
+
# needs: :api_key, :id, :device_id; uses: :address
|
295
|
+
def update_verification(params)
|
296
|
+
# Call show verification to get the crypted salts and resolution for the verification
|
297
|
+
# Note: crypted salts and resolution are also returned in the list verifications response
|
298
|
+
http_options = standard_http_options(params)
|
299
|
+
|
300
|
+
verification = submit_request("get", "#{@hostname}/verifications/#{params[:id]}.json", http_options)
|
301
|
+
crypted_tokens = verification["crypted_tokens"]
|
302
|
+
crypted_salts = verification["crypted_salts"]
|
303
|
+
resolution = verification["resolution"]
|
304
|
+
|
305
|
+
token = decrypt_salt(crypted_tokens, params[:device_id], self.api_key)
|
306
|
+
hashed_token = Crypt.encrypt(token)
|
307
|
+
|
308
|
+
http_options = standard_http_options(params, %W{id})
|
309
|
+
http_options[:body][:verified_token] = hashed_token
|
310
|
+
|
311
|
+
# Generate verified PRP for location verification
|
312
|
+
if !params[:address].nil?
|
313
|
+
prp = Proximity::PRP.new(:address => params[:address], :resolution => resolution)
|
314
|
+
# puts prp.print_bbox # for debugging
|
315
|
+
|
316
|
+
salt = decrypt_salt(crypted_salts, params[:device_id], self.api_key)
|
317
|
+
# puts "Using salt: #{salt}"
|
318
|
+
verified_prp = prp.encrypt(salt)
|
319
|
+
|
320
|
+
http_options[:body][:verified_prp] = verified_prp
|
321
|
+
# puts "Verified PRP: #{verified_prp}"
|
322
|
+
end
|
323
|
+
submit_request("put", "#{@hostname}/verifications/#{params[:id]}.#{@output}",
|
324
|
+
http_options)
|
325
|
+
end
|
326
|
+
|
327
|
+
# Cancel an existing verification
|
328
|
+
# needs: :api_key, :id
|
329
|
+
def cancel_verification(params)
|
330
|
+
submit_request("put", "#{@hostname}/verifications/#{params[:id]}/cancel.#{@output}",
|
331
|
+
standard_http_options(params))
|
332
|
+
end
|
333
|
+
|
334
|
+
# Reject a verification
|
335
|
+
# needs: :api_keu, :id
|
336
|
+
def reject_verification(params)
|
337
|
+
submit_request("put", "#{@hostname}/verifications/#{params[:id]}/reject.#{@output}",
|
338
|
+
standard_http_options(params))
|
339
|
+
end
|
340
|
+
|
341
|
+
private
|
342
|
+
|
343
|
+
# Decrypt salt for a specific device and api_key
|
344
|
+
def decrypt_salt(crypted_salts, device_id, api_key)
|
345
|
+
return nil if crypted_salts.nil?
|
346
|
+
|
347
|
+
# Find crypted salt associated with the device
|
348
|
+
crypted_salt = crypted_salts.detect{|cs| cs["device_id"] == device_id.to_i}
|
349
|
+
return nil if crypted_salt.nil?
|
350
|
+
device_id = crypted_salt["device_id"].to_s
|
351
|
+
ct = crypted_salt["value"]
|
352
|
+
|
353
|
+
# Load the private key
|
354
|
+
crypt = KeyFileCreator.new(device_id)
|
355
|
+
private_key = crypt.private_key()
|
356
|
+
|
357
|
+
# Decrypt the crypted salt
|
358
|
+
salt = private_key.private_decrypt(Base64.decode64(ct))
|
359
|
+
return salt
|
360
|
+
end
|
361
|
+
|
362
|
+
# Returns true if the repsonse contains errors
|
363
|
+
def errors?(response)
|
364
|
+
response.nil? || response['error'] || response['errors']
|
365
|
+
end
|
366
|
+
|
367
|
+
def error_message(response)
|
368
|
+
msg = ""
|
369
|
+
if response['error']
|
370
|
+
msg = response['error']
|
371
|
+
elsif response['errors'].kind_of(Array)
|
372
|
+
# TODO
|
373
|
+
msg = response['errors'].inspect
|
374
|
+
end
|
375
|
+
msg
|
376
|
+
end
|
377
|
+
|
378
|
+
def standard_http_options(params, body_params = [])
|
379
|
+
http_options = { :body => {}, :headers => api_key_header(params) }
|
380
|
+
|
381
|
+
body_params.each do |param_name|
|
382
|
+
param_name = param_name.to_sym
|
383
|
+
next if param_name == :api_key # this is now sent as a header field
|
384
|
+
http_options[:body][param_name] = params[param_name] unless params[param_name].nil?
|
385
|
+
end
|
386
|
+
|
387
|
+
http_options
|
388
|
+
end
|
389
|
+
|
390
|
+
def set_api_key_from_response(response)
|
391
|
+
self.api_key = response["api_key"] if response && response["api_key"]
|
392
|
+
end
|
393
|
+
|
394
|
+
def api_key_header(params)
|
395
|
+
self.api_key ? { API_KEY_HEADER => self.api_key } : {}
|
396
|
+
end
|
397
|
+
|
398
|
+
# Submit the request to the server and process the response (for errors)
|
399
|
+
def submit_request(http_verb, request, params)
|
400
|
+
|
401
|
+
if @debug
|
402
|
+
puts "DEBUG: "
|
403
|
+
puts "DEBUG: hostname: #{@hostname}"
|
404
|
+
puts "DEBUG: output: #{@output}"
|
405
|
+
puts "DEBUG: request: #{http_verb.upcase} #{request}"
|
406
|
+
puts "DEBUG: params: #{params}"
|
407
|
+
end
|
408
|
+
|
409
|
+
case http_verb
|
410
|
+
when "put"
|
411
|
+
response = HTTParty.put(request, params)
|
412
|
+
when "delete"
|
413
|
+
response = HTTParty.delete(request, params)
|
414
|
+
when "post"
|
415
|
+
response = HTTParty.post(request, params)
|
416
|
+
else
|
417
|
+
response = HTTParty.get(request, :query => params[:body],
|
418
|
+
:headers => params[:headers])
|
419
|
+
end
|
420
|
+
|
421
|
+
if @debug
|
422
|
+
puts "DEBUG: response: #{response}"
|
423
|
+
puts "DEBUG: "
|
424
|
+
end
|
425
|
+
|
426
|
+
response.to_hash
|
427
|
+
end
|
428
|
+
|
429
|
+
end # class Touchpass::Client
|
430
|
+
|
431
|
+
end # module Touchpass
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
module Touchpass
|
3
|
+
class Crypt
|
4
|
+
# Encryption method
|
5
|
+
# Options: :md5, :sha1, :sha256, :sha512
|
6
|
+
CRYPTO_PROVIDER = :sha256
|
7
|
+
|
8
|
+
def self.salt(length=10)
|
9
|
+
seeds = ('a'..'z').to_a
|
10
|
+
seeds.concat( ('A'..'Z').to_a )
|
11
|
+
seeds.concat( (0..9).to_a )
|
12
|
+
seeds.concat ['/', '.']
|
13
|
+
seeds.compact!
|
14
|
+
|
15
|
+
salt_string = '$1$'
|
16
|
+
length.times { salt_string << seeds[ rand(seeds.size) ].to_s }
|
17
|
+
|
18
|
+
salt_string
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.encrypt(string)
|
22
|
+
crypto_provider = nil
|
23
|
+
case CRYPTO_PROVIDER
|
24
|
+
when :md5
|
25
|
+
require 'digest/md5' unless defined?(Digest::MD5)
|
26
|
+
crypto_provider = Digest::MD5
|
27
|
+
when :sha1
|
28
|
+
require 'digest/sha1' unless defined?(Digest::SHA1)
|
29
|
+
crypto_provider = Digest::SHA1
|
30
|
+
when :sha256
|
31
|
+
require 'digest/sha2' unless defined?(Digest::SHA2)
|
32
|
+
crypto_provider = Digest::SHA256
|
33
|
+
when :sha512
|
34
|
+
require 'digest/sha2' unless defined?(Digest::SHA2)
|
35
|
+
crypto_provider = Digest::SHA512
|
36
|
+
end
|
37
|
+
return crypto_provider.hexdigest(string)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.read_key(pem)
|
41
|
+
begin
|
42
|
+
return OpenSSL::PKey::RSA.new(pem)
|
43
|
+
rescue
|
44
|
+
puts "Unable to read key."
|
45
|
+
end
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Touchpass
|
2
|
+
module Rp
|
3
|
+
class Device
|
4
|
+
include HTTParty
|
5
|
+
#debug_output
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.class.base_uri Touchpass.base_uri
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_all(user)
|
12
|
+
http_options = { :body => {}, :headers => { Touchpass::Client::API_KEY_HEADER => Touchpass.api_key } }
|
13
|
+
response = self.class.get("/#{user}/devices.json", http_options)
|
14
|
+
return response.parsed_response["devices"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Touchpass
|
2
|
+
|
3
|
+
# Used to help with creation of public / private key files for a device (by clients)
|
4
|
+
class KeyFileCreator
|
5
|
+
|
6
|
+
DEFAULT_KEYS_PATH = File.join(ENV['HOME'], ".touchpass", "certs")
|
7
|
+
PRIVATE_KEY = "private.pem"
|
8
|
+
PUBLIC_KEY = "public.pem"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :keys_path
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :public_key, :private_key, :id
|
15
|
+
|
16
|
+
def initialize(id)
|
17
|
+
@id = id.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
# Use openssl command to generate key pair.
|
21
|
+
# RSA public key PEM generated by Ruby’s OpenSSL::PKey::RSA are unreadable by OpenSSL.
|
22
|
+
# See: http://barelyenough.org/blog/2008/04/fun-with-public-keys/
|
23
|
+
def generate_keys
|
24
|
+
directory = keys_path
|
25
|
+
prepare_directories directory
|
26
|
+
`openssl genrsa -out #{directory}/#{PRIVATE_KEY} 1024 > /dev/null 2>&1`
|
27
|
+
`openssl rsa -in #{directory}/#{PRIVATE_KEY} -out #{directory}/#{PUBLIC_KEY} -outform PEM -pubout > /dev/null 2>&1`
|
28
|
+
end
|
29
|
+
|
30
|
+
def private_key
|
31
|
+
pem_file = File.join(keys_path, PRIVATE_KEY)
|
32
|
+
return self.read_key(pem_file)
|
33
|
+
end
|
34
|
+
|
35
|
+
def public_key
|
36
|
+
pem_file = File.join(keys_path, PUBLIC_KEY)
|
37
|
+
return self.read_key(pem_file)
|
38
|
+
end
|
39
|
+
|
40
|
+
def read_key(pem_file)
|
41
|
+
return OpenSSL::PKey::RSA.new(File.read(pem_file))
|
42
|
+
end
|
43
|
+
|
44
|
+
def keys_path
|
45
|
+
File.join(self.class.keys_path, @id)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
# Create target directory
|
50
|
+
def prepare_directories(dir)
|
51
|
+
FileUtils.makedirs dir
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Touchpass::KeyFileCreator.keys_path = Touchpass::KeyFileCreator::DEFAULT_KEYS_PATH
|