tidas 0.1.0

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,36 @@
1
+ module Tidas
2
+ module Utilities
3
+ module ResponsePackager
4
+
5
+ def self.package_response(response, data = nil)
6
+ if [500, 404].include? response.status
7
+ return InvalidResponse
8
+ end
9
+ begin
10
+ body = JSON.parse(response.body, symbolize_names: true)
11
+ return handle_result(body, data)
12
+ rescue JSON::ParserError
13
+ return InvalidResponse
14
+ end
15
+ return InvalidResponse
16
+ end
17
+
18
+ def self.handle_result(body, data = nil)
19
+ result_type = body.keys[0]
20
+ content = body[result_type]
21
+ case result_type
22
+ when :error_result
23
+ return ErrorResult.new(tidas_id:content[:tidas_id], errors:content[:errors])
24
+ when :successful_result
25
+ if content[:returned_data] != nil
26
+ return SuccessfulResult.new(tidas_id:content[:tidas_id], data:content[:returned_data], message:content[:message])
27
+ else
28
+ return SuccessfulResult.new(tidas_id:content[:tidas_id], data:data, message:content[:message])
29
+ end
30
+ else
31
+ return InvalidResponse
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,93 @@
1
+ require 'base64'
2
+
3
+ module Tidas
4
+ module Utilities
5
+ class Unpacker
6
+
7
+ attr_accessor :parsed_data
8
+
9
+ def self.init_with_blob(blob)
10
+ Utilities::Unpacker.new( {blob: blob} )
11
+ end
12
+
13
+ def parse
14
+ pull_val_from_data(data_str)
15
+ @parsed_data
16
+ end
17
+
18
+ def data_str
19
+ Base64.decode64(@blob)
20
+ end
21
+
22
+ def to_s
23
+ @blob
24
+ end
25
+
26
+ def raw_blob_stripped_of_data
27
+ data_str.sub(data_bytes, '')
28
+ end
29
+
30
+ def blob_stripped_of_data
31
+ Base64.encode64(raw_blob_stripped_of_data)
32
+ end
33
+
34
+ private
35
+
36
+ def initialize(attributes)
37
+ @blob = attributes[:blob]
38
+ @parsed_data = {}
39
+ parse
40
+ raise StandardError, "Malformed Data Object" unless valid?
41
+ end
42
+
43
+ def pull_val_from_data(data)
44
+ return unless data[0]
45
+ type_char = data[0].unpack('C')[0]
46
+ type_str = Utilities::SERIALIZATION_FIELDS[type_char]
47
+ field_len = data[1..4].unpack('I')[0]
48
+
49
+ val_end = 5+field_len-1
50
+ raw_val = data[5..val_end]
51
+
52
+ val = extract_val(type_str, raw_val)
53
+
54
+ @parsed_data[type_str] = val
55
+
56
+ shorter_data = data[val_end+1..-1]
57
+ if shorter_data && shorter_data.length > 0
58
+ pull_val_from_data(shorter_data)
59
+ end
60
+ end
61
+
62
+ def extract_val(type, raw_val)
63
+ if type == :platform
64
+ raw_val.unpack('C')[0]
65
+ elsif type == :timestamp
66
+ time_data = raw_val.unpack('I')[0]
67
+ Time.at(time_data)
68
+ else
69
+ raw_val.unpack('C*').map{|e| e.chr }.join
70
+ end
71
+ end
72
+
73
+ def data_bytes
74
+ data_to_sign = @parsed_data[:data_to_sign] || String.new
75
+ #identifier
76
+ type_byte = [1].pack("C")
77
+ #datalen
78
+ len_bytes = [data_to_sign.length].pack("L")
79
+
80
+ type_byte + len_bytes + data_to_sign
81
+ end
82
+
83
+ def valid?
84
+ return false if @parsed_data[:platform] == nil
85
+ return false if @parsed_data[:timestamp] == nil
86
+ return false if @parsed_data[:data_hash] == nil
87
+ return false if @parsed_data[:signature] == nil
88
+ true
89
+ end
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,14 @@
1
+ module Tidas
2
+ module Utilities
3
+ SERIALIZATION_FIELDS = [
4
+ :platform,
5
+ :data_to_sign,
6
+ :timestamp,
7
+ :data_hash,
8
+ :signature,
9
+ :public_key_data
10
+ ]
11
+
12
+ TEST_DATA_STRING = %{AAEAAAAAAQUAAAB0b2tlbgIIAAAArjLCVQAAAAADFAAAAOampSqv22ZcJqGA8NycnekorHaIBEYAAAAwRAIgR318ZQZ2lZqMCtOmNZJeqaeFM4CXRK1MoV4V2nJoRlACIBQp4Aujc2Ks8t9ouJn//pVOhUFjqXhZltKBbz/GoSC9BUEAAAAELRQfUdYVN0zMrWllimOc9phemEbyqizT2NmPmnAnHrQnE+oTP0CLVFOZjDLhLdyoawcmMT6VurgDCkU9HW9zIg==}
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ module Tidas
2
+ # tidas version
3
+ VERSION = "0.1.0"
4
+ end
data/lib/tidas.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'tidas/utilities'
2
+ require 'tidas/utilities/unpacker'
3
+ require 'tidas/utilities/response_packager'
4
+
5
+ require 'tidas/version'
6
+ require 'tidas/configuration'
7
+
8
+ require 'tidas/client'
9
+ require 'tidas/error_result'
10
+ require 'tidas/identity'
11
+ require 'tidas/successful_result'
12
+
13
+ require 'json'
14
+ require 'faraday'
15
+
16
+ module Tidas
17
+ def self.ping
18
+ Client.ping
19
+ end
20
+ end
@@ -0,0 +1,198 @@
1
+ require 'spec_helper'
2
+ require 'tidas'
3
+
4
+ describe Tidas::Client do
5
+ let(:client) {Tidas::Client}
6
+ let(:response_packager) {Tidas::Utilities::ResponsePackager}
7
+
8
+ describe "client" do
9
+ before {Tidas::Configuration.test_configure}
10
+ it "should return a Faraday::Connection object" do
11
+ expect(Tidas::Client.client.class).to be == Faraday::Connection
12
+ end
13
+
14
+ it "should always return the same connection object (singleton)" do
15
+ client_instance = Tidas::Client.client
16
+ expect(client_instance.object_id).to be == Tidas::Client.client.object_id
17
+ end
18
+ end
19
+
20
+ describe "server" do
21
+ it "should retrieve the server object from Tidas::Configuration" do
22
+ expect(Tidas::Configuration).to receive(:server)
23
+ client.server
24
+ end
25
+ end
26
+
27
+ describe "api_key" do
28
+ it "should retrieve the api_key object from Tidas::Configuration" do
29
+ expect(Tidas::Configuration).to receive(:api_key)
30
+ client.api_key
31
+ end
32
+ end
33
+
34
+ describe "application" do
35
+ it "should retrieve the application object from Tidas::Configuration" do
36
+ expect(Tidas::Configuration).to receive(:application)
37
+ client.application
38
+ end
39
+ end
40
+
41
+ describe "timeout" do
42
+ it "should retrieve the timeout object from Tidas::Configuration" do
43
+ expect(Tidas::Configuration).to receive(:timeout)
44
+ client.timeout
45
+ end
46
+ end
47
+
48
+ describe "ping" do
49
+ it "should call wrapped_get('ping')" do
50
+ expect(client).to receive(:wrapped_get).with('ping')
51
+ client.ping
52
+ end
53
+ end
54
+
55
+ describe "index_identities" do
56
+ it "should call wrapped_get('identities/index')" do
57
+ expect(client).to receive(:wrapped_get).with('identities/index')
58
+ client.index_identities
59
+ end
60
+ end
61
+
62
+ describe "get_identity(id)" do
63
+ let(:tidas_id) { 20 }
64
+ it "should call wrapped_get('identities/index') with an options hash containing an id" do
65
+ expect(client).to receive(:wrapped_get).with('identities/index', {tidas_id: tidas_id})
66
+ client.get_identity(tidas_id)
67
+ end
68
+ end
69
+
70
+ describe "enroll(data, id, overwrite)" do
71
+ describe "when body is valid" do
72
+ it "should call wrapped_post('identities/enroll') with a body hash" do
73
+ body = {enrollment_data:'test_data', tidas_id:'test_id', overwrite:true}
74
+ expect(client).to receive(:wrapped_post).with('identities/enroll', body)
75
+ client.enroll(body[:enrollment_data], body[:tidas_id], body[:overwrite])
76
+ end
77
+ end
78
+
79
+ describe "when body is empty" do
80
+ it "should still call wrapped_post('identities/enroll') with a body hash" do
81
+ body = {enrollment_data:nil}
82
+ expect(client).to receive(:wrapped_post).with('identities/enroll', body)
83
+ client.enroll(body[:enrollment_data], body[:tidas_id], body[:overwrite])
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "validate(data, id)" do
89
+ describe "when data is not valid" do
90
+ it "should return a Tidas::ParameterError explaining that the data was invalid" do
91
+ expect { client.validate("test123", nil) }.to raise_error(Tidas::ParameterError, "Invalid data for request")
92
+ end
93
+ end
94
+
95
+ describe "when data is valid" do
96
+ it "should call wrapped_post('identities/validate') with a body hash and data" do
97
+ data_str = Tidas::Utilities::TEST_DATA_STRING
98
+ data_obj = Tidas::Utilities::Unpacker.init_with_blob(data_str)
99
+ body = {validation_data:data_str, tidas_id:'test_id'}
100
+ extracted_data = data_obj.parse[:data_to_sign]
101
+
102
+ expect(client).to receive(:wrapped_post).with('identities/validate', body, extracted_data)
103
+
104
+ client.validate(body[:validation_data], body[:tidas_id])
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "deactivate(id)" do
110
+ it "should call wrapped_post('identities/deactivate') with a body containing a tidas id" do
111
+ body = {tidas_id: 'test123'}
112
+ expect(client).to receive(:wrapped_post).with('identities/deactivate', body)
113
+ client.deactivate(body[:tidas_id])
114
+ end
115
+ end
116
+
117
+ describe "activate(id)" do
118
+ it "should call wrapped_post('identities/activate') with a body containing a tidas id" do
119
+ body = {tidas_id: 'test123'}
120
+ expect(client).to receive(:wrapped_post).with('identities/activate', body)
121
+ client.activate(body[:tidas_id])
122
+ end
123
+ end
124
+
125
+ describe "wrapped_get(url, extra_params = nil)" do
126
+ describe "when service is reachable, and request does not time out" do
127
+ it "should call ResponsePackager to package the server's response" do
128
+ expect(client).to receive(:get) {true}
129
+ expect(response_packager).to receive(:package_response).with(true)
130
+ client.wrapped_get('test')
131
+ end
132
+
133
+ describe "with_extra params" do
134
+ it "should pass those too" do
135
+ expect(client).to receive(:get).with('test', {test: 'test'}) {true}
136
+ expect(response_packager).to receive(:package_response).with(true)
137
+ client.wrapped_get('test', {test: 'test'})
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "when the service is reachable and the request does time out" do
143
+ it "should return a Tidas::TimeoutError object explaining the timeout" do
144
+ expect(client).to receive(:get) { raise Faraday::TimeoutError }
145
+ expect(client.wrapped_get('test')).to be == Tidas::TimeoutError
146
+ end
147
+ end
148
+
149
+ describe "when the service is unreachable and the request does time out" do
150
+ it "should return a Tidas::Connection object explaining the problem" do
151
+ expect(client.wrapped_get('test')).to be == Tidas::ConnectionError
152
+ end
153
+ end
154
+ end
155
+
156
+ describe "wrapped_post(url, body, data = nil)" do
157
+ let(:body) { {body: 'body'} }
158
+
159
+ describe "when service is reachable, and request does not time out" do
160
+ it "should call ResponsePackager to package the server's response" do
161
+ expect(client).to receive(:post).with('test', body) {true}
162
+ expect(response_packager).to receive(:package_response).with(true)
163
+ client.wrapped_post('test', body)
164
+ end
165
+
166
+ describe "with data" do
167
+ let(:data) { {data: 'data'} }
168
+ it "should pass the data along too" do
169
+ expect(client).to receive(:post).with('test', body) {true}
170
+ expect(response_packager).to receive(:package_response).with(true, data)
171
+ client.wrapped_post('test', body, data)
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "when the service is reachable and the request does time out" do
177
+ it "should return a Tidas::TimeoutError object explaining the timeout" do
178
+ expect(client).to receive(:post).with('test', body) { raise Faraday::TimeoutError }
179
+ expect(client.wrapped_post('test', body)).to be == Tidas::TimeoutError
180
+ end
181
+ end
182
+
183
+ describe "when the service is unreachable and the request does time out" do
184
+ it "should return a Tidas::Connection object explaining the problem" do
185
+ expect(client.wrapped_post('test', body)).to be == Tidas::ConnectionError
186
+ end
187
+ end
188
+ end
189
+
190
+ describe "get(url, extra_params = nil)" do
191
+
192
+ end
193
+
194
+ describe "post(url, body)" do
195
+
196
+ end
197
+
198
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+ require 'tidas'
3
+
4
+ describe Tidas::Configuration do
5
+ let(:subject) {Tidas::Configuration}
6
+
7
+ describe "configure" do
8
+ it "should set server when given one" do
9
+ subject.configure(server:'foo.bar')
10
+ expect(subject.server).to be == 'foo.bar'
11
+ end
12
+
13
+ it "should set api_key when given one" do
14
+ subject.configure(api_key:'dffa40f7a0fc98a8fdd7ffff716d6620')
15
+ expect(subject.api_key).to be == 'dffa40f7a0fc98a8fdd7ffff716d6620'
16
+ end
17
+
18
+ it "should set application name when given one" do
19
+ subject.configure(application:'Javelin')
20
+ expect(subject.application).to be == 'Javelin'
21
+ end
22
+
23
+ it "should set timeout when given one" do
24
+ subject.configure(timeout:2)
25
+ expect(subject.timeout).to be == 2
26
+ end
27
+
28
+ context "using ENV variables" do
29
+ before do
30
+ subject.clear_configuration(:server)
31
+ subject.clear_configuration(:api_key)
32
+ subject.clear_configuration(:application)
33
+ subject.clear_configuration(:timeout)
34
+
35
+ ENV['tidas_server'] = 'test_server'
36
+ ENV['tidas_api_key'] = 'test_api_key'
37
+ ENV['tidas_application'] = 'test_application'
38
+ ENV['tidas_timeout'] = '5'
39
+ end
40
+
41
+ after do
42
+ ENV['tidas_server'] = nil
43
+ ENV['tidas_api_key'] = nil
44
+ ENV['tidas_application'] = nil
45
+ ENV['tidas_timeout'] = nil
46
+ end
47
+
48
+ it "should retrieve attributes from ENV if available" do
49
+ subject.configure
50
+
51
+ expect(subject.server).to be == 'test_server'
52
+ expect(subject.api_key).to be == 'test_api_key'
53
+ expect(subject.application).to be == 'test_application'
54
+ expect(subject.timeout).to be == 5
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "server" do
60
+ before { subject.configure(server: 'test_val')}
61
+ it "should return @server'" do
62
+ expect(subject.server).to be == 'test_val'
63
+ end
64
+ end
65
+
66
+ describe "api_key" do
67
+ before { subject.configure(api_key: 'test_val')}
68
+ it "should return @api_key'" do
69
+ expect(subject.api_key).to be == 'test_val'
70
+ end
71
+ end
72
+
73
+ describe "application" do
74
+ before { subject.configure(application: 'test_val')}
75
+ it "should return @application'" do
76
+ expect(subject.application).to be == 'test_val'
77
+ end
78
+ end
79
+
80
+ describe "timeout" do
81
+ it "should return @timeout' when set" do
82
+ subject.configure(timeout: '2')
83
+ expect(subject.timeout).to be == 2
84
+ end
85
+
86
+ it "should return '20' when @timeout is not set" do
87
+ subject.clear_configuration(:timeout)
88
+ expect(subject.timeout).to be == 20
89
+ end
90
+ end
91
+
92
+ describe "clear_configuration" do
93
+ before { subject.configure(server: "test_server") }
94
+ it "should clear a named configuration @ivar" do
95
+ expect(subject.server).to be == "test_server"
96
+ subject.clear_configuration(:server)
97
+ expect(subject.server).to be == nil
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'tidas'
3
+
4
+ describe Tidas::ErrorResult do
5
+ let(:result) {Tidas::ErrorResult.new(tidas_id: "id123", errors: %w[test1 test2 test3]) }
6
+ describe "success?" do
7
+ it "should return false" do
8
+ expect(result.success?).to be == false
9
+ end
10
+ end
11
+
12
+ describe "errors" do
13
+ it "should return an array of error strings" do
14
+ expect(result.errors.class).to be == Array
15
+ expect(result.errors.length).to be == 3
16
+ expect(result.errors).to be == ["test1", "test2", "test3"]
17
+ end
18
+ end
19
+
20
+ describe "to_json" do
21
+ it "should return the error in a digestable json format" do
22
+ expect(result.to_json).to be == %{{"error_result":{"tidas_id":"id123","errors":["test1","test2","test3"]}}}
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+ require 'tidas'
3
+
4
+ describe Tidas::Identity do
5
+ let(:client) { Tidas::Client }
6
+ let(:subject) { Tidas::Identity }
7
+ let(:fake_id) { 'test123' }
8
+ let(:data) { 'data' }
9
+ let(:identity) { Tidas::Identity.new(id:fake_id, deactivated: false, app_name: 'Javelin', public_key: 'realKeyTrustMe') }
10
+
11
+ describe "index" do
12
+ it "should call Tidas::Client.index_identities and then process the response" do
13
+ expect(client).to receive(:index_identities) { true }
14
+ expect(subject).to receive(:process_identity_response).with(true)
15
+ subject.index
16
+ end
17
+ end
18
+
19
+ describe "get(id)" do
20
+ it "should call Tidas::Client.get_identity(id) and then process the response" do
21
+ expect(client).to receive(:get_identity).with(fake_id) { true }
22
+ expect(subject).to receive(:process_identity_response).with(true)
23
+ subject.get(fake_id)
24
+ end
25
+ end
26
+
27
+ describe "process_identity_response(resp)" do
28
+ describe "when response is successful" do
29
+ let(:resp) { Tidas::SuccessfulResult.new }
30
+ it "should call request_identities(resp)" do
31
+ expect(subject).to receive(:request_identities).with(resp)
32
+ subject.process_identity_response(resp)
33
+ end
34
+ end
35
+
36
+ describe "when a response is not successful" do
37
+ let(:resp) { Tidas::ErrorResult.new }
38
+ it "should call return the resp object" do
39
+ expect(subject.process_identity_response(resp).class).to be == Tidas::ErrorResult
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "request_identities(resp)" do
45
+ let(:resp) { Object.new }
46
+ describe "when resp contains one identity object" do
47
+ let(:json_input) {{identities:[identity.to_hash_with_key]}.to_json}
48
+ it "should return a Tidas::SuccessfulResult object containing that identity" do
49
+ expect(resp).to receive(:data) {json_input}
50
+ output = subject.request_identities(resp)
51
+ expect(output.class).to be == Tidas::SuccessfulResult
52
+ expect(output.data.class).to be == Tidas::Identity
53
+ end
54
+ end
55
+
56
+ describe "when resp contains multiple identity objects" do
57
+ let(:json_input) {{identities:[identity.to_hash_with_key, identity.to_hash_with_key, identity.to_hash_with_key]}.to_json}
58
+ it "should return a Tidas::SuccessfulResult object containing that identity" do
59
+ expect(resp).to receive(:data) {json_input}
60
+ output = subject.request_identities(resp)
61
+ expect(output.class).to be == Tidas::SuccessfulResult
62
+ expect(output.data.class).to be == Array
63
+ expect(output.data[0].class).to be == Tidas::Identity
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "enroll(attributes)" do
69
+ describe "when no options hash is passed" do
70
+ it "should call Tidas::Client.enroll(data, <nil>, <nil>)" do
71
+ expect(client).to receive(:enroll).with(data, nil, nil)
72
+ subject.enroll(data:data)
73
+ end
74
+ end
75
+
76
+ describe "when an options hash is passed" do
77
+ describe "when passing in a tidas_id" do
78
+ it "should call Tidas::Client.enroll(data, id, <nil>)" do
79
+ expect(client).to receive(:enroll).with(data, fake_id, nil)
80
+ subject.enroll(data:data, options:{tidas_id:fake_id})
81
+ end
82
+ end
83
+
84
+ describe "when passing in overwrite as true" do
85
+ it "should call Tidas::Client.enroll(data, nil, true)" do
86
+ expect(client).to receive(:enroll).with(data, nil, true)
87
+ subject.enroll(data:data, options:{overwrite:true})
88
+ end
89
+ end
90
+
91
+ describe "when passing in overwrite as anything else" do
92
+ it "should raise an error, explaining that you must only call the option with 'true'" do
93
+ expect{subject.enroll(data:data, options:{overwrite:'noodle'})}.to raise_error(ParameterError)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ describe "validate(attributes)" do
100
+ it "should call Tidas::Client.validate(data, tidas_id)" do
101
+ expect(client).to receive(:validate).with(data, fake_id)
102
+ subject.validate(data:data, tidas_id:fake_id)
103
+ end
104
+ end
105
+
106
+ describe "deactivate(attributes)" do
107
+ it "should call Tidas::Client.deactivate(id)" do
108
+ expect(client).to receive(:deactivate).with(fake_id)
109
+ subject.deactivate(tidas_id:fake_id)
110
+ end
111
+ end
112
+
113
+ describe "activate(attributes)" do
114
+ it "should call Tidas::Client.activate(id)" do
115
+ expect(client).to receive(:activate).with(fake_id)
116
+ subject.activate(tidas_id:fake_id)
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,7 @@
1
+ require 'rspec'
2
+ require 'tidas/version'
3
+
4
+ require 'simplecov'
5
+ SimpleCov.start
6
+
7
+ include Tidas
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'tidas'
3
+
4
+ describe Tidas::SuccessfulResult do
5
+ describe "success?" do
6
+ it "should return true" do
7
+ expect(Tidas::SuccessfulResult.new.success?).to be == true
8
+ end
9
+ end
10
+
11
+
12
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'tidas'
3
+
4
+ describe Tidas do
5
+ it "should have a VERSION constant" do
6
+ expect(subject.const_get('VERSION')).to_not be_empty
7
+ end
8
+ end