respoke 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +17 -0
- data/.gitignore +7 -0
- data/.simplecov +6 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +93 -0
- data/Rakefile +32 -0
- data/lib/respoke.rb +7 -0
- data/lib/respoke/client.rb +265 -0
- data/lib/respoke/errors.rb +9 -0
- data/lib/respoke/response.rb +9 -0
- data/lib/respoke/response/session_token.rb +12 -0
- data/lib/respoke/response/session_token_id.rb +27 -0
- data/lib/respoke/role.rb +20 -0
- data/lib/respoke/version.rb +4 -0
- data/respoke.gemspec +34 -0
- data/test/spec/brokered_auth_spec.rb +89 -0
- data/test/spec/role_spec.rb +44 -0
- data/test/test_config.example.yml +6 -0
- data/test/test_config.yml.enc +0 -0
- data/test/test_helper.rb +26 -0
- data/test/unit/client_test.rb +43 -0
- data/test/unit/respoke_test.rb +7 -0
- data/test/unit/response/session_token_id_test.rb +40 -0
- data/test/unit/response/session_token_test.rb +9 -0
- data/test/vcr_cassettes/app_token_request.yml +99 -0
- data/test/vcr_cassettes/create_role.yml +51 -0
- data/test/vcr_cassettes/delete_a_role.yml +143 -0
- data/test/vcr_cassettes/find_role.yml +97 -0
- data/test/vcr_cassettes/session_token_id_request.yml +51 -0
- data/test/vcr_cassettes/session_token_request.yml +51 -0
- data/test/vcr_cassettes/update_role.yml +145 -0
- metadata +276 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Respoke
|
4
|
+
module Response
|
5
|
+
# A response object for session token requests.
|
6
|
+
#
|
7
|
+
# @attr token [String] The token for use as an App-Token.
|
8
|
+
# @attr message [String] Token request status information.
|
9
|
+
class SessionToken < Hashie::Mash
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Respoke
|
4
|
+
module Response
|
5
|
+
# A response object for token requests.
|
6
|
+
#
|
7
|
+
# @attr tokenId [String] Token ID used to request an App-Token.
|
8
|
+
# @attr appId [String] App ID App-Token is associated with.
|
9
|
+
# @attr roleId [String] Role ID App-Token is assigned.
|
10
|
+
# @attr endpointId [String] Endpoint ID App-Token is for.
|
11
|
+
# @attr ttl [Number] Number of seconds App-Token is valid for.
|
12
|
+
# @attr createdAt [DateTime] When the token request was made.
|
13
|
+
# @attr expiryTime [DateTime] When the token expires.
|
14
|
+
# @attr createTime [DateTime] When the token was created.
|
15
|
+
class SessionTokenId < Hashie::Mash
|
16
|
+
include Hashie::Extensions::Coercion
|
17
|
+
|
18
|
+
coerce_key :createdAt, ->(time) do
|
19
|
+
Time.parse(time).getutc
|
20
|
+
end
|
21
|
+
|
22
|
+
coerce_key :expiryTime, :createTime, ->(timestamp) do
|
23
|
+
Time.at(timestamp).getutc
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/respoke/role.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Respoke
|
2
|
+
class Role
|
3
|
+
attr_reader :id, :name
|
4
|
+
attr_accessor :rules
|
5
|
+
def initialize(client, id:, name:, **rules)
|
6
|
+
@client = client
|
7
|
+
@id = id
|
8
|
+
@name = name
|
9
|
+
@rules = rules
|
10
|
+
end
|
11
|
+
|
12
|
+
def save
|
13
|
+
response = @client.update_role(id: @id, rules: @rules)
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete
|
17
|
+
response = @client.delete_role(id: @id)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/respoke.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'respoke/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'respoke'
|
8
|
+
spec.version = Respoke::VERSION
|
9
|
+
spec.date = Time.now.strftime('%Y-%m-%d')
|
10
|
+
spec.authors = ['Matthew Turney']
|
11
|
+
spec.email = ['mturney@respoke.io']
|
12
|
+
spec.summary = %q{Wrapper library for the Respoke API.}
|
13
|
+
spec.homepage = 'https://respoke.io'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_dependency 'faraday', '~> 0.9'
|
21
|
+
spec.add_dependency 'faraday_middleware', '~> 0.9'
|
22
|
+
spec.add_dependency 'hashie', '~> 3.3.2'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'minitest'
|
27
|
+
spec.add_development_dependency 'minitest-reporters'
|
28
|
+
spec.add_development_dependency 'yard'
|
29
|
+
spec.add_development_dependency 'simplecov'
|
30
|
+
spec.add_development_dependency 'ruby-lint'
|
31
|
+
spec.add_development_dependency 'vcr'
|
32
|
+
spec.add_development_dependency 'webmock'
|
33
|
+
spec.add_development_dependency 'timecop'
|
34
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'Brokered authentication' do
|
4
|
+
subject { client }
|
5
|
+
|
6
|
+
describe 'when using App-Secret' do
|
7
|
+
let :client do
|
8
|
+
Respoke::Client.new(app_secret: TestConfig.app_secret)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'requests token ID' do
|
12
|
+
response = nil
|
13
|
+
|
14
|
+
VCR.use_cassette 'session_token_id_request' do
|
15
|
+
response = subject.request_session_token_id(
|
16
|
+
appId: TestConfig.app_id,
|
17
|
+
endpointId: 'foo-bar',
|
18
|
+
roleId: TestConfig.role_id,
|
19
|
+
ttl: 60
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
assert response.tokenId?
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'and already given a session token ID' do
|
27
|
+
let :token_id do
|
28
|
+
token_id = ''
|
29
|
+
|
30
|
+
VCR.use_cassette 'session_token_id_request' do
|
31
|
+
token_id = subject.request_session_token_id(
|
32
|
+
appId: TestConfig.app_id,
|
33
|
+
endpointId: 'foo-bar',
|
34
|
+
roleId: TestConfig.role_id,
|
35
|
+
ttl: 60 # this is short since it's only needed in the test
|
36
|
+
).tokenId
|
37
|
+
end
|
38
|
+
|
39
|
+
token_id
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'requests App-Token using session token ID' do
|
43
|
+
response = nil
|
44
|
+
|
45
|
+
VCR.use_cassette 'session_token_request' do
|
46
|
+
response = subject.request_session_token(
|
47
|
+
appId: TestConfig.app_id,
|
48
|
+
tokenId: token_id,
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
assert response.token?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'requests an App-Token in one step' do
|
57
|
+
token = ''
|
58
|
+
|
59
|
+
VCR.use_cassette 'app_token_request' do
|
60
|
+
token = subject.app_token(
|
61
|
+
appId: TestConfig.app_id,
|
62
|
+
endpointId: 'foo-bar',
|
63
|
+
roleId: TestConfig.role_id,
|
64
|
+
ttl: 60
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
assert_instance_of String, token
|
69
|
+
refute_equal '', token
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'when App-Token has already been set' do
|
73
|
+
before do
|
74
|
+
VCR.use_cassette 'app_token_request' do
|
75
|
+
@expected_app_token = subject.app_token(
|
76
|
+
appId: TestConfig.app_id,
|
77
|
+
endpointId: 'foo-bar',
|
78
|
+
roleId: TestConfig.role_id,
|
79
|
+
ttl: 60
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'returns that token' do
|
85
|
+
assert_equal @expected_app_token, subject.app_token
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
|
5
|
+
describe "Roles" do
|
6
|
+
let :client do
|
7
|
+
Respoke::Client.new(app_secret: TestConfig.app_secret)
|
8
|
+
end
|
9
|
+
|
10
|
+
let :existing_role do
|
11
|
+
client.create_role(name: SecureRandom::uuid(), rules: {})
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates a role" do
|
15
|
+
VCR.use_cassette 'create_role' do
|
16
|
+
role_name = SecureRandom::uuid()
|
17
|
+
role = client.create_role(name: role_name, rules: { mediaRelay: false })
|
18
|
+
assert !role.id.nil?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "finds a role" do
|
23
|
+
VCR.use_cassette 'find_role' do
|
24
|
+
response = client.find_role(id: existing_role.id)
|
25
|
+
assert (response.name == existing_role.name) && (existing_role.id == existing_role.id)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "updates a role" do
|
30
|
+
VCR.use_cassette 'update_role' do
|
31
|
+
assert existing_role.rules[:mediaRelay] == false
|
32
|
+
existing_role.rules[:mediaRelay]=true
|
33
|
+
existing_role.save
|
34
|
+
assert (client.find_role(id: existing_role.id).rules[:mediaRelay] == true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "deletes a role" do
|
39
|
+
VCR.use_cassette 'delete_a_role' do
|
40
|
+
existing_role.delete
|
41
|
+
assert client.find_role(id: existing_role.id).nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
require 'hashie'
|
4
|
+
require 'vcr'
|
5
|
+
|
6
|
+
require 'minitest/autorun'
|
7
|
+
require 'minitest/reporters'
|
8
|
+
|
9
|
+
require 'simplecov' if ENV["COVERAGE"]
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
12
|
+
require 'respoke'
|
13
|
+
|
14
|
+
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
15
|
+
|
16
|
+
TestConfig = Hashie::Mash.load(File.expand_path('test_config.yml', __dir__))
|
17
|
+
|
18
|
+
VCR.configure do |config|
|
19
|
+
# Due to a bug in VCR faraday middleware we must use webmock. A fix has been
|
20
|
+
# merged (https://github.com/vcr/vcr/pull/439) but it does not have a tagged
|
21
|
+
# release yet.
|
22
|
+
config.hook_into :webmock
|
23
|
+
config.cassette_library_dir = 'test/vcr_cassettes'
|
24
|
+
config.default_cassette_options = { :record => :once }
|
25
|
+
config.filter_sensitive_data('<APP_SECRET>') { TestConfig.app_secret }
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Respoke::TestClient < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@client = Respoke::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_can_create_new_client
|
9
|
+
assert_instance_of Respoke::Client, @client
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_can_set_app_secret_on_initialization
|
13
|
+
secret = 'foo'
|
14
|
+
client = Respoke::Client.new(app_secret: secret)
|
15
|
+
|
16
|
+
assert_equal secret, client.app_secret
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_cannot_set_app_secret_by_attribute
|
20
|
+
secret = 'foo'
|
21
|
+
|
22
|
+
assert_raises NoMethodError do
|
23
|
+
@client.app_secret = secret
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_has_default_base_url
|
28
|
+
assert_equal Respoke::Client::DEFAULT_BASE_URL, @client.base_url
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_can_override_base_url
|
32
|
+
url = 'https://localhost:2000'
|
33
|
+
client = Respoke::Client.new(base_url: url)
|
34
|
+
|
35
|
+
assert_equal url, client.base_url
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_cannot_set_base_url_by_attribute
|
39
|
+
assert_raises NoMethodError do
|
40
|
+
@client.base_url = 'https://localhost:2000'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Respoke::Response::TestSessionTokenId < Minitest::Test
|
4
|
+
def setup
|
5
|
+
@klass = Respoke::Response::SessionTokenId
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_can_create_new_instance
|
9
|
+
response = @klass.new
|
10
|
+
|
11
|
+
assert_instance_of @klass, response
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_coerces_createdAt_to_utc_Time
|
15
|
+
response = @klass.new(
|
16
|
+
createdAt: '2015-01-02T21:30:57.714Z'
|
17
|
+
)
|
18
|
+
|
19
|
+
assert_instance_of Time, response.createdAt
|
20
|
+
assert response.createdAt.utc?
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_coerces_expiryTime_to_utc_Time
|
24
|
+
response = @klass.new(
|
25
|
+
expiryTime: 1420320657
|
26
|
+
)
|
27
|
+
|
28
|
+
assert_instance_of Time, response.expiryTime
|
29
|
+
assert response.expiryTime.utc?
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_coerces_createTime_to_utc_Time
|
33
|
+
response = @klass.new(
|
34
|
+
createTime: 1420320657
|
35
|
+
)
|
36
|
+
|
37
|
+
assert_instance_of Time, response.createTime
|
38
|
+
assert response.createTime.utc?
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://api.respoke.io/v1/tokens
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"appId":"0dfcfe78-c4af-497f-88f6-c6b3a8151fb3","endpointId":"foo-bar","roleId":"56BD864A-3474-4D41-A3E7-1577078B63D5","ttl":86400}'
|
9
|
+
headers:
|
10
|
+
App-Secret:
|
11
|
+
- "<APP_SECRET>"
|
12
|
+
User-Agent:
|
13
|
+
- Faraday v0.9.0
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- nginx
|
27
|
+
Date:
|
28
|
+
- Fri, 02 Jan 2015 21:30:58 GMT
|
29
|
+
Content-Type:
|
30
|
+
- application/json; charset=utf-8
|
31
|
+
Transfer-Encoding:
|
32
|
+
- chunked
|
33
|
+
Connection:
|
34
|
+
- keep-alive
|
35
|
+
Vary:
|
36
|
+
- Accept-Encoding
|
37
|
+
- Accept-Encoding
|
38
|
+
Request-Id:
|
39
|
+
- 5b39afb9-c629-434f-861e-aadc75319f4b
|
40
|
+
Ratelimit-Limit:
|
41
|
+
- '10'
|
42
|
+
Ratelimit-Time-Units:
|
43
|
+
- seconds
|
44
|
+
Ratelimit-Remaining:
|
45
|
+
- '7'
|
46
|
+
body:
|
47
|
+
encoding: UTF-8
|
48
|
+
string: '{"appId":"0dfcfe78-c4af-497f-88f6-c6b3a8151fb3","endpointId":"foo-bar","roleId":"56BD864A-3474-4D41-A3E7-1577078B63D5","accountId":"9628FFD0-54E8-402C-A979-368B03EC63D9","createTime":1420234258,"expiryTime":1420320658,"id":"E44902BA-639B-4EAD-9C88-CD846146BB19","forDevelopment":false,"createdAt":"2015-01-02T21:30:58.539Z","tokenId":"E44902BA-639B-4EAD-9C88-CD846146BB19"}'
|
49
|
+
http_version:
|
50
|
+
recorded_at: Fri, 02 Jan 2015 21:30:58 GMT
|
51
|
+
- request:
|
52
|
+
method: post
|
53
|
+
uri: https://api.respoke.io/v1/session-tokens
|
54
|
+
body:
|
55
|
+
encoding: UTF-8
|
56
|
+
string: '{"appId":"0dfcfe78-c4af-497f-88f6-c6b3a8151fb3","tokenId":"E44902BA-639B-4EAD-9C88-CD846146BB19"}'
|
57
|
+
headers:
|
58
|
+
App-Secret:
|
59
|
+
- "<APP_SECRET>"
|
60
|
+
User-Agent:
|
61
|
+
- Faraday v0.9.0
|
62
|
+
Content-Type:
|
63
|
+
- application/json
|
64
|
+
Accept-Encoding:
|
65
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
66
|
+
Accept:
|
67
|
+
- "*/*"
|
68
|
+
response:
|
69
|
+
status:
|
70
|
+
code: 200
|
71
|
+
message: OK
|
72
|
+
headers:
|
73
|
+
Server:
|
74
|
+
- nginx
|
75
|
+
Date:
|
76
|
+
- Fri, 02 Jan 2015 21:30:58 GMT
|
77
|
+
Content-Type:
|
78
|
+
- application/json; charset=utf-8
|
79
|
+
Transfer-Encoding:
|
80
|
+
- chunked
|
81
|
+
Connection:
|
82
|
+
- keep-alive
|
83
|
+
Vary:
|
84
|
+
- Accept-Encoding
|
85
|
+
- Accept-Encoding
|
86
|
+
Request-Id:
|
87
|
+
- 9a985440-76b2-4e83-9528-ddf22414e1cf
|
88
|
+
Ratelimit-Limit:
|
89
|
+
- '10'
|
90
|
+
Ratelimit-Time-Units:
|
91
|
+
- seconds
|
92
|
+
Ratelimit-Remaining:
|
93
|
+
- '6'
|
94
|
+
body:
|
95
|
+
encoding: UTF-8
|
96
|
+
string: '{"message":"Authorization successful","token":"1834AD29-F08B-43F7-91F1-52EF33814589"}'
|
97
|
+
http_version:
|
98
|
+
recorded_at: Fri, 02 Jan 2015 21:30:58 GMT
|
99
|
+
recorded_with: VCR 2.9.3
|