respoke 1.1.3
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.
- 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
|
+
[](http://badge.fury.io/rb/respoke)
|
2
|
+
[](https://travis-ci.org/pho3nixf1re/ruby-respoke)
|
3
|
+
[](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
|