tidas 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5cc58b3207e47f086cc6b329923dfa6828076edc
4
+ data.tar.gz: 16df291cedf64e1216b0b5d9a3dccce10885cb65
5
+ SHA512:
6
+ metadata.gz: 1ba75c78c179a113be7f04144c85ae6434d2a635b7ead5bb5acfad18d9df47194888d2176517e3dc65db62504c7c7b752504ee004c335ff88adaada3c2ba5ee9
7
+ data.tar.gz: efaad392817cb09f7782e50516cc2fd22ab9ca8450535a3bbe2a09d9409e1d0dcd3d64987720926a320d24d91dbfd19210fb655b6e7bce595631245858ed7b62
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle
2
+ /.yardoc/
3
+ /Gemfile.lock
4
+ /doc/
5
+ /pkg/
6
+ /vendor/cache/*.gem
7
+ *.pem
8
+ *.pub
9
+ *.gem
10
+ /coverage/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "Tidas Documentation" --protected
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2015-08-28
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'json'
6
+
7
+ group :test do
8
+ gem 'simplecov'
9
+ end
10
+
11
+ group :development do
12
+ gem 'kramdown'
13
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Trail of Bits, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # tidas-ruby
2
+ middleware for tidas integrations
3
+
4
+ [![Code Climate](https://codeclimate.com/github/tidas/tidas-middleware-ruby/badges/gpa.svg)](https://codeclimate.com/github/tidas/tidas-middleware-ruby)
5
+ [![Test Coverage](https://codeclimate.com/github/tidas/tidas-middleware-ruby/badges/coverage.svg)](https://codeclimate.com/github/tidas/tidas-middleware-ruby/coverage)
6
+
7
+ ## Configuration
8
+
9
+ Tidas is configured either with preset ENVs, or with a configuration method explained in the API below
10
+
11
+ Configuration Params:
12
+
13
+ * **server**: `<Tidas::Configuration.server>` The tidas production endpoint. You must set this to `https://app.passwordlessapps.com`
14
+ * **api_key**: `<Tidas::Configuration.api_key>` The API key you use to authenticate your requests to our backend
15
+ * **application**: `<Tidas::Configuration.application>` The application identifier you use to silo your users within our database
16
+ * **timeout**: `<Tidas::Configuration.timeout>` If our default of 20s is too long a timeout for you, optionally set a shorter one for your implementation
17
+
18
+ ***
19
+
20
+ ## API v0.1
21
+
22
+ #### Check Tidas availability ####
23
+
24
+ This call is used to check the availability of the Tidas servers.
25
+
26
+ **API Call:** `Tidas.Ping`
27
+
28
+ Parameters:
29
+
30
+ * **None**
31
+
32
+ Returns:
33
+ * **Success**: `<Tidas::SuccessfulResult>` SuccessfulResult object containing "Pong!"
34
+ * **Bad Request**: `<Tidas::ErrorResult>` ErrorResult explaining what happened
35
+ * **Timeout**: `<Tidas::TimeoutError>` Timeout Error Object
36
+ * **50x**: `<Tidas::ServerError>` Server Error Object
37
+
38
+ ***
39
+
40
+ #### Configure Tidas ####
41
+
42
+ This section is used to set needed ENVs for Tidas to operate.
43
+
44
+ **API Call:** `Tidas::Configure`
45
+
46
+ Parameters:
47
+
48
+ * **server**: `<String>` The tidas production endpoint. You must set this to `https://app.passwordlessapps.com`
49
+ * **api_key**: `<String>` The API key you use to authenticate your requests to our backend
50
+ * **application**: `<String>` The application identifier you use to silo your users within our database
51
+ * **timeout**: `<String>` If our default of 20s is too long a timeout for you, optionally set a shorter one for your implementation
52
+
53
+ Returns:
54
+
55
+ * **Success**: nil
56
+
57
+ Example:
58
+
59
+ ```
60
+ Tidas::Configuration.configure(
61
+ server: 'https://app.passwordlessapps.com',
62
+ api_key: 'c8bbe73e1d6d0ba28b439599091140b1',
63
+ application: 'Javelin',
64
+ timeout: 1
65
+ )
66
+ ```
67
+
68
+ ***
69
+
70
+ #### Enroll Identity ####
71
+
72
+ This call is used to store a public key in our database. You can optionally supply a `tidas_id` if you want identity lookups to use your id conventions. Additionally, you can send an `overwrite` option to update an existing ID with a new identity object.
73
+
74
+ **API Call:** `Tidas::Identity.enroll`
75
+
76
+ Parameters:
77
+
78
+ * **data**: `<Tidas::EnrollmentData>`enrollment blob from client device
79
+ * **options/tidas_id**: `<String>` id to store key info with (optional: if not included, we will return one to you)
80
+ * **options/overwrite**: `<Boolean>` supply `true` to allow overwriting existing public key info with this request
81
+
82
+ Returns:
83
+
84
+ * **Success**: `<Tidas::SuccessfulResult>` Contains tidas_id for you to store/ use to look up the saved identity
85
+ * **Failure**: `<Tidas::ErrorResult>` Contains errors raised when attempting to create or update the `Tidas::Identity`
86
+
87
+ Examples:
88
+
89
+ `Tidas::Identity.enroll(data:<Tidas::EnrollmentData>)`
90
+
91
+ ```
92
+ Tidas::Identity.enroll(
93
+ data:<Tidas::EnrollmentData>,
94
+ options: {
95
+ tidas_id: "e_23423",
96
+ overwrite: true
97
+ }
98
+ )
99
+ ```
100
+
101
+ ***
102
+
103
+ #### Validate Data####
104
+
105
+ This call is used to validate data using a provided hash, signature, and a stored public key from our database. The call works by looking up a user with the provided tidas_id, then validating that the data provided was signed with that users' public key. Upon successful validation, the data is made available in the `SuccessfulResult` object.
106
+
107
+ **API Call:** `Tidas::Identity.validate`
108
+
109
+ Parameters:
110
+
111
+ * **data**: `<Tidas::ValidationData>`validation blob from client device
112
+ * **tidas_id**: `<String>` id to validate data against a specific user
113
+
114
+ Returns:
115
+
116
+ * **Success**: `<Tidas::SuccessfulResult>` Contains tidas_id and the data which was just validated, for you to use on your backend
117
+ * **Failure**: `<Tidas::ErrorResult>` Contains errors raised when attempting to create or validate the data
118
+
119
+ Examples:
120
+
121
+ `Tidas::Identity.validate(data:<Tidas::ValidationData>, tidas_id:"e_23423")`
122
+
123
+ ***
124
+
125
+ #### Deactivate User####
126
+
127
+ This call is used to deactivate users which you no longer wish to validate data from. Users are not deleted, but made inactive in case they decide to use the application again
128
+
129
+ **API Call:** `Tidas::Identity.deactivate`
130
+
131
+ Parameters:
132
+
133
+ * **tidas_id**: `<String>` id of user you wish to deactivate
134
+
135
+ Returns:
136
+
137
+ * **Success**: `<Tidas::SuccessfulResult>` Contains tidas_id and a message confirming deactivation
138
+ * **Failure**: `<Tidas::ErrorResult>` Contains errors raised when attempting to deactivate the user
139
+
140
+ Examples:
141
+
142
+ `Tidas::Identity.deactivate(tidas_id:'24jdpoifj24')`
143
+
144
+ ***
145
+
146
+ #### Activate User####
147
+
148
+ This call is used to activate users which you previously deactivated, and want to begin validating data from again.
149
+
150
+ **API Call:** `Tidas::Identity.activate`
151
+
152
+ Parameters:
153
+
154
+ * **tidas_id**: `<String>` id of user you wish to activate
155
+
156
+ Returns:
157
+
158
+ * **Success**: `<Tidas::SuccessfulResult>` Contains tidas_id and a message confirming activation
159
+ * **Failure**: `<Tidas::ErrorResult>` Contains errors raised when attempting to activate the user
160
+
161
+ Examples:
162
+
163
+ `Tidas::Identity.activate(tidas_id:'24jdpoifj24')`
164
+
165
+ ***
166
+
167
+ #### List Users####
168
+
169
+ This call is used to list users of your application, both active and deactivated. public keys are trimmed for brevity, but the accompanying `get(<tidas_id>)` method will return this info if you ask for a specific user's information
170
+
171
+ **API Call:** `Tidas::Identity.index`
172
+
173
+ Parameters:
174
+
175
+ * **None**
176
+
177
+ Returns:
178
+
179
+ * **Success**: `Array[<Tidas::Identity>]` An array of tidas identity objects
180
+ * **Failure**: `<Tidas::ErrorResult>` Contains errors raised when attempting to list users
181
+
182
+ Examples:
183
+
184
+ `Tidas::Identity.index`
185
+
186
+ ***
187
+
188
+ #### Get User Info####
189
+
190
+ This call is used to retrieve information about a specific user, including their public_key
191
+
192
+ **API Call:** `Tidas::Identity.get`
193
+
194
+ Parameters:
195
+
196
+ * **tidas_id**: `<String>` id of user you wish to get information for
197
+
198
+ Returns:
199
+
200
+ * **Success**: `<Tidas::Identity>` Contains tidas identity object with the requested user's info
201
+ * **Not Found**: `Array[]` Empty Array
202
+ * **Failure**: `<Tidas::ErrorResult>` Contains errors raised when attempting to find the user
203
+
204
+ Examples:
205
+
206
+ `Tidas::Identity.get('24jdpoifj24')`
207
+
208
+
209
+ ***
210
+
211
+ ## Tidas Error Types
212
+
213
+ List of Errors which may be encountered when using the tidas
214
+
215
+ ### Application Errors (these are normal)
216
+ * **Generic App Errors**: `<Tidas::ErrorResult>` ErrorResult with a contextual message from our server
217
+ * **Timeout**: `<Tidas::TimeoutError>` Timeout Error Object
218
+ * **Connection**: `<Tidas::ConnectionError>` Error when server could not be reached
219
+ * **50x**: `<Tidas::ServerError>` Server Error Object
220
+ * **Configuration**: `<Tidas::ConfigurationError>` This error is not wrapped, and an exception will raise explaining that your tidas instance is not configured correctly
221
+
222
+ ### Gem Errors
223
+ * **Uncaught Gem Exception**: `<Tidas::RuntimeError>` Tidas-specific RuntimeError which shouldn't happen! Report these to us please
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError => e
8
+ abort e.message
9
+ end
10
+
11
+ require 'rake'
12
+
13
+
14
+ require 'rubygems/tasks'
15
+ Gem::Tasks.new
16
+
17
+ require 'rspec/core/rake_task'
18
+ RSpec::Core::RakeTask.new
19
+
20
+ task :test => :spec
21
+ task :default => :spec
22
+
23
+ require 'yard'
24
+ YARD::Rake::YardocTask.new
25
+ task :doc => :yard
@@ -0,0 +1,139 @@
1
+ require 'tidas/error_result'
2
+ require 'tidas/exceptions'
3
+
4
+ require 'faraday'
5
+
6
+ module Tidas
7
+ module Client
8
+
9
+ private
10
+
11
+ def self.client
12
+ unless server && api_key && application
13
+ raise(ConfigurationError,"Tidas not configured: see readme for help")
14
+ end
15
+ @client ||= Faraday.new(url:server)
16
+ end
17
+
18
+ def self.server
19
+ Configuration.server
20
+ end
21
+
22
+ def self.api_key
23
+ Configuration.api_key
24
+ end
25
+
26
+ def self.application
27
+ Configuration.application
28
+ end
29
+
30
+ def self.timeout
31
+ Configuration.timeout.to_i
32
+ end
33
+
34
+ def self.ping
35
+ wrapped_get('ping')
36
+ end
37
+
38
+ def self.index_identities
39
+ wrapped_get("identities/index")
40
+ end
41
+
42
+ def self.get_identity(id)
43
+ wrapped_get("identities/index", {tidas_id: id})
44
+ end
45
+
46
+ def self.enroll(data, id, overwrite)
47
+ body = {}
48
+ body[:enrollment_data] = data
49
+ body[:tidas_id] = id if !id.to_s.empty?
50
+ body[:overwrite] = true if overwrite
51
+
52
+ wrapped_post('identities/enroll', body)
53
+ end
54
+
55
+ def self.validate(data, id = nil)
56
+
57
+ begin
58
+ tidas_data = Utilities::Unpacker.init_with_blob(data)
59
+ rescue StandardError, NoMethodError
60
+ raise(ParameterError,"Invalid data for request")
61
+ end
62
+
63
+ body = {}
64
+ body[:validation_data] = tidas_data.to_s
65
+ body[:tidas_id] = id if !id.to_s.empty?
66
+ wrapped_post('identities/validate', body, tidas_data.parse[:data_to_sign])
67
+ end
68
+
69
+ def self.deactivate(id)
70
+ body = {tidas_id: id}
71
+ wrapped_post('identities/deactivate', body)
72
+ end
73
+
74
+ def self.activate(id)
75
+ body = {tidas_id: id}
76
+ wrapped_post('identities/activate', body)
77
+ end
78
+
79
+ def self.wrapped_get(url, extra_params = nil)
80
+ begin
81
+ resp = get(url, extra_params)
82
+ rescue Faraday::TimeoutError
83
+ return TimeoutError
84
+ rescue Faraday::ConnectionFailed
85
+ return ConnectionError
86
+ end
87
+ return Utilities::ResponsePackager.package_response(resp)
88
+ end
89
+
90
+ def self.wrapped_post(url, body, data = nil)
91
+ begin
92
+ resp = post(url,body)
93
+ if data
94
+ return Utilities::ResponsePackager.package_response(resp, data)
95
+ else
96
+ return Utilities::ResponsePackager.package_response(resp)
97
+ end
98
+ rescue Faraday::TimeoutError
99
+ return TimeoutError
100
+ rescue Faraday::ConnectionFailed
101
+ return ConnectionError
102
+ end
103
+ end
104
+
105
+ def self.get(url, extra_params = nil)
106
+ resp = client.get do |req|
107
+ req.url url
108
+ req.params['api_key'] = api_key
109
+ req.params['application'] = application
110
+
111
+ if extra_params != nil
112
+ extra_params.each do |param, value|
113
+ req.params[param] = value
114
+ end
115
+ end
116
+
117
+ req.options[:timeout] = timeout
118
+ end
119
+
120
+ resp
121
+ end
122
+
123
+ def self.post(url, body)
124
+ resp = client.post do |req|
125
+ req.url url
126
+ req.headers['Content-Type'] = 'application/json'
127
+ req.params['api_key'] = api_key
128
+ req.options[:timeout] = timeout
129
+
130
+ body[:application] = application
131
+
132
+ req.body = body.to_json
133
+ end
134
+
135
+ resp
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,67 @@
1
+ module Tidas
2
+ module Configuration
3
+
4
+ attr_reader :server, :api_key, :application, :timeout
5
+
6
+ def self.test_configure #:nodoc#
7
+ Configuration.configure(
8
+ server: 'http://localhost:3000',
9
+ api_key: 'test-api-key',
10
+ application: 'Javelin',
11
+ timeout: 1
12
+ )
13
+ end
14
+
15
+ def self.configure(attributes={})
16
+ server = attributes.fetch(:server,ENV['tidas_server'])
17
+ api_key = attributes.fetch(:api_key,ENV['tidas_api_key'])
18
+ application = attributes.fetch(:application,ENV['tidas_application'])
19
+ timeout = attributes.fetch(:timeout,ENV['tidas_timeout'])
20
+
21
+ if server
22
+ @server = server
23
+ end
24
+
25
+ if api_key
26
+ @api_key = api_key
27
+ end
28
+
29
+ if application
30
+ @application = application
31
+ end
32
+
33
+ if timeout
34
+ @timeout = timeout.to_s
35
+ end
36
+ end
37
+
38
+ def self.clear_configuration(flag)
39
+ case flag
40
+ when :server then @server = nil
41
+ when :api_key then @api_key = nil
42
+ when :application then @application = nil
43
+ when :timeout then @timeout = nil
44
+ end
45
+ end
46
+
47
+ def self.server
48
+ @server
49
+ end
50
+
51
+ def self.api_key
52
+ @api_key
53
+ end
54
+
55
+ def self.application
56
+ @application
57
+ end
58
+
59
+ def self.timeout
60
+ if timeout = @timeout
61
+ timeout.to_i
62
+ else
63
+ 20
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,39 @@
1
+ module Tidas
2
+
3
+ class ErrorResult
4
+
5
+ attr_reader :tidas_id, :errors, :message
6
+
7
+ def initialize(attributes = {}) # :nodoc:
8
+ @tidas_id = attributes[:tidas_id]
9
+ @errors = Array(attributes[:errors])
10
+ end
11
+
12
+ def success?
13
+ false
14
+ end
15
+
16
+ def msg
17
+ @errors.first
18
+ end
19
+
20
+ def to_json
21
+ {
22
+ error_result: {
23
+ tidas_id: @tidas_id,
24
+ errors: @errors.map(&:to_s)
25
+ }
26
+ }.to_json
27
+ end
28
+
29
+ end
30
+
31
+ ConnectionError = ErrorResult.new(errors: "Could not connect to server")
32
+
33
+ TimeoutError = ErrorResult.new(errors: "Request Timeout")
34
+
35
+ class ServerError < ErrorResult
36
+ end
37
+
38
+ InvalidResponse = ServerError.new(errors: "Invalid response received from server - we're on it!")
39
+ end
@@ -0,0 +1,13 @@
1
+ module Tidas
2
+ ###################
3
+ # Wrap Exceptions #
4
+ ###################
5
+ class TidasError < StandardError
6
+ end
7
+
8
+ class ParameterError < TidasError
9
+ end
10
+
11
+ class ConfigurationError < TidasError
12
+ end
13
+ end
@@ -0,0 +1,100 @@
1
+ require 'tidas/exceptions'
2
+
3
+ module Tidas
4
+ class Identity
5
+
6
+ attr_reader :id, :deactivated, :app_name, :public_key
7
+
8
+ def self.index
9
+ resp = Client.index_identities
10
+ process_identity_response(resp)
11
+ end
12
+
13
+ def self.get(id)
14
+ resp = Client.get_identity(id)
15
+ process_identity_response(resp)
16
+ end
17
+
18
+ def self.process_identity_response(resp)
19
+ if resp.success?
20
+ request_identities(resp)
21
+ else
22
+ resp
23
+ end
24
+ end
25
+
26
+ def self.request_identities(resp)
27
+ identities_data = JSON.parse(resp.data, symbolize_names: true)
28
+ identities_array = identities_data[:identities]
29
+ identities_array.map! do |identity_hash|
30
+ Identity.new(
31
+ id:identity_hash[:id],
32
+ deactivated: identity_hash[:deactivated],
33
+ app_name: identity_hash[:app],
34
+ public_key: identity_hash[:public_key]
35
+ )
36
+ end
37
+
38
+ if identities_array.length == 1
39
+ out = identities_array[0]
40
+ else
41
+ out = identities_array
42
+ end
43
+
44
+ SuccessfulResult.new(message:"Search successful", data:out)
45
+ end
46
+
47
+ def self.enroll(attributes)
48
+ data = attributes[:data]
49
+ if attributes[:options] != nil
50
+ tidas_id = attributes[:options][:tidas_id]
51
+ overwrite = attributes[:options][:overwrite]
52
+ end
53
+ if overwrite && overwrite != true
54
+ raise(ParameterError,"Overwrite may only be called as an enrollment option if the value is set to true")
55
+ end
56
+
57
+ Client.enroll(data, tidas_id, overwrite)
58
+ end
59
+
60
+ def self.validate(attributes)
61
+ data = attributes[:data]
62
+ tidas_id = attributes[:tidas_id]
63
+ Client.validate(data, tidas_id)
64
+ end
65
+
66
+ def self.deactivate(attributes)
67
+ tidas_id = attributes[:tidas_id]
68
+ Client.deactivate(tidas_id)
69
+ end
70
+
71
+ def self.activate(attributes)
72
+ tidas_id = attributes[:tidas_id]
73
+ Client.activate(tidas_id)
74
+ end
75
+
76
+ def to_hash_with_key
77
+ hash = to_hash
78
+ hash[:public_key] = public_key
79
+ hash
80
+ end
81
+
82
+ def to_hash
83
+ {
84
+ id: id,
85
+ deactivated: deactivated,
86
+ app: app_name
87
+ }
88
+ end
89
+
90
+ private
91
+
92
+ def initialize(attributes)
93
+ @id = attributes[:id]
94
+ @deactivated = attributes[:deactivated]
95
+ @app_name = attributes[:app_name]
96
+ @public_key = attributes[:public_key] || "<Filtered>"
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,17 @@
1
+ module Tidas
2
+ class SuccessfulResult
3
+
4
+ attr_reader :tidas_id, :data, :message
5
+
6
+ def initialize(attributes = {}) # :nodoc:
7
+ @tidas_id = attributes[:tidas_id]
8
+ @data = attributes[:data]
9
+ @message = attributes[:message]
10
+ end
11
+
12
+ def success?
13
+ true
14
+ end
15
+
16
+ end
17
+ end