tidas 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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