latch-sdk 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6059a598c347a268972aec50301bac778e0a208a
4
+ data.tar.gz: 8f5d3b7711b4181234f56ea723d44b60ddad42d7
5
+ SHA512:
6
+ metadata.gz: 4af6bdcdbd00ddfac2571c7e1aec97989e3211c820443515a7c009bf7f52fa1658c0c09a42a85da79ba3774ff2d17fa927d4d61be8c7fcc6797cfba446abe3f0
7
+ data.tar.gz: 2220962f2845d2b9819bfe93fde77af8bb9ac367c39c3e2055a8766cc4da79e639408519e299c9dc6ccbfcdeafd62d3f0e7a7d4864c3cbecafe8227408444edf
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'latch-sdk'
6
+ s.version = '0.0.1'
7
+ s.platform = Gem::Platform::RUBY
8
+ s.summary = 'Latch SDK'
9
+ s.email = 'crresse@gmail.com'
10
+ # s.homepage = ''
11
+ s.description = s.summary
12
+ s.authors = ['Carlos Rodriguez']
13
+ s.license = 'MIT'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # s.add_development_dependency('rake', '>= 0.9')
21
+ # s.add_development_dependency('rdoc', '>= 3')
22
+ s.add_development_dependency('rails', '>= 4.0')
23
+ # s.add_development_dependency('sqlite3')
24
+ # s.add_development_dependency('rspec-rails')
25
+ end
@@ -0,0 +1,6 @@
1
+ require 'latch/latch'
2
+ require 'latch/latch_response'
3
+ require 'latch/error'
4
+
5
+ module Latch
6
+ end
@@ -0,0 +1,45 @@
1
+ # Latch Ruby SDK - Set of reusable classes to allow developers integrate Latch on their applications.
2
+ # Copyright (C) 2013 Eleven Paths
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Latch
19
+ class Error
20
+ attr_reader :code
21
+ attr_reader :message
22
+
23
+ # @param string json a Json representation of an error with "code" and "message" elements
24
+ def initialize(json)
25
+ if (json.is_a?(String))
26
+ json = JSON.Parse(json)
27
+ end
28
+
29
+ if(json.has_key?("code") && json.has_key?("message"))
30
+ @code = json["code"]
31
+ @message = json["message"]
32
+ else
33
+ puts "Error creating error object from string " + json
34
+ end
35
+ end
36
+
37
+ # JSON representing the Error Object
38
+ def to_json
39
+ error = {}
40
+ error["code"] = @code
41
+ error["message"] = @message
42
+ error.to_json
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,292 @@
1
+ # Latch Ruby SDK - Set of reusable classes to allow developers integrate Latch on their applications.
2
+ # Copyright (C) 2013 Eleven Paths
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Latch
19
+ class Latch
20
+ attr_accessor :api_host
21
+ API_HOST = 'https://latch.elevenpaths.com'
22
+ API_VERSION = '0.9'
23
+ API_CHECK_STATUS_URL = "/api/#{API_VERSION}/status"
24
+ API_PAIR_URL = "/api/#{API_VERSION}/pair"
25
+ API_PAIR_WITH_ID_URL = "/api/#{API_VERSION}/pairWithId"
26
+ API_UNPAIR_URL = "/api/#{API_VERSION}/unpair"
27
+ API_LOCK_URL = "/api/#{API_VERSION}/lock"
28
+ API_UNLOCK_URL = "/api/#{API_VERSION}/unlock"
29
+ API_HISTORY_URL = "/api/#{API_VERSION}/history"
30
+ API_OPERATIONS_URL = "/api/#{API_VERSION}/operation"
31
+
32
+ AUTHORIZATION_HEADER_NAME = 'Authorization'
33
+ DATE_HEADER_NAME = 'X-11Paths-Date'
34
+ AUTHORIZATION_METHOD = '11PATHS'
35
+ AUTHORIZATION_HEADER_FIELD_SEPARATOR = ' '
36
+
37
+ HMAC_ALGORITHM = 'sha1'
38
+
39
+ X_11PATHS_HEADER_PREFIX = 'X-11Paths-'
40
+ X_11PATHS_HEADER_SEPARATOR = ':'
41
+
42
+ # The custom header consists of three parts, the method, the appId and the signature.
43
+ # This method returns the specified part if it exists.
44
+ # @param $part The zero indexed part to be returned
45
+ # @param $header The HTTP header value from which to extract the part
46
+ # @return string the specified part from the header or an empty string if not existent
47
+ def getPartFromHeader(part, header)
48
+ if (header.empty?)
49
+ parts = header.split(AUTHORIZATION_HEADER_FIELD_SEPARATOR)
50
+ if(parts.length > part)
51
+ return parts[part]
52
+ end
53
+ end
54
+ return ""
55
+ end
56
+
57
+ # @param $authorizationHeader Authorization HTTP Header
58
+ # @return string the Authorization method. Typical values are "Basic", "Digest" or "11PATHS"
59
+ def getAuthMethodFromHeader(authorizationHeader)
60
+ getPartFromHeader(0, authorizationHeader)
61
+ end
62
+
63
+ # @param $authorizationHeader Authorization HTTP Header
64
+ # @return string the requesting application Id. Identifies the application using the API
65
+ def getAppIdFromHeader(authorizationHeader)
66
+ getPartFromHeader(1, authorizationHeader)
67
+ end
68
+
69
+
70
+ # @param $authorizationHeader Authorization HTTP Header
71
+ # @return string the signature of the current request. Verifies the identity of the application using the API
72
+ def getSignatureFromHeader(authorizationHeader)
73
+ getPartFromHeader(2, authorizationHeader)
74
+ end
75
+
76
+
77
+ # Create an instance of the class with the Application ID and secret obtained from Eleven Paths
78
+ # @param $appId
79
+ # @param $secretKey
80
+ def initialize(appid, secret)
81
+ @appid = appid
82
+ @secret = secret
83
+ @api_host = API_HOST
84
+ end
85
+
86
+ def http(method, url, headers, params=nil)
87
+ uri = URI.parse(url)
88
+ http = Net::HTTP.new(uri.host, uri.port)
89
+
90
+ if (uri.default_port == 443)
91
+ http.use_ssl = true
92
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
93
+ end
94
+
95
+ if(method == "GET")
96
+ request = Net::HTTP::Get.new(uri.request_uri)
97
+ elsif (method == 'POST')
98
+ request = Net::HTTP::Post.new(uri.request_uri)
99
+ request.set_form_data(params)
100
+ elsif (method == "PUT")
101
+ request = Net::HTTP::Put.new(uri.request_uri)
102
+ request.set_form_data(params)
103
+ elsif (method == "DELETE")
104
+ request = Net::HTTP::Delete.new(uri.request_uri)
105
+ end
106
+
107
+ headers.map do |key,value|
108
+ request[key] = value
109
+ end
110
+
111
+ response = http.request(request)
112
+ response.body
113
+ end
114
+
115
+
116
+ def http_get_proxy(url)
117
+ LatchResponse.new(http("GET", api_host + url, authenticationHeaders('GET', url, nil)))
118
+ end
119
+
120
+ def http_post_proxy(url, params)
121
+ LatchResponse.new(http("POST", api_host + url, authenticationHeaders('POST', url, nil, nil, params), params))
122
+ end
123
+
124
+ def http_put_proxy(url, params)
125
+ LatchResponse.new(http("PUT", api_host + url, authenticationHeaders('PUT', url, nil, nil, params), params))
126
+ end
127
+
128
+ def http_delete_proxy(url)
129
+ LatchResponse.new(http("DELETE", api_host + url, authenticationHeaders('DELETE', url, nil)))
130
+ end
131
+
132
+ def pairWithId(accountId)
133
+ http_get_proxy(API_PAIR_WITH_ID_URL + '/' + accountId)
134
+ end
135
+
136
+
137
+ def pair(token)
138
+ http_get_proxy(API_PAIR_URL + '/' + token)
139
+ end
140
+
141
+
142
+ def status(accountId)
143
+ http_get_proxy(API_CHECK_STATUS_URL + '/' + accountId)
144
+ end
145
+
146
+
147
+ def operationStatus(accountId, operationId)
148
+ http_get_proxy(API_CHECK_STATUS_URL + "/" + accountId + '/op/' + operationId)
149
+ end
150
+
151
+
152
+ def unpair(accountId)
153
+ http_get_proxy(API_UNPAIR_URL + '/' + accountId)
154
+ end
155
+
156
+ def lock(accountId, operationId=nil)
157
+ if (operationId == nil)
158
+ http_post_proxy(API_LOCK_URL + '/' + accountId, {})
159
+ else
160
+ http_post_proxy(API_LOCK_URL + '/' + accountId + '/op/' + operationId, {})
161
+ end
162
+ end
163
+
164
+ def unlock(accountId, operationId=nil)
165
+ if (operationId == nil)
166
+ http_post_proxy(API_UNLOCK_URL + '/' + accountId, {})
167
+ else
168
+ http_post_proxy(API_UNLOCK_URL + '/' + accountId + '/op/' + operationId, {})
169
+ end
170
+ end
171
+
172
+ def history (accountId, from='0', to=nil)
173
+ if (to == nil)
174
+ to = Time.now.to_i*1000
175
+ end
176
+ http_get_proxy(API_HISTORY_URL + '/' + accountId + '/' + from + '/' + to.to_s)
177
+ end
178
+
179
+ def createOperation(parentId, name, twoFactor, lockOnRequest)
180
+ params = { 'parentId' => parentId, 'name' => name, 'two_factor'=>twoFactor, 'lock_on_request'=>lockOnRequest}
181
+ http_put_proxy(API_OPERATIONS_URL, params)
182
+ end
183
+
184
+ def updateOperation(operationId, name, twoFactor, lockOnRequest)
185
+ params = { 'name' => name, 'two_factor'=>twoFactor, 'lock_on_request'=>lockOnRequest}
186
+ http_post_proxy(API_OPERATIONS_URL + '/' + operationId, params)
187
+ end
188
+
189
+ def deleteOperation(operationId)
190
+ http_delete_proxy(API_OPERATIONS_URL + '/' + operationId)
191
+ end
192
+
193
+ def getOperations(operationId=nil)
194
+ if (operationId == nil)
195
+ http_get_proxy(API_OPERATIONS_URL)
196
+ else
197
+ http_get_proxy(API_OPERATIONS_URL + '/' + operationId)
198
+ end
199
+ end
200
+
201
+ # @param $data the string to sign
202
+ # @return string base64 encoding of the HMAC-SHA1 hash of the data parameter using {@code secretKey} as cipher key.
203
+ def signData(data)
204
+ Base64.encode64(OpenSSL::HMAC.digest(HMAC_ALGORITHM, @secret, data))
205
+ end
206
+
207
+
208
+ # Calculate the authentication headers to be sent with a request to the API
209
+ # @param $HTTPMethod the HTTP Method, currently only GET is supported
210
+ # @param $queryString the urlencoded string including the path (from the first forward slash) and the parameters
211
+ # @param $xHeaders HTTP headers specific to the 11-paths API. null if not needed.
212
+ # @param $utc the Universal Coordinated Time for the Date HTTP header
213
+ # @return array a map with the Authorization and Date headers needed to sign a Latch API request
214
+ def authenticationHeaders(httpMethod, queryString, xHeaders=nil, utc=nil, params=nil)
215
+ if (utc == nil)
216
+ utc = getCurrentUTC
217
+ end
218
+
219
+ stringToSign = (httpMethod.upcase).strip + "\n" +
220
+ utc.to_s + "\n" +
221
+ getSerializedHeaders(xHeaders) + "\n" +
222
+ queryString.strip
223
+
224
+ if (params != nil && params.size > 0)
225
+ serializedParams = getSerializedParams(params)
226
+ if (serializedParams != nil && serializedParams.size > 0)
227
+ stringToSign = stringToSign.strip + "\n" + serializedParams
228
+ end
229
+ end
230
+
231
+ authorizationHeader = AUTHORIZATION_METHOD +
232
+ AUTHORIZATION_HEADER_FIELD_SEPARATOR +
233
+ @appid +
234
+ AUTHORIZATION_HEADER_FIELD_SEPARATOR +
235
+ signData(stringToSign).chop
236
+
237
+ headers = {}
238
+ headers[AUTHORIZATION_HEADER_NAME] = authorizationHeader
239
+ headers[DATE_HEADER_NAME] = utc
240
+ return headers
241
+ end
242
+
243
+
244
+
245
+ # Prepares and returns a string ready to be signed from the 11-paths specific HTTP headers received
246
+ # @param $xHeaders a non necessarily ordered map of the HTTP headers to be ordered without duplicates.
247
+ # @return a String with the serialized headers, an empty string if no headers are passed, or null if there's a problem
248
+ # such as non 11paths specific headers
249
+ def getSerializedHeaders(xHeaders)
250
+ if(xHeaders != nil)
251
+ headers = xHeaders.inject({}) do |xHeaders, keys|
252
+ hash[keys[0].downcase] = keys[1]
253
+ hash
254
+ end
255
+
256
+
257
+ serializedHeaders = ''
258
+
259
+ headers.sort.map do |key,value|
260
+ if(key.downcase == X_11PATHS_HEADER_PREFIX.downcase)
261
+ puts "Error serializing headers. Only specific " + X_11PATHS_HEADER_PREFIX + " headers need to be singed"
262
+ return nil
263
+ end
264
+ serializedHeaders += key + X_11PATHS_HEADER_SEPARATOR + value + ' '
265
+ end
266
+ substitute = 'utf-8'
267
+ return serializedHeaders.gsub(/^[#{substitute}]+|[#{substitute}]+$/, '')
268
+ else
269
+ return ""
270
+ end
271
+ end
272
+
273
+ def getSerializedParams(parameters)
274
+ if (parameters != nil)
275
+ serializedParams = ''
276
+
277
+ parameters.sort.map do |key,value|
278
+ serializedParams += key + "=" + value + '&'
279
+ end
280
+ substitute = '&'
281
+ return serializedParams.gsub(/^[#{substitute}]+|[#{substitute}]+$/, '')
282
+ else
283
+ return ""
284
+ end
285
+ end
286
+
287
+ # @return a string representation of the current time in UTC to be used in a Date HTTP Header
288
+ def getCurrentUTC
289
+ Time.now.utc
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,56 @@
1
+ # Latch Ruby SDK - Set of reusable classes to allow developers integrate Latch on their applications.
2
+ # Copyright (C) 2013 Eleven Paths
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Latch
19
+ # This class models a response from any of the endpoints in the Latch API.
20
+ # It consists of a "data" and an "error" elements. Although normally only one of them will be
21
+ # present, they are not mutually exclusive, since errors can be non fatal, and therefore a response
22
+ # could have valid information in the data field and at the same time inform of an error.
23
+ class LatchResponse
24
+ attr_accessor :data
25
+ attr_accessor :error
26
+
27
+ # @param jsonString a json string received from one of the methods of the Latch API
28
+ def initialize(jsonString)
29
+ json = JSON.parse(jsonString)
30
+
31
+ if(json != nil)
32
+ if (json.has_key?("data"))
33
+ @data = json["data"]
34
+ end
35
+
36
+ if (json.has_key?("error"))
37
+ @error = Error.new(json["error"])
38
+ end
39
+ end
40
+ end
41
+
42
+ # Get JSON String that represents the LatchResponse object
43
+ def to_json
44
+ response = {}
45
+
46
+ if (@data != nil)
47
+ response["data"] = @data
48
+ end
49
+
50
+ if (@error != nil)
51
+ response["error"] = @error.to_json
52
+ end
53
+ response.to_json
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: latch-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Carlos Rodriguez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-10 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: '4.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ description: Latch SDK
28
+ email: crresse@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - Gemfile
34
+ - latch.gemspec
35
+ - lib/latch.rb
36
+ - lib/latch/error.rb
37
+ - lib/latch/latch.rb
38
+ - lib/latch/latch_response.rb
39
+ homepage:
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.4.5
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Latch SDK
63
+ test_files: []