toopher_api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc 'Run tests'
8
+ task :default => :test
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/ruby
2
+ require_relative '../lib/toopher_api'
3
+
4
+ key = ENV['TOOPHER_CONSUMER_KEY']
5
+ secret = ENV['TOOPHER_CONSUMER_SECRET']
6
+ if key.empty? or secret.empty?
7
+ puts 'enter consumer credentials (set environment variables to prevent prompting):'
8
+ print 'TOOPHER_CONSUMER_KEY='
9
+ STDOUT.flush
10
+ key = gets
11
+ key.chomp!
12
+ print 'TOOPHER_CONSUMER_SECRET='
13
+ STDOUT.flush
14
+ secret = gets
15
+ secret.chomp!
16
+ end
17
+
18
+ toopher = ToopherAPI.new(key, secret)
19
+
20
+ puts 'STEP 1: Pair device'
21
+ puts 'enter pairing phrase:'
22
+ phrase = gets
23
+ phrase.chomp!
24
+ puts 'enter user name:'
25
+ user = gets
26
+ user.chomp!
27
+
28
+ pairing = toopher.pair(phrase, user)
29
+
30
+ while(!pairing['enabled'])
31
+ puts 'waiting for authorization...'
32
+ sleep(1)
33
+ pairing = toopher.get_pairing_status(pairing['id'])
34
+ end
35
+
36
+ puts 'paired successfully!'
37
+
38
+ puts 'STEP 2: Authenticate login'
39
+ puts 'enter terminal name:'
40
+ terminal_name = gets;
41
+ terminal_name.chomp!
42
+ puts 'enter action name, or [ENTER] for none:'
43
+ while (true)
44
+ action = gets;
45
+ action.chomp!
46
+
47
+ puts 'sending authentication request...'
48
+ auth = toopher.authenticate(pairing['id'], terminal_name, action)
49
+
50
+ while(auth['pending'])
51
+ puts 'waiting for authentication...'
52
+ sleep(1)
53
+ auth = toopher.get_authentication_status(auth['id'])
54
+ end
55
+
56
+ puts "Successfully authorized action '" + action + "'. Enter another action to authorize again, or [Ctrl-C] to exit"
57
+ end
58
+
@@ -0,0 +1,117 @@
1
+ =begin
2
+ Copyright (c) 2012 Toopher, Inc
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
21
+ =end
22
+
23
+ require 'rubygems'
24
+ require 'net/http'
25
+ require 'net/https'
26
+ require 'uri'
27
+ require 'json'
28
+ require 'oauth'
29
+
30
+ class ToopherApiError < StandardError
31
+ end
32
+
33
+ class ToopherAPI
34
+
35
+ DEFAULT_BASE_URL = 'https://toopher-api.appspot.com/v1/'
36
+
37
+ def initialize(key,secret,options={}, base_url = DEFAULT_BASE_URL)
38
+ consumer_key = key
39
+ consumer_secret = secret
40
+
41
+ consumer_key.empty? and raise ArgumentError, "Toopher consumer key cannot be empty!"
42
+ consumer_secret.empty? and raise ArgumentError, "Toopher consumer secret cannot be empty!"
43
+
44
+ @base_url = base_url
45
+ @oauth_consumer = OAuth::Consumer.new(consumer_key, consumer_secret)
46
+ @oauth_options = options
47
+ end
48
+
49
+ def pair(pairing_phrase, user_name)
50
+ return make_pair_response(post('pairings/create', {
51
+ 'pairing_phrase' => pairing_phrase,
52
+ 'user_name' => user_name
53
+ }))
54
+ end
55
+
56
+ def get_pairing_status(pairing_request_id)
57
+ return make_pair_response(get('pairings/' + pairing_request_id))
58
+ end
59
+
60
+ def authenticate(pairing_id, terminal_name, action_name = '')
61
+ parameters = {
62
+ 'pairing_id' => pairing_id,
63
+ 'terminal_name' => terminal_name
64
+ }
65
+ action_name.empty? or (parameters['action_name'] = action_name)
66
+ return make_auth_response(post('authentication_requests/initiate', parameters))
67
+ end
68
+
69
+ def get_authentication_status(authentication_request_id)
70
+ return make_auth_response(get('authentication_requests/' + authentication_request_id))
71
+ end
72
+
73
+ private
74
+ def make_pair_response(result)
75
+ return {
76
+ 'id' => result['id'],
77
+ 'enabled' => result['enabled'],
78
+ 'user_id' => result['user']['id'],
79
+ 'user_name' => result['user']['name']
80
+ }
81
+ end
82
+ def make_auth_response(result)
83
+ return {
84
+ 'id' => result['id'],
85
+ 'pending' => result['pending'],
86
+ 'granted' => result['granted'],
87
+ 'automated' => result['automated'],
88
+ 'reason' => result['reason'],
89
+ 'terminal_id' => result['terminal']['id'],
90
+ 'terminal_name' => result['terminal']['name']
91
+ }
92
+ end
93
+ def post(endpoint, parameters)
94
+ url = URI.parse(@base_url + endpoint)
95
+ req = Net::HTTP::Post.new(url.path)
96
+ req.set_form_data(parameters)
97
+ return request(url, req)
98
+ end
99
+
100
+ def get(endpoint)
101
+ url = URI.parse(@base_url + endpoint)
102
+ req = Net::HTTP::Get.new(url.path)
103
+ return request(url, req)
104
+ end
105
+
106
+ def request(url, req)
107
+ http = Net::HTTP::new(url.host, url.port)
108
+ http.use_ssl = url.port == 443
109
+ req.oauth!(http, @oauth_consumer, nil, @oauth_options)
110
+ res = http.request(req)
111
+ decoded = JSON.parse(res.body)
112
+ if(decoded.has_key?("error_code"))
113
+ raise ToopherApiError, "Error code " + decoded['error_code'].to_s + ": " + decoded['error_message']
114
+ end
115
+ return decoded
116
+ end
117
+ end
@@ -0,0 +1,138 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'webmock/test_unit'
4
+ require 'toopher_api'
5
+
6
+ class TestToopher < Test::Unit::TestCase
7
+ def test_constructor()
8
+
9
+ assert_raise ArgumentError do
10
+ api = ToopherAPI.new
11
+ end
12
+
13
+ assert_raise ArgumentError do
14
+ api = ToopherAPI.new('key')
15
+ end
16
+
17
+ assert_raise ArgumentError do
18
+ api = ToopherAPI.new('', 'secret')
19
+ end
20
+
21
+ assert_nothing_raised do
22
+ api = ToopherAPI.new('key', 'secret')
23
+ end
24
+
25
+ end
26
+
27
+ def test_create_pairing_immediate_success()
28
+ stub_http_request(:post, "https://toopher-api.appspot.com/v1/pairings/create").
29
+ with(
30
+ # :headers => {
31
+ # 'Authorization' => 'OAuth oauth_consumer_key="key",oauth_nonce="nonce",oauth_signature="%2FW9rUAFDuJTTBtfSxeQ%2FDxWpVQY%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="0",oauth_version="1.0"'
32
+ # },
33
+ :body => { 'pairing_phrase' => 'immediate_pair', 'user_name' => 'user' }
34
+ ).
35
+ to_return(
36
+ :body => '{"id":"1","enabled":true,"user":{"id":"1","name":"user"}}',
37
+ :status => 200
38
+ )
39
+
40
+ toopher = ToopherAPI.new('key', 'secret', {:nonce => 'nonce', :timestamp => '0' })
41
+ pairing = toopher.pair('immediate_pair', 'user')
42
+ assert(pairing['id'] == '1', 'bad pairing id')
43
+ assert(pairing['enabled'] == true, 'pairing not enabled')
44
+ assert(pairing['user_id'] == '1', 'bad user id')
45
+ assert(pairing['user_name'] == 'user', 'bad user name')
46
+
47
+ end
48
+ def test_get_pairing_status()
49
+ stub_http_request(:get, "https://toopher-api.appspot.com/v1/pairings/1").
50
+ to_return(
51
+ :body => '{"id":"1","enabled":true,"user":{"id":"1","name":"paired user"}}',
52
+ :status => 200
53
+ )
54
+ stub_http_request(:get, "https://toopher-api.appspot.com/v1/pairings/2").
55
+ to_return(
56
+ :body => '{"id":"2","enabled":false,"user":{"id":"2","name":"unpaired user"}}',
57
+ :status => 200
58
+ )
59
+
60
+ toopher = ToopherAPI.new('key', 'secret', {:nonce => 'nonce', :timestamp => '0' })
61
+ pairing = toopher.get_pairing_status('1')
62
+ assert(pairing['id'] == '1', 'bad pairing id')
63
+ assert(pairing['enabled'] == true, 'pairing not enabled')
64
+ assert(pairing['user_id'] == '1', 'bad user id')
65
+ assert(pairing['user_name'] == 'paired user', 'bad user name')
66
+
67
+ pairing = toopher.get_pairing_status('2')
68
+ assert(pairing['id'] == '2', 'bad pairing id')
69
+ assert(pairing['enabled'] == false, 'pairing should not be enabled')
70
+ assert(pairing['user_id'] == '2', 'bad user id')
71
+ assert(pairing['user_name'] == 'unpaired user', 'bad user name')
72
+ end
73
+
74
+ def test_create_authentication_with_no_action()
75
+ stub_http_request(:post, "https://toopher-api.appspot.com/v1/authentication_requests/initiate").
76
+ with(
77
+ :body => { 'pairing_id' => '1', 'terminal_name' => 'term name' }
78
+ ).
79
+ to_return(
80
+ :body => '{"id":"1","pending":false,"granted":true,"automated":true,"reason":"some reason","terminal":{"id":"1","name":"term name"}}',
81
+ :status => 200
82
+ )
83
+
84
+ toopher = ToopherAPI.new('key', 'secret', {:nonce => 'nonce', :timestamp => '0' })
85
+ auth = toopher.authenticate('1', 'term name')
86
+ assert(auth['id'] == '1', 'wrong auth id')
87
+ assert(auth['pending'] == false, 'wrong auth pending')
88
+ assert(auth['granted'] == true, 'wrong auth granted')
89
+ assert(auth['automated'] == true, 'wrong auth automated')
90
+ assert(auth['reason'] == 'some reason', 'wrong auth reason')
91
+ assert(auth['terminal_id'] == '1', 'wrong auth terminal id')
92
+ assert(auth['terminal_name'] == 'term name', 'wrong auth terminal name')
93
+ end
94
+
95
+ def test_get_authentication_status()
96
+ stub_http_request(:get, "https://toopher-api.appspot.com/v1/authentication_requests/1").
97
+ to_return(
98
+ :body => '{"id":"1","pending":false,"granted":true,"automated":true,"reason":"some reason","terminal":{"id":"1","name":"term name"}}',
99
+ :status => 200
100
+ )
101
+ stub_http_request(:get, "https://toopher-api.appspot.com/v1/authentication_requests/2").
102
+ to_return(
103
+ :body => '{"id":"2","pending":true,"granted":false,"automated":false,"reason":"some other reason","terminal":{"id":"2","name":"another term name"}}',
104
+ :status => 200
105
+ )
106
+
107
+ toopher = ToopherAPI.new('key', 'secret', {:nonce => 'nonce', :timestamp => '0' })
108
+ auth = toopher.get_authentication_status('1')
109
+ assert(auth['id'] == '1', 'wrong auth id')
110
+ assert(auth['pending'] == false, 'wrong auth pending')
111
+ assert(auth['granted'] == true, 'wrong auth granted')
112
+ assert(auth['automated'] == true, 'wrong auth automated')
113
+ assert(auth['reason'] == 'some reason', 'wrong auth reason')
114
+ assert(auth['terminal_id'] == '1', 'wrong auth terminal id')
115
+ assert(auth['terminal_name'] == 'term name', 'wrong auth terminal name')
116
+
117
+ auth = toopher.get_authentication_status('2')
118
+ assert(auth['id'] == '2', 'wrong auth id')
119
+ assert(auth['pending'] == true, 'wrong auth pending')
120
+ assert(auth['granted'] == false, 'wrong auth granted')
121
+ assert(auth['automated'] == false, 'wrong auth automated')
122
+ assert(auth['reason'] == 'some other reason', 'wrong auth reason')
123
+ assert(auth['terminal_id'] == '2', 'wrong auth terminal id')
124
+ assert(auth['terminal_name'] == 'another term name', 'wrong auth terminal name')
125
+ end
126
+
127
+ def test_toopher_request_error()
128
+ stub_http_request(:get, "https://toopher-api.appspot.com/v1/authentication_requests/1").
129
+ to_return(
130
+ :body => '{"error_code":401,"error_message":"Not a valid OAuth signed request"}',
131
+ :status => 401
132
+ )
133
+ toopher = ToopherAPI.new('key', 'secret', {:nonce => 'nonce', :timestamp => '0' })
134
+ assert_raise ToopherApiError do
135
+ auth = toopher.get_authentication_status('1')
136
+ end
137
+ end
138
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: toopher_api
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Toopher, Inc.
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-12-05 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: oauth
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 1
29
+ segments:
30
+ - 0
31
+ - 4
32
+ - 7
33
+ version: 0.4.7
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: json
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 1
45
+ segments:
46
+ - 1
47
+ - 7
48
+ - 5
49
+ version: 1.7.5
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: webmock
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 51
61
+ segments:
62
+ - 1
63
+ - 9
64
+ - 0
65
+ version: 1.9.0
66
+ type: :development
67
+ version_requirements: *id003
68
+ description: Synchronous interface to the toopher.com authentication api.
69
+ email: support@toopher.com
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - Rakefile
78
+ - lib/toopher_api.rb
79
+ - test/test_toopher_api.rb
80
+ - demo/toopher_demo.rb
81
+ homepage: http://toopher.org
82
+ licenses: []
83
+
84
+ post_install_message:
85
+ rdoc_options: []
86
+
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 3
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ requirements: []
108
+
109
+ rubyforge_project:
110
+ rubygems_version: 1.8.24
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: Interface to the toopher.com authentication api
114
+ test_files: []
115
+