descope 1.1.0 → 1.2.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/ci.yaml +4 -4
- data/.github/workflows/publish-gem.yaml +2 -2
- data/.github/workflows/release-please.yaml +1 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +10 -0
- data/Gemfile +5 -5
- data/Gemfile.lock +32 -32
- data/README.md +233 -1
- data/lib/descope/api/v1/auth/enchantedlink.rb +76 -9
- data/lib/descope/api/v1/management/access_key.rb +8 -7
- data/lib/descope/api/v1/management/analytics.rb +62 -0
- data/lib/descope/api/v1/management/audit.rb +2 -2
- data/lib/descope/api/v1/management/authz.rb +15 -15
- data/lib/descope/api/v1/management/common.rb +76 -0
- data/lib/descope/api/v1/management/descoper.rb +45 -0
- data/lib/descope/api/v1/management/engine.rb +44 -0
- data/lib/descope/api/v1/management/fga.rb +64 -0
- data/lib/descope/api/v1/management/flow.rb +5 -5
- data/lib/descope/api/v1/management/group.rb +42 -0
- data/lib/descope/api/v1/management/jwt_template.rb +49 -0
- data/lib/descope/api/v1/management/lists.rb +96 -0
- data/lib/descope/api/v1/management/management_key.rb +54 -0
- data/lib/descope/api/v1/management/outbound_app.rb +112 -0
- data/lib/descope/api/v1/management/password.rb +2 -2
- data/lib/descope/api/v1/management/permission.rb +4 -4
- data/lib/descope/api/v1/management/project.rb +5 -5
- data/lib/descope/api/v1/management/role.rb +5 -5
- data/lib/descope/api/v1/management/scim.rb +14 -14
- data/lib/descope/api/v1/management/scope_claim_mapping.rb +34 -0
- data/lib/descope/api/v1/management/sso_application.rb +7 -7
- data/lib/descope/api/v1/management/sso_settings.rb +5 -5
- data/lib/descope/api/v1/management/tenant.rb +8 -6
- data/lib/descope/api/v1/management/third_party_application.rb +190 -0
- data/lib/descope/api/v1/management/user.rb +36 -36
- data/lib/descope/api/v1/management.rb +22 -0
- data/lib/descope/http_client.rb +30 -0
- data/lib/descope/mixins/common.rb +1 -0
- data/lib/descope/mixins/http.rb +23 -16
- data/lib/descope/mixins/initializer.rb +19 -9
- data/lib/descope/mixins/requests.rb +24 -0
- data/lib/descope/mixins.rb +2 -1
- data/lib/descope/version.rb +1 -1
- data/spec/integration/lib.descope/api/v1/management/user_spec.rb +1 -1
- data/spec/lib.descope/api/v1/auth/enchantedlink_spec.rb +163 -1
- data/spec/lib.descope/api/v1/management/access_key_spec.rb +7 -7
- data/spec/lib.descope/api/v1/management/analytics_spec.rb +82 -0
- data/spec/lib.descope/api/v1/management/audit_spec.rb +2 -2
- data/spec/lib.descope/api/v1/management/authz_spec.rb +16 -16
- data/spec/lib.descope/api/v1/management/descoper_spec.rb +101 -0
- data/spec/lib.descope/api/v1/management/engine_spec.rb +115 -0
- data/spec/lib.descope/api/v1/management/fga_spec.rb +169 -0
- data/spec/lib.descope/api/v1/management/flow_spec.rb +5 -5
- data/spec/lib.descope/api/v1/management/group_spec.rb +72 -0
- data/spec/lib.descope/api/v1/management/jwt_template_spec.rb +103 -0
- data/spec/lib.descope/api/v1/management/lists_spec.rb +285 -0
- data/spec/lib.descope/api/v1/management/management_key_spec.rb +109 -0
- data/spec/lib.descope/api/v1/management/outbound_app_spec.rb +236 -0
- data/spec/lib.descope/api/v1/management/password_spec.rb +2 -2
- data/spec/lib.descope/api/v1/management/permission_spec.rb +4 -4
- data/spec/lib.descope/api/v1/management/project_spec.rb +4 -4
- data/spec/lib.descope/api/v1/management/role_spec.rb +5 -5
- data/spec/lib.descope/api/v1/management/scim_spec.rb +14 -14
- data/spec/lib.descope/api/v1/management/scope_claim_mapping_spec.rb +59 -0
- data/spec/lib.descope/api/v1/management/sso_application_spec.rb +6 -6
- data/spec/lib.descope/api/v1/management/sso_settings_spec.rb +5 -5
- data/spec/lib.descope/api/v1/management/tenant_spec.rb +6 -6
- data/spec/lib.descope/api/v1/management/third_party_application_spec.rb +171 -0
- data/spec/lib.descope/api/v1/management/user_spec.rb +36 -36
- data/spec/lib.descope/auth_management_key_spec.rb +151 -0
- data/spec/support/client_config.rb +1 -0
- data/spec/support/dummy_class.rb +12 -9
- metadata +28 -3
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'descope/mixins/http'
|
|
4
|
+
|
|
5
|
+
module Descope
|
|
6
|
+
# Performs the SDK's HTTP calls. One instance is created per kind of request so that the two
|
|
7
|
+
# management keys are never sent together: the management client carries the management key, the
|
|
8
|
+
# authentication client carries the auth management key.
|
|
9
|
+
class HttpClient
|
|
10
|
+
include Descope::Mixins::HTTP
|
|
11
|
+
|
|
12
|
+
def initialize(base_uri:, project_id:, headers:, logger:, key: nil, timeout: nil, retry_count: nil)
|
|
13
|
+
@base_uri = base_uri
|
|
14
|
+
@project_id = project_id
|
|
15
|
+
@key = key
|
|
16
|
+
@headers = headers.dup
|
|
17
|
+
@logger = logger
|
|
18
|
+
@timeout = timeout
|
|
19
|
+
@retry_count = retry_count
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Bearer <project_id>[:<pswd>][:<key>] where pswd is a refresh token or access key presented by
|
|
23
|
+
# the caller and key is this client's management key. Blank parts are skipped, so callers do
|
|
24
|
+
# not have to branch on what happens to be configured.
|
|
25
|
+
def authorization_header(pswd = nil)
|
|
26
|
+
bearer = [@project_id, pswd, @key].reject { |part| part.nil? || part.to_s.empty? }.join(':')
|
|
27
|
+
{ 'Authorization' => "Bearer #{bearer}" }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -81,6 +81,7 @@ module Descope
|
|
|
81
81
|
VERIFY_ENCHANTEDLINK_AUTH_PATH = '/v1/auth/enchantedlink/verify'
|
|
82
82
|
GET_SESSION_ENCHANTEDLINK_AUTH_PATH = '/v1/auth/enchantedlink/pending-session'
|
|
83
83
|
UPDATE_USER_EMAIL_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/update/email'
|
|
84
|
+
UPDATE_USER_PHONE_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/update/phone'
|
|
84
85
|
|
|
85
86
|
# oauth
|
|
86
87
|
OAUTH_START_PATH = '/v1/auth/oauth/authorize'
|
data/lib/descope/mixins/http.rb
CHANGED
|
@@ -18,13 +18,18 @@ module Descope
|
|
|
18
18
|
MIN_REQUEST_RETRY_DELAY = 250
|
|
19
19
|
BASE_DELAY = 100
|
|
20
20
|
|
|
21
|
-
%i[get post
|
|
21
|
+
HTTP_METHODS = %i[get post put patch delete].freeze
|
|
22
|
+
|
|
23
|
+
HTTP_METHODS.each do |method|
|
|
22
24
|
define_method(method) do |uri, body = {}, extra_headers = {}, pswd = nil|
|
|
23
25
|
body = body.delete_if { |_, v| v.nil? }
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
# The Authorization header travels with the request rather than being merged into the
|
|
28
|
+
# shared @headers, so concurrent requests cannot send each other's credentials.
|
|
29
|
+
headers = authorization_header(pswd).merge(extra_headers || {})
|
|
25
30
|
|
|
26
31
|
@logger.debug "request => method: #{method}, uri: #{uri}, body: #{body}, extra_headers: #{extra_headers}}"
|
|
27
|
-
request_with_retry(method, uri, body,
|
|
32
|
+
request_with_retry(method, uri, body, headers)
|
|
28
33
|
end
|
|
29
34
|
end
|
|
30
35
|
|
|
@@ -118,6 +123,15 @@ module Descope
|
|
|
118
123
|
@headers.merge!(h.to_hash)
|
|
119
124
|
end
|
|
120
125
|
|
|
126
|
+
# Everything after the project ID in the bearer is either a token or a management key, so
|
|
127
|
+
# none of it belongs in a log line.
|
|
128
|
+
def mask_authorization(headers)
|
|
129
|
+
authorization = headers['Authorization']
|
|
130
|
+
return headers if authorization.nil?
|
|
131
|
+
|
|
132
|
+
headers.merge('Authorization' => authorization.sub(/\A(Bearer [^:]*):.*\z/, '\1:***'))
|
|
133
|
+
end
|
|
134
|
+
|
|
121
135
|
def request_with_retry(method, uri, body = {}, extra_headers = {}, pswd = nil)
|
|
122
136
|
Retryable.retryable(retry_options) do
|
|
123
137
|
request(method, uri, body, extra_headers)
|
|
@@ -125,23 +139,16 @@ module Descope
|
|
|
125
139
|
end
|
|
126
140
|
|
|
127
141
|
def request(method, uri, body = {}, extra_headers = {})
|
|
128
|
-
|
|
129
|
-
headers_debug = @headers.dup
|
|
130
|
-
if headers_debug['Authorization']
|
|
131
|
-
headers_debug['Authorization'] = headers_debug['Authorization'].gsub(/(.{10})\z/, '***********')
|
|
132
|
-
end
|
|
142
|
+
request_headers = @headers.merge(extra_headers)
|
|
133
143
|
|
|
134
144
|
@logger.debug "base url: #{@base_uri}"
|
|
135
|
-
@logger.debug "request method: #{method}, uri: #{uri}, body: #{body},
|
|
145
|
+
@logger.debug "request method: #{method}, uri: #{uri}, body: #{body}, " \
|
|
146
|
+
"headers: #{mask_authorization(request_headers)}"
|
|
136
147
|
result = case method
|
|
137
|
-
when :get
|
|
138
|
-
|
|
139
|
-
call(:get, encode_uri(uri), timeout, get_headers)
|
|
140
|
-
when :delete
|
|
141
|
-
delete_headers = @headers.merge({ params: body })
|
|
142
|
-
call(:delete, encode_uri(uri), timeout, delete_headers)
|
|
148
|
+
when :get, :delete
|
|
149
|
+
call(method, encode_uri(uri), timeout, request_headers.merge({ params: body }))
|
|
143
150
|
else
|
|
144
|
-
call(method, encode_uri(uri), timeout,
|
|
151
|
+
call(method, encode_uri(uri), timeout, request_headers, body.to_json)
|
|
145
152
|
end
|
|
146
153
|
|
|
147
154
|
raise Descope::Unsupported.new('No response from server', code: 400) unless result.respond_to?(:code)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
|
+
require 'descope/http_client'
|
|
4
5
|
|
|
5
6
|
module Descope
|
|
6
7
|
module Mixins
|
|
7
8
|
# Helper class for initializing the Descope API
|
|
8
9
|
module Initializer
|
|
9
10
|
attr_accessor :public_keys, :mlock
|
|
11
|
+
attr_reader :base_uri, :headers
|
|
10
12
|
|
|
11
13
|
def initialize(config)
|
|
12
14
|
options = Hash[config.map { |(k, v)| [k.to_sym, v] }]
|
|
@@ -33,7 +35,10 @@ module Descope
|
|
|
33
35
|
@skip_verify = options[:skip_verify]
|
|
34
36
|
@secure = !@skip_verify
|
|
35
37
|
@management_key = options[:management_key] || ENV['DESCOPE_MANAGEMENT_KEY']
|
|
36
|
-
|
|
38
|
+
# Sent with every authentication request so that methods whose public access has been
|
|
39
|
+
# disabled can still be used. Never sent on management requests, and can - and probably
|
|
40
|
+
# should - be a different management key than @management_key.
|
|
41
|
+
@auth_management_key = options[:auth_management_key] || ENV['DESCOPE_AUTH_MANAGEMENT_KEY']
|
|
37
42
|
@timeout_seconds = options[:timeout_seconds] || Common::DEFAULT_TIMEOUT_SECONDS
|
|
38
43
|
@jwt_validation_leeway = options[:jwt_validation_leeway] || Common::DEFAULT_JWT_VALIDATION_LEEWAY
|
|
39
44
|
|
|
@@ -60,16 +65,21 @@ module Descope
|
|
|
60
65
|
|
|
61
66
|
end
|
|
62
67
|
|
|
63
|
-
def authorization_header(pswd = nil)
|
|
64
|
-
pswd = @default_pswd if pswd.nil? || pswd.empty?
|
|
65
|
-
bearer = "#{@project_id}:#{pswd}"
|
|
66
|
-
add_headers('Authorization' => "Bearer #{bearer}")
|
|
67
|
-
end
|
|
68
|
-
|
|
69
68
|
def initialize_api(options)
|
|
70
69
|
initialize_v1(options)
|
|
71
|
-
@
|
|
72
|
-
|
|
70
|
+
@auth_http = http_client(@auth_management_key)
|
|
71
|
+
@mgmt_http = http_client(@management_key)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def http_client(key)
|
|
75
|
+
HttpClient.new(
|
|
76
|
+
base_uri: @base_uri,
|
|
77
|
+
project_id: @project_id,
|
|
78
|
+
key: key,
|
|
79
|
+
headers: @headers,
|
|
80
|
+
logger: @logger,
|
|
81
|
+
timeout: @timeout_seconds
|
|
82
|
+
)
|
|
73
83
|
end
|
|
74
84
|
|
|
75
85
|
def initialize_v1(_options)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'descope/mixins/http'
|
|
4
|
+
|
|
5
|
+
module Descope
|
|
6
|
+
module Mixins
|
|
7
|
+
# Routes an API call to the HTTP client that holds the right key: the bare verbs serve the
|
|
8
|
+
# authentication APIs and go out with the auth management key, the mgmt_ prefixed verbs serve
|
|
9
|
+
# the management APIs and go out with the management key.
|
|
10
|
+
module Requests
|
|
11
|
+
attr_reader :auth_http, :mgmt_http
|
|
12
|
+
|
|
13
|
+
Descope::Mixins::HTTP::HTTP_METHODS.each do |method|
|
|
14
|
+
define_method(method) do |*args|
|
|
15
|
+
@auth_http.public_send(method, *args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
define_method(:"mgmt_#{method}") do |*args|
|
|
19
|
+
@mgmt_http.public_send(method, *args)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/descope/mixins.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'logger'
|
|
|
4
4
|
require 'jwt'
|
|
5
5
|
require 'descope/mixins/headers'
|
|
6
6
|
require 'descope/mixins/http'
|
|
7
|
+
require 'descope/mixins/requests'
|
|
7
8
|
require 'descope/mixins/initializer'
|
|
8
9
|
require 'descope/mixins/validation'
|
|
9
10
|
require 'descope/mixins/logging'
|
|
@@ -15,7 +16,7 @@ module Descope
|
|
|
15
16
|
module Mixins
|
|
16
17
|
include Descope::Mixins::Common
|
|
17
18
|
include Descope::Mixins::Headers
|
|
18
|
-
include Descope::Mixins::
|
|
19
|
+
include Descope::Mixins::Requests
|
|
19
20
|
include Descope::Mixins::Initializer
|
|
20
21
|
include Descope::Mixins::Logging
|
|
21
22
|
end
|
data/lib/descope/version.rb
CHANGED
|
@@ -242,7 +242,7 @@ describe Descope::Api::V1::Management::User do
|
|
|
242
242
|
begin
|
|
243
243
|
@client.expire_password(user['loginIds'][0])
|
|
244
244
|
@client.password_sign_in(login_id: user['loginIds'][0], password:)
|
|
245
|
-
rescue Descope::
|
|
245
|
+
rescue Descope::Unauthorized => e
|
|
246
246
|
expect(e.message).to match(/"errorCode":"E062909"/)
|
|
247
247
|
end
|
|
248
248
|
end
|
|
@@ -30,7 +30,7 @@ describe Descope::Api::V1::EnchantedLink do
|
|
|
30
30
|
expect(@instance).to receive(:post).with(
|
|
31
31
|
enchanted_link_compose_signin_url,
|
|
32
32
|
request_params,
|
|
33
|
-
|
|
33
|
+
{},
|
|
34
34
|
'refresh_token'
|
|
35
35
|
)
|
|
36
36
|
|
|
@@ -60,6 +60,48 @@ describe Descope::Api::V1::EnchantedLink do
|
|
|
60
60
|
@instance.send(:validate_refresh_token_provided, { mfa: true, stepup: true }, '')
|
|
61
61
|
end.to raise_error(Descope::AuthException, 'Missing refresh token for stepup/mfa')
|
|
62
62
|
end
|
|
63
|
+
|
|
64
|
+
it 'is expected to respond to sign in with phone' do
|
|
65
|
+
expect(@instance).to respond_to(:enchanted_link_sign_in_with_phone)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'is expected to sign in with enchanted link via sms' do
|
|
69
|
+
request_params = {
|
|
70
|
+
loginId: '+1234567890',
|
|
71
|
+
redirectUrl: 'https://some-uri/sms',
|
|
72
|
+
loginOptions: {
|
|
73
|
+
stepup: false,
|
|
74
|
+
customClaims: { 'abc': '123' },
|
|
75
|
+
mfa: false,
|
|
76
|
+
ssoAppId: 'sso-id'
|
|
77
|
+
},
|
|
78
|
+
providerId: 'provider-id',
|
|
79
|
+
templateId: 'template-id'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
expect(@instance).to receive(:post).with(
|
|
83
|
+
'/v1/auth/enchantedlink/signin/sms',
|
|
84
|
+
request_params,
|
|
85
|
+
{},
|
|
86
|
+
'refresh_token'
|
|
87
|
+
).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
|
|
88
|
+
|
|
89
|
+
res = @instance.enchanted_link_sign_in_with_phone(
|
|
90
|
+
login_id: '+1234567890',
|
|
91
|
+
uri: 'https://some-uri/sms',
|
|
92
|
+
login_options: {
|
|
93
|
+
stepup: false,
|
|
94
|
+
custom_claims: { 'abc': '123' },
|
|
95
|
+
mfa: false,
|
|
96
|
+
sso_app_id: 'sso-id'
|
|
97
|
+
},
|
|
98
|
+
provider_id: 'provider-id',
|
|
99
|
+
template_id: 'template-id',
|
|
100
|
+
refresh_token: 'refresh_token'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
expect(res['maskedPhone']).to eq('+1******890')
|
|
104
|
+
end
|
|
63
105
|
end
|
|
64
106
|
|
|
65
107
|
context '.sign_up' do
|
|
@@ -88,6 +130,36 @@ describe Descope::Api::V1::EnchantedLink do
|
|
|
88
130
|
)
|
|
89
131
|
end.not_to raise_error
|
|
90
132
|
end
|
|
133
|
+
|
|
134
|
+
it 'is expected to respond to sign up with phone' do
|
|
135
|
+
expect(@instance).to respond_to(:enchanted_link_sign_up_with_phone)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'is expected to sign up with enchanted link via sms' do
|
|
139
|
+
request_params = {
|
|
140
|
+
loginId: '+1234567890',
|
|
141
|
+
redirectUrl: 'https://some-uri/sms',
|
|
142
|
+
user: { loginId: 'user1', phone: '+1234567890' },
|
|
143
|
+
phone: '+1234567890',
|
|
144
|
+
providerId: 'provider-id',
|
|
145
|
+
templateId: 'template-id'
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
expect(@instance).to receive(:post).with(
|
|
149
|
+
'/v1/auth/enchantedlink/signup/sms',
|
|
150
|
+
request_params
|
|
151
|
+
).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
|
|
152
|
+
|
|
153
|
+
res = @instance.enchanted_link_sign_up_with_phone(
|
|
154
|
+
login_id: '+1234567890',
|
|
155
|
+
uri: 'https://some-uri/sms',
|
|
156
|
+
user: { login_id: 'user1', phone: '+1234567890' },
|
|
157
|
+
provider_id: 'provider-id',
|
|
158
|
+
template_id: 'template-id'
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
expect(res['maskedPhone']).to eq('+1******890')
|
|
162
|
+
end
|
|
91
163
|
end
|
|
92
164
|
|
|
93
165
|
context '.sign_up_or_in' do
|
|
@@ -125,6 +197,96 @@ describe Descope::Api::V1::EnchantedLink do
|
|
|
125
197
|
)
|
|
126
198
|
end.not_to raise_error
|
|
127
199
|
end
|
|
200
|
+
|
|
201
|
+
it 'is expected to respond to sign up or in with phone' do
|
|
202
|
+
expect(@instance).to respond_to(:enchanted_link_sign_up_or_in_with_phone)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it 'is expected to sign up or in with enchanted link via sms' do
|
|
206
|
+
request_params = {
|
|
207
|
+
loginId: '+1234567890',
|
|
208
|
+
redirectUrl: 'https://some-uri/sms',
|
|
209
|
+
loginOptions: {
|
|
210
|
+
stepup: false,
|
|
211
|
+
customClaims: { 'abc': '123' },
|
|
212
|
+
mfa: false,
|
|
213
|
+
ssoAppId: 'sso-id'
|
|
214
|
+
},
|
|
215
|
+
providerId: 'provider-id',
|
|
216
|
+
templateId: 'template-id'
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
expect(@instance).to receive(:post).with(
|
|
220
|
+
'/v1/auth/enchantedlink/signup-in/sms',
|
|
221
|
+
request_params
|
|
222
|
+
).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
|
|
223
|
+
|
|
224
|
+
res = @instance.enchanted_link_sign_up_or_in_with_phone(
|
|
225
|
+
login_id: '+1234567890',
|
|
226
|
+
uri: 'https://some-uri/sms',
|
|
227
|
+
login_options: {
|
|
228
|
+
stepup: false,
|
|
229
|
+
custom_claims: { 'abc': '123' },
|
|
230
|
+
mfa: false,
|
|
231
|
+
sso_app_id: 'sso-id'
|
|
232
|
+
},
|
|
233
|
+
provider_id: 'provider-id',
|
|
234
|
+
template_id: 'template-id'
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
expect(res['maskedPhone']).to eq('+1******890')
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
context '.update_user_phone' do
|
|
242
|
+
it 'is expected to respond to update user phone' do
|
|
243
|
+
expect(@instance).to respond_to(:enchanted_link_update_user_phone)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it 'is expected to update user phone with enchanted link via sms' do
|
|
247
|
+
request_params = {
|
|
248
|
+
loginId: 'test',
|
|
249
|
+
phone: '+1234567890',
|
|
250
|
+
addToLoginIDs: true,
|
|
251
|
+
onMergeUseExisting: true,
|
|
252
|
+
redirectUrl: 'https://some-uri/sms',
|
|
253
|
+
providerId: 'provider-id',
|
|
254
|
+
templateId: 'template-id',
|
|
255
|
+
templateOptions: { 'abc': '123' }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
expect(@instance).to receive(:post).with(
|
|
259
|
+
'/v1/auth/enchantedlink/update/phone/sms',
|
|
260
|
+
request_params,
|
|
261
|
+
{},
|
|
262
|
+
'refresh_token'
|
|
263
|
+
).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
|
|
264
|
+
|
|
265
|
+
res = @instance.enchanted_link_update_user_phone(
|
|
266
|
+
login_id: 'test',
|
|
267
|
+
phone: '+1234567890',
|
|
268
|
+
uri: 'https://some-uri/sms',
|
|
269
|
+
add_to_login_ids: true,
|
|
270
|
+
on_merge_use_existing: true,
|
|
271
|
+
provider_id: 'provider-id',
|
|
272
|
+
template_id: 'template-id',
|
|
273
|
+
template_options: { 'abc': '123' },
|
|
274
|
+
refresh_token: 'refresh_token'
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
expect(res['maskedPhone']).to eq('+1******890')
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it 'is expected to raise an error on an invalid phone number' do
|
|
281
|
+
expect do
|
|
282
|
+
@instance.enchanted_link_update_user_phone(
|
|
283
|
+
login_id: 'test',
|
|
284
|
+
phone: 'not-a-phone',
|
|
285
|
+
uri: 'https://some-uri/sms',
|
|
286
|
+
refresh_token: 'refresh_token'
|
|
287
|
+
)
|
|
288
|
+
end.to raise_error(Descope::AuthException)
|
|
289
|
+
end
|
|
128
290
|
end
|
|
129
291
|
|
|
130
292
|
context '.enchanted_link_verify_token' do
|
|
@@ -15,7 +15,7 @@ describe Descope::Api::V1::Management::AccessKey do
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
it 'is expected to create access key' do
|
|
18
|
-
expect(@instance).to receive(:
|
|
18
|
+
expect(@instance).to receive(:mgmt_post).with(
|
|
19
19
|
ACCESS_KEY_CREATE_PATH, {
|
|
20
20
|
name: 'test',
|
|
21
21
|
expireTime: 0,
|
|
@@ -46,7 +46,7 @@ describe Descope::Api::V1::Management::AccessKey do
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
it 'is expected to load an access key' do
|
|
49
|
-
expect(@instance).to receive(:
|
|
49
|
+
expect(@instance).to receive(:mgmt_get).with(
|
|
50
50
|
ACCESS_KEY_LOAD_PATH, { id: '123' }
|
|
51
51
|
)
|
|
52
52
|
expect { @instance.load_access_key('123') }.not_to raise_error
|
|
@@ -59,7 +59,7 @@ describe Descope::Api::V1::Management::AccessKey do
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
it 'is expected to search all access keys' do
|
|
62
|
-
expect(@instance).to receive(:
|
|
62
|
+
expect(@instance).to receive(:mgmt_post).with(
|
|
63
63
|
ACCESS_KEYS_SEARCH_PATH, { tenantIds: %w[123 456] }
|
|
64
64
|
)
|
|
65
65
|
expect { @instance.search_all_access_keys(%w[123 456]) }.not_to raise_error
|
|
@@ -72,7 +72,7 @@ describe Descope::Api::V1::Management::AccessKey do
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
it 'is expected to update an access keys' do
|
|
75
|
-
expect(@instance).to receive(:
|
|
75
|
+
expect(@instance).to receive(:mgmt_post).with(
|
|
76
76
|
ACCESS_KEY_UPDATE_PATH, { id: '123', name: 'test1' }
|
|
77
77
|
)
|
|
78
78
|
expect { @instance.update_access_key(id: '123', name: 'test1') }.not_to raise_error
|
|
@@ -85,7 +85,7 @@ describe Descope::Api::V1::Management::AccessKey do
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
it 'is expected to deactivate an access keys' do
|
|
88
|
-
expect(@instance).to receive(:
|
|
88
|
+
expect(@instance).to receive(:mgmt_post).with(
|
|
89
89
|
ACCESS_KEY_DEACTIVATE_PATH, { id: '123' }
|
|
90
90
|
)
|
|
91
91
|
expect { @instance.deactivate_access_key('123') }.not_to raise_error
|
|
@@ -98,7 +98,7 @@ describe Descope::Api::V1::Management::AccessKey do
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
it 'is expected to activate an access keys' do
|
|
101
|
-
expect(@instance).to receive(:
|
|
101
|
+
expect(@instance).to receive(:mgmt_post).with(
|
|
102
102
|
ACCESS_KEY_ACTIVATE_PATH, { id: '123' }
|
|
103
103
|
)
|
|
104
104
|
expect { @instance.activate_access_key('123') }.not_to raise_error
|
|
@@ -111,7 +111,7 @@ describe Descope::Api::V1::Management::AccessKey do
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
it 'is expected to delete an access keys' do
|
|
114
|
-
expect(@instance).to receive(:
|
|
114
|
+
expect(@instance).to receive(:mgmt_post).with(
|
|
115
115
|
ACCESS_KEY_DELETE_PATH, { id: '123' }
|
|
116
116
|
)
|
|
117
117
|
expect { @instance.delete_access_key('123') }.not_to raise_error
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Descope::Api::V1::Management::Analytics do
|
|
6
|
+
before(:all) do
|
|
7
|
+
dummy_instance = DummyClass.new
|
|
8
|
+
dummy_instance.extend(Descope::Api::V1::Management::Analytics)
|
|
9
|
+
@instance = dummy_instance
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context '.search' do
|
|
13
|
+
let(:mock_response) do
|
|
14
|
+
{
|
|
15
|
+
'analytics' => [
|
|
16
|
+
{
|
|
17
|
+
'projectId' => 'abc',
|
|
18
|
+
'action' => 'get',
|
|
19
|
+
'created' => '1234567000',
|
|
20
|
+
'device' => 'mobile',
|
|
21
|
+
'method' => 'post',
|
|
22
|
+
'geo' => 'US',
|
|
23
|
+
'tenant' => 'tenant1',
|
|
24
|
+
'referrer' => 'referrer1',
|
|
25
|
+
'cnt' => '5'
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
before do
|
|
32
|
+
allow(@instance).to receive(:mgmt_post).with(
|
|
33
|
+
ANALYTICS_SEARCH_PATH,
|
|
34
|
+
{
|
|
35
|
+
from: 1_234_567_000,
|
|
36
|
+
to: 123_456_789_000,
|
|
37
|
+
groupByAction: true,
|
|
38
|
+
groupByDevice: true,
|
|
39
|
+
groupByMethod: true,
|
|
40
|
+
groupByGeo: true,
|
|
41
|
+
groupByTenant: true,
|
|
42
|
+
groupByReferrer: true,
|
|
43
|
+
groupByCreated: true,
|
|
44
|
+
actions: %w[action1 action2],
|
|
45
|
+
excludedActions: %w[exclude1 exclude2],
|
|
46
|
+
devices: %w[Bot Mobile Desktop Tablet Unknown],
|
|
47
|
+
methods: %w[otp totp magiclink oauth saml password],
|
|
48
|
+
geos: %w[US IL],
|
|
49
|
+
tenants: %w[tenant1 tenant2]
|
|
50
|
+
}
|
|
51
|
+
).and_return(mock_response)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'should respond to .analytics_search' do
|
|
55
|
+
expect(@instance).to respond_to :analytics_search
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'is expected to search analytics and get records' do
|
|
59
|
+
res = nil # define res outside the block
|
|
60
|
+
expect do
|
|
61
|
+
res = @instance.analytics_search(
|
|
62
|
+
from_ts: 1_234_567_000,
|
|
63
|
+
to_ts: 123_456_789_000,
|
|
64
|
+
group_by_action: true,
|
|
65
|
+
group_by_device: true,
|
|
66
|
+
group_by_method: true,
|
|
67
|
+
group_by_geo: true,
|
|
68
|
+
group_by_tenant: true,
|
|
69
|
+
group_by_referrer: true,
|
|
70
|
+
group_by_created: true,
|
|
71
|
+
actions: %w[action1 action2],
|
|
72
|
+
excluded_actions: %w[exclude1 exclude2],
|
|
73
|
+
devices: %w[Bot Mobile Desktop Tablet Unknown],
|
|
74
|
+
methods: %w[otp totp magiclink oauth saml password],
|
|
75
|
+
geos: %w[US IL],
|
|
76
|
+
tenants: %w[tenant1 tenant2]
|
|
77
|
+
)
|
|
78
|
+
end.not_to raise_error
|
|
79
|
+
expect(res['analytics'][0]['projectId']).to eq('abc')
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -31,7 +31,7 @@ describe Descope::Api::V1::Management::Audit do
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
before do
|
|
34
|
-
allow(@instance).to receive(:
|
|
34
|
+
allow(@instance).to receive(:mgmt_post).twice.with(
|
|
35
35
|
AUDIT_SEARCH,
|
|
36
36
|
{
|
|
37
37
|
noTenants: true,
|
|
@@ -144,7 +144,7 @@ describe Descope::Api::V1::Management::Audit do
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
it 'is expected to create an audit event' do
|
|
147
|
-
expect(@instance).to receive(:
|
|
147
|
+
expect(@instance).to receive(:mgmt_post).with(
|
|
148
148
|
'/v1/mgmt/audit/event',
|
|
149
149
|
{
|
|
150
150
|
action: 'get',
|