onelogin 1.7.0 → 1.9.0
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 +4 -4
- data/.github/workflows/test.yml +1 -1
- data/README.md +75 -5
- data/examples/Gemfile.lock +15 -11
- data/examples/events-to-csv.rb +5 -4
- data/lib/onelogin/api/apiexception.rb +3 -0
- data/lib/onelogin/api/client.rb +109 -18
- data/lib/onelogin/api/cursor.rb +46 -3
- data/lib/onelogin/api/models/app.rb +8 -1
- data/lib/onelogin/api/models/user.rb +2 -1
- data/lib/onelogin/api/util/constants.rb +18 -0
- data/lib/onelogin/api/util/parser.rb +6 -2
- data/lib/onelogin/api/util/url_builder.rb +25 -1
- data/lib/onelogin/version.rb +1 -1
- data/onelogin.gemspec +2 -2
- metadata +6 -115
- data/.travis.yml +0 -6
- data/examples/rails-custom-login-page/.gitignore +0 -45
- data/examples/rails-custom-login-page/.ruby-version +0 -1
- data/examples/rails-custom-login-page/Gemfile +0 -57
- data/examples/rails-custom-login-page/Gemfile.lock +0 -212
- data/examples/rails-custom-login-page/README.md +0 -137
- data/examples/rails-custom-login-page/Rakefile +0 -6
- data/examples/rails-custom-login-page/app/assets/config/manifest.js +0 -3
- data/examples/rails-custom-login-page/app/assets/images/.keep +0 -0
- data/examples/rails-custom-login-page/app/assets/javascripts/application.js +0 -18
- data/examples/rails-custom-login-page/app/assets/javascripts/cable.js +0 -13
- data/examples/rails-custom-login-page/app/assets/javascripts/channels/.keep +0 -0
- data/examples/rails-custom-login-page/app/assets/javascripts/dashboard.coffee +0 -3
- data/examples/rails-custom-login-page/app/assets/javascripts/home.coffee +0 -3
- data/examples/rails-custom-login-page/app/assets/javascripts/sessions.coffee +0 -3
- data/examples/rails-custom-login-page/app/assets/stylesheets/application.css +0 -15
- data/examples/rails-custom-login-page/app/assets/stylesheets/dashboard.scss +0 -3
- data/examples/rails-custom-login-page/app/assets/stylesheets/home.scss +0 -3
- data/examples/rails-custom-login-page/app/assets/stylesheets/sessions.scss +0 -3
- data/examples/rails-custom-login-page/app/channels/application_cable/channel.rb +0 -4
- data/examples/rails-custom-login-page/app/channels/application_cable/connection.rb +0 -4
- data/examples/rails-custom-login-page/app/controllers/application_controller.rb +0 -23
- data/examples/rails-custom-login-page/app/controllers/concerns/.keep +0 -0
- data/examples/rails-custom-login-page/app/controllers/dashboard_controller.rb +0 -10
- data/examples/rails-custom-login-page/app/controllers/home_controller.rb +0 -5
- data/examples/rails-custom-login-page/app/controllers/sessions_controller.rb +0 -46
- data/examples/rails-custom-login-page/app/controllers/users_controller.rb +0 -120
- data/examples/rails-custom-login-page/app/helpers/application_helper.rb +0 -2
- data/examples/rails-custom-login-page/app/helpers/dashboard_helper.rb +0 -28
- data/examples/rails-custom-login-page/app/helpers/home_helper.rb +0 -2
- data/examples/rails-custom-login-page/app/helpers/sessions_helper.rb +0 -87
- data/examples/rails-custom-login-page/app/helpers/users_helper.rb +0 -3
- data/examples/rails-custom-login-page/app/jobs/application_job.rb +0 -2
- data/examples/rails-custom-login-page/app/mailers/application_mailer.rb +0 -4
- data/examples/rails-custom-login-page/app/models/application_record.rb +0 -3
- data/examples/rails-custom-login-page/app/models/concerns/.keep +0 -0
- data/examples/rails-custom-login-page/app/views/dashboard/index.html.erb +0 -43
- data/examples/rails-custom-login-page/app/views/home/index.html.erb +0 -272
- data/examples/rails-custom-login-page/app/views/layouts/application.html.erb +0 -31
- data/examples/rails-custom-login-page/app/views/layouts/mailer.html.erb +0 -13
- data/examples/rails-custom-login-page/app/views/layouts/mailer.text.erb +0 -1
- data/examples/rails-custom-login-page/app/views/users/_form.html.erb +0 -1
- data/examples/rails-custom-login-page/app/views/users/_user.json.jbuilder +0 -2
- data/examples/rails-custom-login-page/app/views/users/edit.html.erb +0 -33
- data/examples/rails-custom-login-page/app/views/users/index.html.erb +0 -33
- data/examples/rails-custom-login-page/app/views/users/index.json.jbuilder +0 -1
- data/examples/rails-custom-login-page/app/views/users/new.html.erb +0 -60
- data/examples/rails-custom-login-page/app/views/users/onboard.html.erb +0 -54
- data/examples/rails-custom-login-page/app/views/users/show.html.erb +0 -17
- data/examples/rails-custom-login-page/app/views/users/show.json.jbuilder +0 -1
- data/examples/rails-custom-login-page/bin/bundle +0 -3
- data/examples/rails-custom-login-page/bin/rails +0 -9
- data/examples/rails-custom-login-page/bin/rake +0 -9
- data/examples/rails-custom-login-page/bin/setup +0 -38
- data/examples/rails-custom-login-page/bin/spring +0 -17
- data/examples/rails-custom-login-page/bin/update +0 -29
- data/examples/rails-custom-login-page/bin/yarn +0 -11
- data/examples/rails-custom-login-page/config/application.rb +0 -18
- data/examples/rails-custom-login-page/config/boot.rb +0 -3
- data/examples/rails-custom-login-page/config/cable.yml +0 -10
- data/examples/rails-custom-login-page/config/database.yml +0 -25
- data/examples/rails-custom-login-page/config/environment.rb +0 -5
- data/examples/rails-custom-login-page/config/environments/development.rb +0 -54
- data/examples/rails-custom-login-page/config/environments/production.rb +0 -91
- data/examples/rails-custom-login-page/config/environments/test.rb +0 -42
- data/examples/rails-custom-login-page/config/initializers/application_controller_renderer.rb +0 -8
- data/examples/rails-custom-login-page/config/initializers/assets.rb +0 -14
- data/examples/rails-custom-login-page/config/initializers/backtrace_silencers.rb +0 -7
- data/examples/rails-custom-login-page/config/initializers/cookies_serializer.rb +0 -5
- data/examples/rails-custom-login-page/config/initializers/filter_parameter_logging.rb +0 -4
- data/examples/rails-custom-login-page/config/initializers/inflections.rb +0 -16
- data/examples/rails-custom-login-page/config/initializers/mime_types.rb +0 -4
- data/examples/rails-custom-login-page/config/initializers/onelogin.rb +0 -6
- data/examples/rails-custom-login-page/config/initializers/wrap_parameters.rb +0 -14
- data/examples/rails-custom-login-page/config/locales/en.yml +0 -33
- data/examples/rails-custom-login-page/config/puma.rb +0 -56
- data/examples/rails-custom-login-page/config/routes.rb +0 -23
- data/examples/rails-custom-login-page/config/secrets.yml.sample +0 -38
- data/examples/rails-custom-login-page/config/spring.rb +0 -6
- data/examples/rails-custom-login-page/config.ru +0 -5
- data/examples/rails-custom-login-page/db/seeds.rb +0 -7
- data/examples/rails-custom-login-page/lib/assets/.keep +0 -0
- data/examples/rails-custom-login-page/lib/tasks/.keep +0 -0
- data/examples/rails-custom-login-page/package.json +0 -5
- data/examples/rails-custom-login-page/public/404.html +0 -67
- data/examples/rails-custom-login-page/public/422.html +0 -67
- data/examples/rails-custom-login-page/public/500.html +0 -66
- data/examples/rails-custom-login-page/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/rails-custom-login-page/public/apple-touch-icon.png +0 -0
- data/examples/rails-custom-login-page/public/favicon.ico +0 -0
- data/examples/rails-custom-login-page/public/robots.txt +0 -1
- data/examples/rails-custom-login-page/test/application_system_test_case.rb +0 -5
- data/examples/rails-custom-login-page/test/controllers/.keep +0 -0
- data/examples/rails-custom-login-page/test/controllers/dashboard_controller_test.rb +0 -9
- data/examples/rails-custom-login-page/test/controllers/home_controller_test.rb +0 -9
- data/examples/rails-custom-login-page/test/controllers/sessions_controller_test.rb +0 -7
- data/examples/rails-custom-login-page/test/controllers/users_controller_test.rb +0 -48
- data/examples/rails-custom-login-page/test/fixtures/.keep +0 -0
- data/examples/rails-custom-login-page/test/fixtures/files/.keep +0 -0
- data/examples/rails-custom-login-page/test/helpers/.keep +0 -0
- data/examples/rails-custom-login-page/test/integration/.keep +0 -0
- data/examples/rails-custom-login-page/test/mailers/.keep +0 -0
- data/examples/rails-custom-login-page/test/models/.keep +0 -0
- data/examples/rails-custom-login-page/test/system/.keep +0 -0
- data/examples/rails-custom-login-page/test/test_helper.rb +0 -9
- data/examples/rails-custom-login-page/vendor/.keep +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0887ddcbf97b6c873d5547842d27822f35b7e4eeefa4c78285ed2828c0cf546b'
|
|
4
|
+
data.tar.gz: 5a59b6e4c2ff4605775a8473931ed82183d9f434d455a559214828cd612c1988
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 01b0e196b7f431a2d6e04a1b64757a4b0224b0a5b34e3b17ee6c829f642e6b321305d6ebe15dee6dae44fd15b92f64555927522994c469f7f8ba2a1964ccfb10
|
|
7
|
+
data.tar.gz: 2595a8b6f36f9a446e991baa18b66e37c9f2ac21687a43d7ca6f0c1dfa1b5c515a7edf8c9d57f3e64874d7690d79d807af31c3f689842fc21fda936bd8fd2b52
|
data/.github/workflows/test.yml
CHANGED
data/README.md
CHANGED
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
This SDK will let you execute all the API methods, version/1, described
|
|
4
4
|
at https://developers.onelogin.com/api-docs/1/getting-started/dev-overview.
|
|
5
5
|
|
|
6
|
-
The toolkit is hosted on
|
|
6
|
+
The toolkit is hosted on GitHub. You can download it from:
|
|
7
7
|
* Lastest release: https://github.com/onelogin/onelogin-ruby-sdk/releases/latest
|
|
8
|
-
*
|
|
8
|
+
* Main repo: https://github.com/onelogin/onelogin-ruby-sdk/tree/main
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Support
|
|
12
|
+
|
|
13
|
+
OneLogin by One Identity open source projects are supported through [OneLogin GitHub issues](https://github.com/onelogin/onelogin-ruby-sdk/issues). This includes all scripts, plugins, SDKs, modules, code snippets or other solutions. For assistance with any OneLogin by One Identity GitHub project, please raise a new Issue on the [OneLogin GitHub issues](https://github.com/onelogin/onelogin-ruby-sdk/issues) page. Requests for assistance made through official One Identity Support will be referred back to GitHub where those requests can benefit all users.
|
|
14
|
+
|
|
9
15
|
|
|
10
16
|
|
|
11
17
|
## Installation
|
|
@@ -27,7 +33,17 @@ Or install it yourself as:
|
|
|
27
33
|
|
|
28
34
|
### Dependencies
|
|
29
35
|
|
|
36
|
+
* Ruby >= 3.2
|
|
30
37
|
* httparty
|
|
38
|
+
* nokogiri
|
|
39
|
+
|
|
40
|
+
Ruby 3.2 is the lowest version on which `nokogiri` resolves to a release with
|
|
41
|
+
no known security advisories — below it, the newest installable `nokogiri` is
|
|
42
|
+
still affected by a critical one.
|
|
43
|
+
|
|
44
|
+
On an older Ruby, `bundle install` resolves to the most recent release that
|
|
45
|
+
still supports it rather than failing, so existing builds keep working. You
|
|
46
|
+
just stop receiving updates until you upgrade Ruby.
|
|
31
47
|
|
|
32
48
|
## Getting started
|
|
33
49
|
|
|
@@ -43,6 +59,7 @@ If you don't have an account you can [sign up for a free developer account here]
|
|
|
43
59
|
|max_results| Optional: Defaults to 1000 |
|
|
44
60
|
|timeout| Optional: Defaults to 60 (requires httparty > 0.16.2) |
|
|
45
61
|
|token_expiration_buffer| Optional: Non-negative number of seconds before token expiration to trigger a refresh. Set to `0` to disable. Defaults to 30 |
|
|
62
|
+
|api_version| Optional: `1` or `2`. Calls that API version from every method that supports it. See [API versions](#api-versions) |
|
|
46
63
|
|
|
47
64
|
```ruby
|
|
48
65
|
require 'onelogin'
|
|
@@ -142,6 +159,34 @@ client = OneLogin::Api::Client.new(
|
|
|
142
159
|
client.get_events.map {|event| event.id}
|
|
143
160
|
```
|
|
144
161
|
|
|
162
|
+
### API versions
|
|
163
|
+
|
|
164
|
+
Each method calls one version of the OneLogin API by default, and that default
|
|
165
|
+
doesn't change. Pass `api_version: 2` to move the methods below to API v2:
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
client = OneLogin::Api::Client.new(
|
|
169
|
+
client_id: '',
|
|
170
|
+
client_secret:'',
|
|
171
|
+
api_version: 2
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`get_users`, `get_user`, `get_user_apps`, `create_user`, `update_user`,
|
|
176
|
+
`delete_user`, `get_roles` and `get_role` support both versions. Every other method
|
|
177
|
+
keeps its default version, whatever `api_version` says.
|
|
178
|
+
`OneLogin::Api::Client::API_VERSIONS` lists the versions each method supports.
|
|
179
|
+
|
|
180
|
+
Under v2 they return the same models, filled from v2's responses. Two differences
|
|
181
|
+
show through:
|
|
182
|
+
|
|
183
|
+
- v2 lists fewer fields per user than v1. Under v2, `get_users` leaves `role_ids`
|
|
184
|
+
and `custom_attributes` empty, for example. `get_user` fills them.
|
|
185
|
+
- `App#login_id` from `get_user_apps` is the login's numeric id in v2, where v1
|
|
186
|
+
gives the username used to sign in. v2 fills the `provisioning_enabled`,
|
|
187
|
+
`provisioning_state` and `provisioning_status` fields rather than `provisioned`
|
|
188
|
+
and `personal`.
|
|
189
|
+
|
|
145
190
|
|
|
146
191
|
|
|
147
192
|
### Available Methods
|
|
@@ -167,17 +212,42 @@ query_parameters = {
|
|
|
167
212
|
}
|
|
168
213
|
users_filtered2 = client.get_users(query_parameters)
|
|
169
214
|
|
|
170
|
-
# Get Users with
|
|
215
|
+
# Get Users with a page size of 3.
|
|
216
|
+
#
|
|
217
|
+
# `limit` is the API's page size, not a cap on the total. To stop after a
|
|
218
|
+
# fixed number of results use `take`, or set `max_results` on the client.
|
|
171
219
|
query_parameters = {
|
|
172
220
|
limit: 3
|
|
173
221
|
}
|
|
174
|
-
|
|
222
|
+
users_paged_by_three = client.get_users(query_parameters)
|
|
223
|
+
|
|
224
|
+
# Stop after 3 users regardless of page size
|
|
225
|
+
first_three = client.get_users.take(3)
|
|
175
226
|
|
|
176
227
|
# Only return the firstname and email fields for each user
|
|
177
228
|
client.get_users(fields: 'email,firstname').each do |user|
|
|
178
229
|
puts "#{user.firstname} - #{user.email}"
|
|
179
230
|
end
|
|
180
231
|
|
|
232
|
+
# Sort server-side with the sort parameter. Prefix with - to reverse.
|
|
233
|
+
# Sorting happens in the API, so you do not need to fetch every user and
|
|
234
|
+
# sort client-side.
|
|
235
|
+
query_parameters = {
|
|
236
|
+
sort: '+id'
|
|
237
|
+
}
|
|
238
|
+
users_sorted = client.get_users(query_parameters)
|
|
239
|
+
|
|
240
|
+
# Ten least recently active users.
|
|
241
|
+
#
|
|
242
|
+
# `sort` is applied by the API. `limit` sets the page size so the request
|
|
243
|
+
# itself only fetches ten rows, and `take` stops the cursor after ten so it
|
|
244
|
+
# does not page any further.
|
|
245
|
+
#
|
|
246
|
+
# Use both: `limit` alone does not cap the total, because the cursor keeps
|
|
247
|
+
# following pages until they run out or `max_results` is reached, and `take`
|
|
248
|
+
# alone still transfers a full default-sized page and discards the extra.
|
|
249
|
+
least_recently_active = client.get_users(sort: '+last_login', limit: 10).take(10)
|
|
250
|
+
|
|
181
251
|
# Get User by id
|
|
182
252
|
user = client.get_user(users_filtered.first.id)
|
|
183
253
|
user_mfa = client.get_user(users_filtered2.first.id)
|
|
@@ -515,4 +585,4 @@ The gem is available as open source under the terms of the [MIT License](http://
|
|
|
515
585
|
|
|
516
586
|
## Code of Conduct
|
|
517
587
|
|
|
518
|
-
Everyone interacting in the OneLogin Ruby Sdk project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/onelogin/onelogin-ruby-sdk/blob/
|
|
588
|
+
Everyone interacting in the OneLogin Ruby Sdk project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/onelogin/onelogin-ruby-sdk/blob/main/CODE_OF_CONDUCT.md).
|
data/examples/Gemfile.lock
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
GEM
|
|
2
2
|
remote: https://rubygems.org/
|
|
3
3
|
specs:
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
bigdecimal (4.1.2)
|
|
5
|
+
csv (3.3.6)
|
|
6
|
+
httparty (0.24.2)
|
|
7
|
+
csv
|
|
8
|
+
mini_mime (>= 1.0.0)
|
|
6
9
|
multi_xml (>= 0.5.2)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
onelogin (1.
|
|
10
|
+
mini_mime (1.1.5)
|
|
11
|
+
mini_portile2 (2.8.9)
|
|
12
|
+
multi_xml (0.9.1)
|
|
13
|
+
bigdecimal (>= 3.1, < 5)
|
|
14
|
+
nokogiri (1.19.4)
|
|
15
|
+
mini_portile2 (~> 2.8.2)
|
|
16
|
+
racc (~> 1.4)
|
|
17
|
+
onelogin (1.7.0)
|
|
15
18
|
httparty (>= 0.13.7)
|
|
16
19
|
nokogiri (>= 1.6.3.1)
|
|
20
|
+
racc (1.8.1)
|
|
17
21
|
|
|
18
22
|
PLATFORMS
|
|
19
23
|
ruby
|
|
@@ -22,4 +26,4 @@ DEPENDENCIES
|
|
|
22
26
|
onelogin
|
|
23
27
|
|
|
24
28
|
BUNDLED WITH
|
|
25
|
-
2.
|
|
29
|
+
2.6.9
|
data/examples/events-to-csv.rb
CHANGED
|
@@ -52,11 +52,15 @@ OptionParser.new do |opts|
|
|
|
52
52
|
end
|
|
53
53
|
end.parse!
|
|
54
54
|
|
|
55
|
+
counter = 0
|
|
56
|
+
limit = options[:limit] || 1000
|
|
57
|
+
|
|
55
58
|
# Fetch the events
|
|
56
59
|
client = OneLogin::Api::Client.new(
|
|
57
60
|
client_id: 'ONELOGIN_CLIENT_ID_GOES_HERE',
|
|
58
61
|
client_secret: 'ONELOGIN_CLIENT_SECRET_GOES_HERE',
|
|
59
|
-
region: 'us'
|
|
62
|
+
region: 'us',
|
|
63
|
+
max_results: limit
|
|
60
64
|
)
|
|
61
65
|
|
|
62
66
|
attribute_names = ['id', 'created_at', 'account_id', 'user_id', 'user_name', 'event_type_id',
|
|
@@ -66,9 +70,6 @@ attribute_names = ['id', 'created_at', 'account_id', 'user_id', 'user_name', 'ev
|
|
|
66
70
|
'operation_name', 'directory_sync_run_id', 'directory_id', 'resolution', 'client_id',
|
|
67
71
|
'resource_type_id', 'error_description', 'risk_score', 'risk_reasons', 'risk_cookie_id', 'browser_fingerprint']
|
|
68
72
|
|
|
69
|
-
counter = 0
|
|
70
|
-
limit = options[:limit] || 1000
|
|
71
|
-
|
|
72
73
|
# We remove limit from options parsed to the api as we want to fetch
|
|
73
74
|
# the max number of records possible and then use the cursor that is
|
|
74
75
|
# built into the ruby sdk to limit the results
|
data/lib/onelogin/api/client.rb
CHANGED
|
@@ -21,6 +21,7 @@ module OneLogin
|
|
|
21
21
|
|
|
22
22
|
attr_accessor :client_id, :client_secret, :region
|
|
23
23
|
attr_accessor :user_agent, :error, :error_description, :error_attribute
|
|
24
|
+
attr_reader :api_version
|
|
24
25
|
|
|
25
26
|
NOKOGIRI_OPTIONS = Nokogiri::XML::ParseOptions::STRICT |
|
|
26
27
|
Nokogiri::XML::ParseOptions::NONET
|
|
@@ -32,6 +33,9 @@ module OneLogin
|
|
|
32
33
|
#
|
|
33
34
|
# @param config [Hash] Client Id, Client Secret and Region
|
|
34
35
|
#
|
|
36
|
+
# Pass api_version to call that API version from every method that
|
|
37
|
+
# supports it (see API_VERSIONS). Other methods keep their default.
|
|
38
|
+
#
|
|
35
39
|
def initialize(config)
|
|
36
40
|
options = Hash[config.map { |(k, v)| [k.to_sym, v] }]
|
|
37
41
|
|
|
@@ -40,6 +44,7 @@ module OneLogin
|
|
|
40
44
|
@region = options[:region] || 'us'
|
|
41
45
|
@max_results = options[:max_results] || 1000
|
|
42
46
|
@token_expiration_buffer = normalize_expiration_buffer(options[:token_expiration_buffer])
|
|
47
|
+
@api_version = normalize_api_version(options[:api_version])
|
|
43
48
|
|
|
44
49
|
if options[:timeout] and defined? self.class.default_timeout
|
|
45
50
|
self.class.default_timeout options[:timeout]
|
|
@@ -60,6 +65,28 @@ module OneLogin
|
|
|
60
65
|
raise ArgumentError, 'client_id & client_secret are required' unless @client_id && @client_secret
|
|
61
66
|
end
|
|
62
67
|
|
|
68
|
+
# Normalize caller-supplied parameter hashes to string keys.
|
|
69
|
+
#
|
|
70
|
+
# The README and examples use symbol keys, but the required-parameter
|
|
71
|
+
# checks below look them up as strings. `to_json` already serializes both
|
|
72
|
+
# forms identically, so only the validation ever disagreed.
|
|
73
|
+
#
|
|
74
|
+
# Anything that isn't a Hash becomes an empty one so the required-parameter
|
|
75
|
+
# guards still fire and report the missing attribute, rather than blowing
|
|
76
|
+
# up on has_key? and surfacing as a generic 500.
|
|
77
|
+
#
|
|
78
|
+
# An already-string-keyed hash is returned untouched, so existing callers
|
|
79
|
+
# keep the exact object they passed - including Hash subclasses with their
|
|
80
|
+
# own to_json.
|
|
81
|
+
#
|
|
82
|
+
def stringify_param_keys(params)
|
|
83
|
+
return {} unless params.is_a?(Hash)
|
|
84
|
+
return params if params.keys.all? { |key| key.is_a?(String) }
|
|
85
|
+
|
|
86
|
+
params.each_with_object({}) { |(key, value), out| out[key.to_s] = value }
|
|
87
|
+
end
|
|
88
|
+
private :stringify_param_keys
|
|
89
|
+
|
|
63
90
|
# Coerce the configured refresh buffer into a non-negative Integer.
|
|
64
91
|
#
|
|
65
92
|
# ENV-backed configs hand this over as a String, and a negative buffer
|
|
@@ -83,6 +110,40 @@ module OneLogin
|
|
|
83
110
|
end
|
|
84
111
|
private :normalize_expiration_buffer
|
|
85
112
|
|
|
113
|
+
# Coerce the configured API version into one the SDK can call.
|
|
114
|
+
#
|
|
115
|
+
# nil leaves every method on its default version. Anything else must be a
|
|
116
|
+
# version at least one method supports, so a typo fails here instead of
|
|
117
|
+
# quietly calling the defaults. Only Integers and Strings convert:
|
|
118
|
+
# Integer() would truncate 1.9 to 1.
|
|
119
|
+
#
|
|
120
|
+
def normalize_api_version(value)
|
|
121
|
+
return nil if value.nil?
|
|
122
|
+
|
|
123
|
+
version = begin
|
|
124
|
+
Integer(value) if value.is_a?(Integer) || value.is_a?(String)
|
|
125
|
+
rescue ArgumentError
|
|
126
|
+
nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
supported = API_VERSIONS.values.flatten.uniq.sort
|
|
130
|
+
unless supported.include?(version)
|
|
131
|
+
raise ArgumentError, "api_version must be one of #{supported.join(', ')}, got #{value.inspect}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
version
|
|
135
|
+
end
|
|
136
|
+
private :normalize_api_version
|
|
137
|
+
|
|
138
|
+
# The API version a method calls: the configured api_version when the
|
|
139
|
+
# method supports it, otherwise the method's default.
|
|
140
|
+
#
|
|
141
|
+
def api_version_for(method)
|
|
142
|
+
versions = API_VERSIONS.fetch(method)
|
|
143
|
+
versions.include?(@api_version) ? @api_version : versions.first
|
|
144
|
+
end
|
|
145
|
+
private :api_version_for
|
|
146
|
+
|
|
86
147
|
# Clean any previous error registered at the client.
|
|
87
148
|
#
|
|
88
149
|
def clean_error
|
|
@@ -373,14 +434,16 @@ module OneLogin
|
|
|
373
434
|
prepare_token
|
|
374
435
|
|
|
375
436
|
begin
|
|
437
|
+
version = api_version_for(:get_users)
|
|
376
438
|
options = {
|
|
377
439
|
model: OneLogin::Api::Models::User,
|
|
378
440
|
headers: authorized_headers,
|
|
379
441
|
max_results: @max_results,
|
|
380
|
-
params: params
|
|
442
|
+
params: params,
|
|
443
|
+
api_version: version
|
|
381
444
|
}
|
|
382
445
|
|
|
383
|
-
return Cursor.new(self, url_for(GET_USERS_URL), options)
|
|
446
|
+
return Cursor.new(self, url_for(GET_USERS_URL, version: version), options)
|
|
384
447
|
|
|
385
448
|
rescue Exception => e
|
|
386
449
|
@error = '500'
|
|
@@ -409,7 +472,8 @@ module OneLogin
|
|
|
409
472
|
return
|
|
410
473
|
end
|
|
411
474
|
|
|
412
|
-
|
|
475
|
+
version = api_version_for(:get_user)
|
|
476
|
+
url = url_for(GET_USER_URL, user_id, version: version)
|
|
413
477
|
|
|
414
478
|
response = self.class.get(
|
|
415
479
|
url,
|
|
@@ -418,7 +482,9 @@ module OneLogin
|
|
|
418
482
|
|
|
419
483
|
if response.code == 200
|
|
420
484
|
json_data = JSON.parse(response.body)
|
|
421
|
-
if
|
|
485
|
+
if version == 2
|
|
486
|
+
return OneLogin::Api::Models::User.new(json_data) if json_data.is_a?(Hash)
|
|
487
|
+
elsif json_data && json_data['data']
|
|
422
488
|
return OneLogin::Api::Models::User.new(json_data['data'][0])
|
|
423
489
|
end
|
|
424
490
|
else
|
|
@@ -452,13 +518,15 @@ module OneLogin
|
|
|
452
518
|
return
|
|
453
519
|
end
|
|
454
520
|
|
|
521
|
+
version = api_version_for(:get_user_apps)
|
|
455
522
|
options = {
|
|
456
523
|
model: OneLogin::Api::Models::App,
|
|
457
524
|
headers: authorized_headers,
|
|
458
|
-
max_results: @max_results
|
|
525
|
+
max_results: @max_results,
|
|
526
|
+
api_version: version
|
|
459
527
|
}
|
|
460
528
|
|
|
461
|
-
return Cursor.new(self, url_for(GET_APPS_FOR_USER_URL, user_id), options)
|
|
529
|
+
return Cursor.new(self, url_for(GET_APPS_FOR_USER_URL, user_id, version: version), options)
|
|
462
530
|
|
|
463
531
|
rescue Exception => e
|
|
464
532
|
@error = '500'
|
|
@@ -566,7 +634,8 @@ module OneLogin
|
|
|
566
634
|
prepare_token
|
|
567
635
|
|
|
568
636
|
begin
|
|
569
|
-
|
|
637
|
+
version = api_version_for(:create_user)
|
|
638
|
+
url = url_for(CREATE_USER_URL, version: version)
|
|
570
639
|
|
|
571
640
|
response = self.class.post(
|
|
572
641
|
url,
|
|
@@ -574,9 +643,12 @@ module OneLogin
|
|
|
574
643
|
body: user_params.to_json
|
|
575
644
|
)
|
|
576
645
|
|
|
577
|
-
|
|
646
|
+
# v1 answers a create with 200, v2 with 201 Created.
|
|
647
|
+
if response.code == (version == 2 ? 201 : 200)
|
|
578
648
|
json_data = JSON.parse(response.body)
|
|
579
|
-
if
|
|
649
|
+
if version == 2
|
|
650
|
+
return OneLogin::Api::Models::User.new(json_data) if json_data.is_a?(Hash)
|
|
651
|
+
elsif json_data && json_data['data']
|
|
580
652
|
return OneLogin::Api::Models::User.new(json_data['data'][0])
|
|
581
653
|
end
|
|
582
654
|
else
|
|
@@ -617,7 +689,8 @@ module OneLogin
|
|
|
617
689
|
return
|
|
618
690
|
end
|
|
619
691
|
|
|
620
|
-
|
|
692
|
+
version = api_version_for(:update_user)
|
|
693
|
+
url = url_for(UPDATE_USER_URL, user_id, version: version)
|
|
621
694
|
|
|
622
695
|
response = self.class.put(
|
|
623
696
|
url,
|
|
@@ -627,7 +700,9 @@ module OneLogin
|
|
|
627
700
|
|
|
628
701
|
if response.code == 200
|
|
629
702
|
json_data = JSON.parse(response.body)
|
|
630
|
-
if
|
|
703
|
+
if version == 2
|
|
704
|
+
return OneLogin::Api::Models::User.new(json_data) if json_data.is_a?(Hash)
|
|
705
|
+
elsif json_data && json_data['data']
|
|
631
706
|
return OneLogin::Api::Models::User.new(json_data['data'][0])
|
|
632
707
|
end
|
|
633
708
|
else
|
|
@@ -1047,14 +1122,18 @@ module OneLogin
|
|
|
1047
1122
|
return
|
|
1048
1123
|
end
|
|
1049
1124
|
|
|
1050
|
-
|
|
1125
|
+
version = api_version_for(:delete_user)
|
|
1126
|
+
url = url_for(DELETE_USER_URL, user_id, version: version)
|
|
1051
1127
|
|
|
1052
1128
|
response = self.class.delete(
|
|
1053
1129
|
url,
|
|
1054
1130
|
headers: authorized_headers
|
|
1055
1131
|
)
|
|
1056
1132
|
|
|
1057
|
-
|
|
1133
|
+
# v2 confirms a delete with an empty 204 rather than a status body.
|
|
1134
|
+
if version == 2 && response.code == 204
|
|
1135
|
+
return true
|
|
1136
|
+
elsif version == 1 && response.code == 200
|
|
1058
1137
|
return handle_operation_response(response)
|
|
1059
1138
|
else
|
|
1060
1139
|
@error = response.code.to_s
|
|
@@ -1141,6 +1220,8 @@ module OneLogin
|
|
|
1141
1220
|
begin
|
|
1142
1221
|
url = url_for(SESSION_LOGIN_TOKEN_URL)
|
|
1143
1222
|
|
|
1223
|
+
query_params = stringify_param_keys(query_params)
|
|
1224
|
+
|
|
1144
1225
|
if query_params.nil? || !query_params.has_key?('username_or_email') || !query_params.has_key?('password') || !query_params.has_key?('subdomain')
|
|
1145
1226
|
raise "username_or_email, password and subdomain are required parameters"
|
|
1146
1227
|
end
|
|
@@ -1366,7 +1447,12 @@ module OneLogin
|
|
|
1366
1447
|
begin
|
|
1367
1448
|
url = url_for(CREATE_APP_URL)
|
|
1368
1449
|
|
|
1369
|
-
|
|
1450
|
+
app_params = stringify_param_keys(app_params)
|
|
1451
|
+
|
|
1452
|
+
# Was `unless has_key? || value.empty?`, which is true in every case,
|
|
1453
|
+
# so the check never fired and an app could be created with no
|
|
1454
|
+
# connector_id at all.
|
|
1455
|
+
if !app_params.has_key?('connector_id') || app_params['connector_id'].to_s.empty?
|
|
1370
1456
|
@error = '400'
|
|
1371
1457
|
@error_description = "connector_id is required"
|
|
1372
1458
|
@error_attribute = "connector_id"
|
|
@@ -1592,14 +1678,16 @@ module OneLogin
|
|
|
1592
1678
|
prepare_token
|
|
1593
1679
|
|
|
1594
1680
|
begin
|
|
1681
|
+
version = api_version_for(:get_roles)
|
|
1595
1682
|
options = {
|
|
1596
1683
|
model: OneLogin::Api::Models::Role,
|
|
1597
1684
|
headers: authorized_headers,
|
|
1598
1685
|
max_results: @max_results,
|
|
1599
|
-
params: params
|
|
1686
|
+
params: params,
|
|
1687
|
+
api_version: version
|
|
1600
1688
|
}
|
|
1601
1689
|
|
|
1602
|
-
return Cursor.new(self, url_for(GET_ROLES_URL), options)
|
|
1690
|
+
return Cursor.new(self, url_for(GET_ROLES_URL, version: version), options)
|
|
1603
1691
|
|
|
1604
1692
|
rescue Exception => e
|
|
1605
1693
|
@error = '500'
|
|
@@ -1628,7 +1716,8 @@ module OneLogin
|
|
|
1628
1716
|
return
|
|
1629
1717
|
end
|
|
1630
1718
|
|
|
1631
|
-
|
|
1719
|
+
version = api_version_for(:get_role)
|
|
1720
|
+
url = url_for(GET_ROLE_URL, role_id, version: version)
|
|
1632
1721
|
|
|
1633
1722
|
response = self.class.get(
|
|
1634
1723
|
url,
|
|
@@ -1637,7 +1726,9 @@ module OneLogin
|
|
|
1637
1726
|
|
|
1638
1727
|
if response.code == 200
|
|
1639
1728
|
json_data = JSON.parse(response.body)
|
|
1640
|
-
if
|
|
1729
|
+
if version == 2
|
|
1730
|
+
return OneLogin::Api::Models::Role.new(json_data) if json_data.is_a?(Hash)
|
|
1731
|
+
elsif json_data && json_data['data']
|
|
1641
1732
|
return OneLogin::Api::Models::Role.new(json_data['data'][0])
|
|
1642
1733
|
end
|
|
1643
1734
|
else
|
data/lib/onelogin/api/cursor.rb
CHANGED
|
@@ -25,6 +25,7 @@ class Cursor
|
|
|
25
25
|
@params = options[:params] || {}
|
|
26
26
|
@max_results = options[:max_results]
|
|
27
27
|
@container = options[:container] || 'data'
|
|
28
|
+
@api_version = options[:api_version]
|
|
28
29
|
|
|
29
30
|
@collection = []
|
|
30
31
|
@after_cursor = options.fetch(:after_cursor, nil)
|
|
@@ -53,19 +54,24 @@ class Cursor
|
|
|
53
54
|
private
|
|
54
55
|
|
|
55
56
|
def fetch_next_page
|
|
57
|
+
return fetch_next_v2_page if @api_version == 2
|
|
58
|
+
|
|
56
59
|
@params = @params.merge(after_cursor: @after_cursor) if @after_cursor
|
|
57
60
|
@client.prepare_token
|
|
58
61
|
|
|
59
62
|
response = @client.class.get(
|
|
60
63
|
@url,
|
|
61
|
-
headers:
|
|
64
|
+
headers: request_headers,
|
|
62
65
|
query: @params
|
|
63
66
|
)
|
|
64
67
|
|
|
65
68
|
json = response.parsed_response
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
# HTTParty picks its parser from Content-Type, so a gateway or proxy error
|
|
71
|
+
# page comes back as a String and every has_key? below would blow up with a
|
|
72
|
+
# NoMethodError instead of a usable ApiException.
|
|
73
|
+
if json.nil? || !json.is_a?(Hash)
|
|
74
|
+
raise OneLogin::Api::ApiException.new("Response could not be parsed", response.code)
|
|
69
75
|
elsif !json.has_key?(@container) && json.has_key?('status') && json["status"]["error"] == true
|
|
70
76
|
raise OneLogin::Api::ApiException.new(extract_error_message_from_response(response), json["status"]["code"])
|
|
71
77
|
elsif !json.has_key?(@container) && json.has_key?('statusCode')
|
|
@@ -88,6 +94,43 @@ class Cursor
|
|
|
88
94
|
end
|
|
89
95
|
end
|
|
90
96
|
|
|
97
|
+
# v2 returns a bare array and puts the next page's cursor in the After-Cursor
|
|
98
|
+
# header, which is absent on the last page. The cursor already encodes the
|
|
99
|
+
# filters and page size, and the API rejects a cursor sent alongside limit or
|
|
100
|
+
# page, so follow-up requests send the cursor alone.
|
|
101
|
+
def fetch_next_v2_page
|
|
102
|
+
@client.prepare_token
|
|
103
|
+
|
|
104
|
+
response = @client.class.get(
|
|
105
|
+
@url,
|
|
106
|
+
headers: request_headers,
|
|
107
|
+
query: @after_cursor ? { cursor: @after_cursor } : @params
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
results = response.parsed_response
|
|
111
|
+
|
|
112
|
+
unless response.code == 200 && results.is_a?(Array)
|
|
113
|
+
message = results.is_a?(Hash) ? extract_error_message_from_response(response) : "Response could not be parsed"
|
|
114
|
+
raise OneLogin::Api::ApiException.new(message, response.code)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
@collection += if results_remaining < results.size
|
|
118
|
+
results.slice(0, results_remaining)
|
|
119
|
+
else
|
|
120
|
+
results
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
@after_cursor = response.headers['after-cursor']
|
|
124
|
+
@last_cursor_empty = @after_cursor.nil? || @after_cursor.empty?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# The headers were built when the cursor was, and prepare_token may have
|
|
128
|
+
# refreshed the token since: a cursor enumerated later, or one whose pages
|
|
129
|
+
# outlive the token. Take the current token from the client.
|
|
130
|
+
def request_headers
|
|
131
|
+
@headers.merge(@client.authorized_headers)
|
|
132
|
+
end
|
|
133
|
+
|
|
91
134
|
def after_cursor(json)
|
|
92
135
|
value = nil
|
|
93
136
|
if json.has_key?('pagination')
|
|
@@ -6,15 +6,22 @@ module OneLogin
|
|
|
6
6
|
|
|
7
7
|
attr_accessor :id, :name, :icon, :provisioned
|
|
8
8
|
attr_accessor :extension, :login_id, :personal
|
|
9
|
+
attr_accessor :provisioning_enabled, :provisioning_state, :provisioning_status
|
|
9
10
|
|
|
11
|
+
# v2 renames icon to icon_url and replaces provisioned and personal with
|
|
12
|
+
# the provisioning_* fields. Its login_id is the numeric id of the
|
|
13
|
+
# user's login, where v1 gives the username used to sign in.
|
|
10
14
|
def initialize(data)
|
|
11
15
|
@id = data['id']
|
|
12
16
|
@name = data['name']
|
|
13
|
-
@icon = data['icon']
|
|
17
|
+
@icon = data['icon'] || data['icon_url']
|
|
14
18
|
@provisioned = data['provisioned']
|
|
15
19
|
@extension = data['extension']
|
|
16
20
|
@login_id = data['login_id']
|
|
17
21
|
@personal = data['personal']
|
|
22
|
+
@provisioning_enabled = data['provisioning_enabled']
|
|
23
|
+
@provisioning_state = data['provisioning_state']
|
|
24
|
+
@provisioning_status = data['provisioning_status']
|
|
18
25
|
end
|
|
19
26
|
end
|
|
20
27
|
end
|
|
@@ -28,7 +28,8 @@ module OneLogin
|
|
|
28
28
|
@samaccountname = data['samaccountname'].to_s
|
|
29
29
|
@userprincipalname = data['userprincipalname'].to_s
|
|
30
30
|
@group_id = data['group_id']? data['group_id'].to_i : nil
|
|
31
|
-
|
|
31
|
+
# v1 calls it role_id, v2 role_ids.
|
|
32
|
+
@role_ids = data['role_id'] || data['role_ids'] || []
|
|
32
33
|
@custom_attributes = data['custom_attributes'] ? data['custom_attributes'] : []
|
|
33
34
|
@openid_name = data['openid_name'].to_s
|
|
34
35
|
@locale_code = data['locale_code'].to_s
|
|
@@ -98,6 +98,24 @@ module OneLogin
|
|
|
98
98
|
GET_USERS_ASSIGNED_TO_PRIVILEGE_URL = "https://api.%s.onelogin.com/api/1/privileges/%s/users"
|
|
99
99
|
ASSIGN_USERS_TO_PRIVILEGE_URL = "https://api.%s.onelogin.com/api/1/privileges/%s/users"
|
|
100
100
|
REMOVE_USER_FROM_PRIVILEGE_URL = "https://api.%s.onelogin.com/api/1/privileges/%s/users/%s"
|
|
101
|
+
|
|
102
|
+
# API versions each client method can call, keyed by method name.
|
|
103
|
+
#
|
|
104
|
+
# The first version is the default and matches the version in the
|
|
105
|
+
# method's URL constant. A method that isn't listed only calls the
|
|
106
|
+
# version in its URL constant. List a version here only once the method
|
|
107
|
+
# can parse that version's response: v1 and v2 use different envelopes.
|
|
108
|
+
API_VERSIONS = {
|
|
109
|
+
get_users: [1, 2],
|
|
110
|
+
get_user: [1, 2],
|
|
111
|
+
get_user_apps: [1, 2],
|
|
112
|
+
create_user: [1, 2],
|
|
113
|
+
update_user: [1, 2],
|
|
114
|
+
delete_user: [1, 2],
|
|
115
|
+
get_roles: [1, 2],
|
|
116
|
+
get_role: [1, 2]
|
|
117
|
+
}.transform_values(&:freeze).freeze
|
|
118
|
+
|
|
101
119
|
VALID_ACTIONS = [
|
|
102
120
|
"apps:List",
|
|
103
121
|
"apps:Get",
|
|
@@ -6,7 +6,9 @@ module OneLogin
|
|
|
6
6
|
message = ''
|
|
7
7
|
content = JSON.parse(response.body)
|
|
8
8
|
if content
|
|
9
|
-
|
|
9
|
+
# v1 nests the error under a status object. Some v2 endpoints send
|
|
10
|
+
# status as the bare HTTP code, with the text in description.
|
|
11
|
+
if content['status'].is_a?(Hash)
|
|
10
12
|
status = content['status']
|
|
11
13
|
if status.has_key?('message')
|
|
12
14
|
if status['message'].instance_of?(Hash)
|
|
@@ -21,6 +23,8 @@ module OneLogin
|
|
|
21
23
|
end
|
|
22
24
|
elsif content.has_key?('message')
|
|
23
25
|
message = content['message']
|
|
26
|
+
elsif content.has_key?('description')
|
|
27
|
+
message = content['description']
|
|
24
28
|
elsif content.has_key?('name')
|
|
25
29
|
message = content['name']
|
|
26
30
|
end
|
|
@@ -31,7 +35,7 @@ module OneLogin
|
|
|
31
35
|
def extract_error_attribute_from_response(response)
|
|
32
36
|
attribute = nil
|
|
33
37
|
content = JSON.parse(response.body)
|
|
34
|
-
if content && content.
|
|
38
|
+
if content && content['status'].is_a?(Hash)
|
|
35
39
|
status = content['status']
|
|
36
40
|
if status.has_key?('message') && status['message'].instance_of?(Hash)
|
|
37
41
|
if status['message'].has_key?('attribute')
|