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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 196657fbadbddac896125b5633e9a41ddc6ba4b5
|
4
|
+
data.tar.gz: ee0fd8872889c7868049130640092baa13f34294
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 15c8773001b7e574b8841fbb58190067f2d97f46a7b55be273ef008710d8b4210c4c2b4e323b100c4948952ec70d75dfa43cde995026e8fea51ee95cb8432926
|
7
|
+
data.tar.gz: 8380b37f0d43ffd581d77389d8f5cbb6fbe8eb89c23283e85810102013ab09197e5eb2fbc41969b2ed77e209c0b292786d0f323a607786b743c4f0feaba0c307
|
data/.editorconfig
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# EditorConfig is awesome: http://EditorConfig.org
|
2
|
+
|
3
|
+
# top-most EditorConfig file
|
4
|
+
root = true
|
5
|
+
|
6
|
+
# Unix-style newlines with a newline ending every file
|
7
|
+
[*]
|
8
|
+
end_of_line = lf
|
9
|
+
insert_final_newline = true
|
10
|
+
trim_trailing_whitespace = true
|
11
|
+
max_line_length = 80
|
12
|
+
|
13
|
+
# Set default charset
|
14
|
+
[*.{rb,gemspec}, Gemfile]
|
15
|
+
charset = utf-8
|
16
|
+
indent_style = space
|
17
|
+
indent_size = 2
|
data/.gitignore
ADDED
data/.simplecov
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
script: bundle exec rake test
|
2
|
+
rvm:
|
3
|
+
- 2.1
|
4
|
+
notifications:
|
5
|
+
email:
|
6
|
+
recipients:
|
7
|
+
- monitoring@respoke.io
|
8
|
+
email:
|
9
|
+
on_success: change
|
10
|
+
on_failure: always
|
11
|
+
cache: bundler
|
12
|
+
os:
|
13
|
+
- linux
|
14
|
+
- osx
|
15
|
+
before_install:
|
16
|
+
- openssl aes-256-cbc -K $encrypted_4e4ac21e4bd9_key -iv $encrypted_4e4ac21e4bd9_iv
|
17
|
+
-in test/test_config.yml.enc -out test/test_config.yml -d
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Digium, Inc.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/respoke.svg)](http://badge.fury.io/rb/respoke)
|
2
|
+
[![Build Status](https://travis-ci.org/pho3nixf1re/ruby-respoke.svg?branch=master)](https://travis-ci.org/pho3nixf1re/ruby-respoke)
|
3
|
+
[![Dependency Status](https://gemnasium.com/pho3nixf1re/ruby-respoke.svg)](https://gemnasium.com/pho3nixf1re/ruby-respoke)
|
4
|
+
|
5
|
+
# Respoke
|
6
|
+
|
7
|
+
ruby-respoke is a wrapper for the Respoke API. For more information on the
|
8
|
+
Respoke service and API see [docs.respoke.io](http://docs.respoke.io).
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Using bundler, add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'respoke'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
For details on the ruby-respoke API please refer to the [full documentation].
|
23
|
+
|
24
|
+
[full documentation]: http://www.rubydoc.info/github/pho3nixf1re/ruby-respoke/master
|
25
|
+
|
26
|
+
## Running the tests
|
27
|
+
|
28
|
+
The test suite uses VCR to record API requests and responses once. After the
|
29
|
+
first run it caches these and works offline. These cached files are checked into
|
30
|
+
Git and can be found in the `test/vcr_cassettes` directory. To run the tests
|
31
|
+
against the live API just delete the `test/vcr_cassettes` directory. Please note
|
32
|
+
that this will change the expected input of the encrypted
|
33
|
+
`test/test_config.yml.enc` file used by Travis and that will need to be updated
|
34
|
+
if you intend to commit the new VCR cache. Otherwise just omit the new cache
|
35
|
+
files when making your tests.
|
36
|
+
|
37
|
+
### Units
|
38
|
+
|
39
|
+
Note that the unit tests do not use the VCR cache in favor of stubbing.
|
40
|
+
|
41
|
+
```sh
|
42
|
+
rake test:unit
|
43
|
+
```
|
44
|
+
|
45
|
+
### Specs
|
46
|
+
|
47
|
+
Before you can run the specs yourself you will need to remove the VCR cache
|
48
|
+
files and provide your Respoke API credentials. Copy the `test/test_config.yml`
|
49
|
+
file and replace the example values with the ones in the Respoke
|
50
|
+
[developer portal].
|
51
|
+
|
52
|
+
[developer portal]: https://portal.respoke.io
|
53
|
+
|
54
|
+
```
|
55
|
+
cp test/test_config.example.yml test/test_config.yml
|
56
|
+
```
|
57
|
+
|
58
|
+
Fill in the test_config.yml values then run the spec rake task.
|
59
|
+
|
60
|
+
```sh
|
61
|
+
rake test:spec
|
62
|
+
```
|
63
|
+
|
64
|
+
## Building the documentation
|
65
|
+
|
66
|
+
The documentation is marked up using Yard + Markdown. The easiest way to build
|
67
|
+
the included docs is to use the rake task.
|
68
|
+
|
69
|
+
```sh
|
70
|
+
rake yard
|
71
|
+
```
|
72
|
+
|
73
|
+
## Contributing
|
74
|
+
|
75
|
+
If you wish to submit an issue use the [issue tracker].
|
76
|
+
|
77
|
+
[issue tracker]: https://github.com/pho3nixf1re/ruby-respoke/issues
|
78
|
+
|
79
|
+
1. Fork it ( https://github.com/[my-github-username]/ruby-respoke/fork )
|
80
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
81
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
82
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
83
|
+
5. Create a new Pull Request
|
84
|
+
|
85
|
+
## License
|
86
|
+
|
87
|
+
Copyright 2014, Digium, Inc.
|
88
|
+
All rights reserved.
|
89
|
+
|
90
|
+
This source code is licensed under The MIT License found in the
|
91
|
+
[LICENSE](LICENSE) file in the root directory of this source tree.
|
92
|
+
|
93
|
+
For all details and documentation: https://www.respoke.io
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
require 'yard'
|
4
|
+
require 'bundler/version'
|
5
|
+
|
6
|
+
namespace :test do
|
7
|
+
Rake::TestTask.new(:spec) do |t|
|
8
|
+
t.libs << "test"
|
9
|
+
t.test_files = FileList['test/spec/*_spec.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::TestTask.new(:unit) do |t|
|
13
|
+
t.libs << "test"
|
14
|
+
t.test_files = FileList['test/unit/**/*_test.rb']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
task :test => ['test:spec', 'test:unit']
|
19
|
+
|
20
|
+
task :default => :test
|
21
|
+
|
22
|
+
YARD::Rake::YardocTask.new do |t|
|
23
|
+
t.options = ['-m', 'markdown']
|
24
|
+
end
|
25
|
+
|
26
|
+
task :build do
|
27
|
+
system "gem build respoke.gemspec"
|
28
|
+
end
|
29
|
+
|
30
|
+
task :release => :build do
|
31
|
+
system "gem push respoke-#{Respoke::VERSION}.gem"
|
32
|
+
end
|
data/lib/respoke.rb
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
|
4
|
+
require 'respoke/errors'
|
5
|
+
require 'respoke/response'
|
6
|
+
|
7
|
+
# Contains methods to make API calls.
|
8
|
+
#
|
9
|
+
# @example One-step endpoint authentication
|
10
|
+
#
|
11
|
+
# require 'respoke'
|
12
|
+
#
|
13
|
+
# client = Respoke::Client.new(app_secret: '77269d84-d7f3-49da-8eab-bd6686160035')
|
14
|
+
# client.app_token(
|
15
|
+
# appId: '0cdf7bc1-45d1-420a-963e-c797a6f7ba61',
|
16
|
+
# roleId: '47ea573f-5a78-42f4-927c-fe658bc00f91',
|
17
|
+
# endpointId: 'foo-bar-user'
|
18
|
+
# )
|
19
|
+
# #=> '3c022dbd-0a82-4382-bd0d-5af6e11b8d67'
|
20
|
+
#
|
21
|
+
# @example Manual endpoint authentication
|
22
|
+
#
|
23
|
+
# require 'respoke'
|
24
|
+
#
|
25
|
+
# client = Respoke::Client.new(app_secret: '77269d84-d7f3-49da-8eab-bd6686160035')
|
26
|
+
#
|
27
|
+
# session_id_response = client.request_session_token_id(
|
28
|
+
# appId: '0cdf7bc1-45d1-420a-963e-c797a6f7ba61',
|
29
|
+
# roleId: '47ea573f-5a78-42f4-927c-fe658bc00f91',
|
30
|
+
# endpointId: 'foo-bar-user'
|
31
|
+
# )
|
32
|
+
# session = client.request_session_token(
|
33
|
+
# appId: session_id_response.appId,
|
34
|
+
# tokenId: session_id_response.tokenId
|
35
|
+
# )
|
36
|
+
#
|
37
|
+
# session.token #=> '3c022dbd-0a82-4382-bd0d-5af6e11b8d67'
|
38
|
+
#
|
39
|
+
# # OR you can just use Client#app_token since Client#request_session_token
|
40
|
+
# # sets `@app_token`.
|
41
|
+
# client.app_token #=> '3c022dbd-0a82-4382-bd0d-5af6e11b8d67'
|
42
|
+
#
|
43
|
+
# @attr_reader app_token
|
44
|
+
#
|
45
|
+
class Respoke::Client
|
46
|
+
|
47
|
+
# Default base_url
|
48
|
+
DEFAULT_BASE_URL = 'https://api.respoke.io/v1'
|
49
|
+
|
50
|
+
# Base URL used for API requests
|
51
|
+
attr_reader :base_url
|
52
|
+
|
53
|
+
# Sets the App-Secret token
|
54
|
+
attr_reader :app_secret
|
55
|
+
|
56
|
+
# Creates a new Client instance.
|
57
|
+
#
|
58
|
+
# @param app_secret [String] The application App-Secret token.
|
59
|
+
# (Defaults to nil)
|
60
|
+
# @param base_url [String] Overrides the {DEFAULT_BASE_URL} constant.
|
61
|
+
# (Defaults to {DEFAULT_BASE_URL})
|
62
|
+
#
|
63
|
+
# @return [Respoke::Client]
|
64
|
+
def initialize(app_secret: nil, base_url: DEFAULT_BASE_URL)
|
65
|
+
@base_url = base_url
|
66
|
+
@app_secret = app_secret
|
67
|
+
end
|
68
|
+
|
69
|
+
# Either returns the current `@app_token` or sets it based on the parameter
|
70
|
+
# Hash `token_request_params`.
|
71
|
+
#
|
72
|
+
# @param token_request_params [Hash] parameters for
|
73
|
+
# {#request_session_token_id}.
|
74
|
+
# @option token_request_params [String] appId The application ID.
|
75
|
+
# @option token_request_params [String] roleId Role ID to use for permissions
|
76
|
+
# on given endpoint ID.
|
77
|
+
# @option token_request_params [String] endpointId The endpoint ID to
|
78
|
+
# authenticate.
|
79
|
+
# @option token_request_params [Number] ttl (86400) Length of time token is
|
80
|
+
# valid.
|
81
|
+
#
|
82
|
+
# @return [String] App-Token value.
|
83
|
+
def app_token(token_request_params={})
|
84
|
+
@app_token ||= (
|
85
|
+
if token_request_params
|
86
|
+
response = request_session_token_id(token_request_params)
|
87
|
+
request_session_token(
|
88
|
+
appId: response.appId,
|
89
|
+
tokenId: response.tokenId
|
90
|
+
).token
|
91
|
+
end
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Request a token ID for use in requesting the App-Token value.
|
96
|
+
#
|
97
|
+
# @todo test return value
|
98
|
+
#
|
99
|
+
# @param appId [String] The application ID that matches the App-Secret.
|
100
|
+
# @param roleId [String] The role ID to use for the given endpoint.
|
101
|
+
# @param endpointId [String] The endpoint ID that is being authenticated.
|
102
|
+
# @param ttl [Number] The amount of time in seconds the App-Token is
|
103
|
+
# valid. (Defaults to 86400)
|
104
|
+
#
|
105
|
+
# @return [Respoke::Response::SessionTokenId] The API response object.
|
106
|
+
def request_session_token_id(appId:, roleId:, endpointId:, ttl: 86400)
|
107
|
+
response = connection.post 'tokens' do |request|
|
108
|
+
request.body = {
|
109
|
+
appId: appId,
|
110
|
+
endpointId: endpointId,
|
111
|
+
roleId: roleId,
|
112
|
+
ttl: 86400
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
if response.status != 200
|
117
|
+
raise Respoke::Errors::UnexpectedServerError, <<-ERR
|
118
|
+
request failed with status #{response.status}:
|
119
|
+
#{response.body}
|
120
|
+
ERR
|
121
|
+
else
|
122
|
+
Respoke::Response::SessionTokenId.new(response.body)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Request the session token using the tokenId retrived with
|
127
|
+
# {#request_session_token_id}. This method sets the `app_token` attribute.
|
128
|
+
#
|
129
|
+
# @todo test setting of `@app_token`.
|
130
|
+
#
|
131
|
+
# @param appId [String] The application ID used in the token request.
|
132
|
+
# @param tokenId [String] The token ID requested from
|
133
|
+
# {#request_session_token_id}.
|
134
|
+
#
|
135
|
+
# @return [Respoke::Response::SessionToken] The API response object.
|
136
|
+
def request_session_token(appId:, tokenId:)
|
137
|
+
response = connection.post 'session-tokens' do |request|
|
138
|
+
request.body = {
|
139
|
+
appId: appId,
|
140
|
+
tokenId: tokenId
|
141
|
+
}
|
142
|
+
end
|
143
|
+
@app_token = response.body.fetch('token', nil)
|
144
|
+
|
145
|
+
if response.status != 200
|
146
|
+
raise Respoke::Errors::UnexpectedServerError, <<-ERR
|
147
|
+
request failed with status #{response.status}:
|
148
|
+
#{response.body}
|
149
|
+
ERR
|
150
|
+
else
|
151
|
+
Respoke::Response::SessionToken.new(response.body)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Get the roles
|
156
|
+
#
|
157
|
+
# @return [Array<Respoke::Role>] An array of role objects
|
158
|
+
# @raise [Respoke::Errors::UnexpectedServerError] if errors occur
|
159
|
+
def roles()
|
160
|
+
response = connection.get 'roles'
|
161
|
+
|
162
|
+
if response.status != 200
|
163
|
+
raise Respoke::Errors::UnexpectedServerError, <<-ERR
|
164
|
+
request failed with status #{response.status}:
|
165
|
+
#{response.body}
|
166
|
+
ERR
|
167
|
+
else
|
168
|
+
response.body.map { |r| Respoke::Role.new(connection, r.each_with_object({}) { |(k,v), h| h[k.to_sym] = v} ) }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Create a role
|
173
|
+
#
|
174
|
+
# @param name [String] The name of the role
|
175
|
+
# @param rules [Hash] The permissions for the role
|
176
|
+
#
|
177
|
+
# @return [Respoke::Role] The role that was created
|
178
|
+
# @raise [Respoke::Errors::UnexpectedServerError] if errors occur
|
179
|
+
def create_role(name:, rules: {})
|
180
|
+
response = connection.post 'roles' do |request|
|
181
|
+
request.body = rules.merge( name: name )
|
182
|
+
end
|
183
|
+
|
184
|
+
if response.status != 200
|
185
|
+
raise Respoke::Errors::UnexpectedServerError, <<-ERR
|
186
|
+
request failed with status #{response.status}:
|
187
|
+
#{response.body}
|
188
|
+
ERR
|
189
|
+
else
|
190
|
+
Respoke::Role.new(self, response.body.each_with_object({}) { |(k,v), h| h[k.to_sym] = v} )
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
# Find a role
|
196
|
+
#
|
197
|
+
# @param id [String] The id of the role to retrieve
|
198
|
+
#
|
199
|
+
# @return [Respoke::Role] The role that was retrieved, nil if none found
|
200
|
+
# @raise [Respoke::Errors::UnexpectedServerError] if errors occur
|
201
|
+
def find_role(id:)
|
202
|
+
response = connection.get "roles/#{id}"
|
203
|
+
|
204
|
+
if response.status == 404
|
205
|
+
nil
|
206
|
+
elsif !response.success?
|
207
|
+
raise Respoke::Errors::UnexpectedServerError, <<-ERR
|
208
|
+
request failed with status #{response.status}:
|
209
|
+
#{response.body}
|
210
|
+
ERR
|
211
|
+
else
|
212
|
+
Respoke::Role.new(self, response.body.each_with_object({}) { |(k,v), h| h[k.to_sym] = v} )
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Update a role
|
217
|
+
#
|
218
|
+
# @param id [String] The id of the role to update
|
219
|
+
# @param rules [Hash] The new permissions for the role
|
220
|
+
#
|
221
|
+
# @return [Boolean] true if successfully updated
|
222
|
+
# @raise [Respoke::Errors::UnexpectedServerError] if errors occur
|
223
|
+
def update_role(id:, rules:)
|
224
|
+
response = connection.put "roles/#{id}" do |request|
|
225
|
+
request.body = rules
|
226
|
+
end
|
227
|
+
|
228
|
+
if !response.success?
|
229
|
+
raise Respoke::Errors::UnexpectedServerError, <<-ERR
|
230
|
+
request failed with status #{response.status}:
|
231
|
+
#{response.body}
|
232
|
+
ERR
|
233
|
+
else
|
234
|
+
true
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Delete a role
|
239
|
+
#
|
240
|
+
# @param id [String] The id of the role to retrieve
|
241
|
+
#
|
242
|
+
# @return [Boolean] true if the role was deleted, false otherwise
|
243
|
+
def delete_role(id:)
|
244
|
+
response = connection.delete "roles/#{id}"
|
245
|
+
response.success?
|
246
|
+
end
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
# Creates a Faraday connection object to make requests with.
|
251
|
+
#
|
252
|
+
# @return [Faraday::Connection] The connection object for making API requests.
|
253
|
+
def connection
|
254
|
+
@connection ||= Faraday.new(
|
255
|
+
url: @base_url,
|
256
|
+
headers: { :'App-Secret' => @app_secret }
|
257
|
+
) do |faraday|
|
258
|
+
faraday.request :json
|
259
|
+
|
260
|
+
faraday.response :json, :content_type => 'application/json'
|
261
|
+
|
262
|
+
faraday.adapter Faraday.default_adapter
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|