stormpath-sdk 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +12 -0
- data/lib/stormpath-sdk.rb +4 -0
- data/lib/stormpath-sdk/data_store.rb +172 -166
- data/lib/stormpath-sdk/provider/account_request.rb +5 -6
- data/lib/stormpath-sdk/provider/account_resolver.rb +27 -12
- data/lib/stormpath-sdk/provider/twitter/twitter_provider.rb +18 -0
- data/lib/stormpath-sdk/provider/twitter/twitter_provider_data.rb +18 -0
- data/lib/stormpath-sdk/resource/account_linking_policy.rb +21 -0
- data/lib/stormpath-sdk/resource/application.rb +7 -5
- data/lib/stormpath-sdk/resource/application_web_config.rb +9 -0
- data/lib/stormpath-sdk/resource/field.rb +2 -1
- data/lib/stormpath-sdk/resource/organization.rb +2 -1
- data/lib/stormpath-sdk/version.rb +2 -2
- data/spec/auth/basic_authenticator_spec.rb +100 -13
- data/spec/provider/account_resolver_spec.rb +34 -10
- data/spec/provider/provider_spec.rb +21 -6
- data/spec/resource/account_linking_policy_spec.rb +31 -0
- data/spec/resource/application_spec.rb +44 -2
- data/spec/resource/application_web_config_spec.rb +65 -0
- data/spec/resource/collection_spec.rb +2 -2
- data/spec/resource/directory_spec.rb +7 -0
- data/spec/resource/organization_spec.rb +1 -0
- data/spec/resource/schema_spec.rb +10 -2
- data/spec/spec_helper.rb +1 -0
- data/spec/support/custom_data_save_period.rb +12 -0
- data/spec/support/mocked_provider_accounts.rb +30 -0
- metadata +9 -2
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright
|
2
|
+
# Copyright 2016 Stormpath, Inc.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -16,15 +16,14 @@
|
|
16
16
|
module Stormpath
|
17
17
|
module Provider
|
18
18
|
class AccountRequest
|
19
|
+
attr_accessor :provider, :token_type, :token_value, :account_store
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
def initialize(provider, token_type, token_value)
|
21
|
+
def initialize(provider, token_type, token_value, account_store: {})
|
23
22
|
@provider = provider
|
24
23
|
@token_type = token_type
|
25
24
|
@token_value = token_value
|
25
|
+
@account_store = account_store
|
26
26
|
end
|
27
|
-
|
28
27
|
end
|
29
28
|
end
|
30
|
-
end
|
29
|
+
end
|
@@ -17,27 +17,42 @@ module Stormpath
|
|
17
17
|
module Provider
|
18
18
|
class AccountResolver
|
19
19
|
include Stormpath::Util::Assert
|
20
|
+
attr_reader :data_store, :parent_href, :request
|
20
21
|
|
21
|
-
def initialize
|
22
|
+
def initialize(data_store, parent_href, request)
|
22
23
|
@data_store = data_store
|
24
|
+
@parent_href = parent_href
|
25
|
+
@request = request
|
26
|
+
assert_not_nil(parent_href, 'parent_href argument must be specified')
|
27
|
+
assert_kind_of(AccountRequest, request, "Only #{AccountRequest} instances are supported.")
|
23
28
|
end
|
24
29
|
|
25
|
-
def resolve_provider_account
|
26
|
-
|
27
|
-
|
30
|
+
def resolve_provider_account
|
31
|
+
attempt.provider_data = provider_data
|
32
|
+
data_store.create(href, attempt, Stormpath::Provider::AccountResult)
|
33
|
+
end
|
28
34
|
|
29
|
-
|
35
|
+
def provider_data
|
36
|
+
@provider_data ||= {}.tap do |body|
|
37
|
+
body[request.token_type.to_s.camelize(:lower)] = request.token_value
|
38
|
+
body['providerId'] = request.provider
|
39
|
+
body['accountStore'] = request_account_store_hash if request.account_store.present?
|
40
|
+
end
|
41
|
+
end
|
30
42
|
|
31
|
-
|
32
|
-
request.token_type.to_s.camelize(:lower) => request.token_value,
|
33
|
-
"providerId" => request.provider
|
34
|
-
}
|
43
|
+
private
|
35
44
|
|
36
|
-
|
45
|
+
def attempt
|
46
|
+
@attempt ||= data_store.instantiate(AccountAccess)
|
47
|
+
end
|
37
48
|
|
38
|
-
|
49
|
+
def href
|
50
|
+
"#{parent_href}/accounts"
|
39
51
|
end
|
40
52
|
|
53
|
+
def request_account_store_hash
|
54
|
+
request.account_store.transform_keys { |key| key.to_s.camelize(:lower) }
|
55
|
+
end
|
41
56
|
end
|
42
57
|
end
|
43
|
-
end
|
58
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2016 Stormpath, Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
class Stormpath::Provider::TwitterProvider < Stormpath::Provider::Provider
|
17
|
+
prop_reader :client_id, :client_secret
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2016 Stormpath, Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
class Stormpath::Provider::TwitterProviderData < Stormpath::Provider::ProviderData
|
17
|
+
prop_reader :access_token
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2016 Stormpath, Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
class Stormpath::Resource::AccountLinkingPolicy < Stormpath::Resource::Instance
|
17
|
+
prop_accessor :status, :automatic_provisioning, :matching_property
|
18
|
+
prop_reader :created_at, :modified_at
|
19
|
+
|
20
|
+
belongs_to :tenant
|
21
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright
|
2
|
+
# Copyright 2016 Stormpath, Inc.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -20,7 +20,7 @@ class Stormpath::Resource::Application < Stormpath::Resource::Instance
|
|
20
20
|
|
21
21
|
class LoadError < ArgumentError; end
|
22
22
|
|
23
|
-
prop_accessor :name, :description, :authorized_callback_uris, :status
|
23
|
+
prop_accessor :name, :description, :authorized_callback_uris, :status, :authorized_origin_uris
|
24
24
|
prop_reader :created_at, :modified_at
|
25
25
|
|
26
26
|
belongs_to :tenant
|
@@ -36,6 +36,8 @@ class Stormpath::Resource::Application < Stormpath::Resource::Instance
|
|
36
36
|
has_one :default_group_store_mapping, class_name: :accountStoreMapping
|
37
37
|
has_one :custom_data
|
38
38
|
has_one :o_auth_policy, class_name: :oauthPolicy
|
39
|
+
has_one :web_config, class_name: :applicationWebConfig
|
40
|
+
has_one :account_linking_policy
|
39
41
|
|
40
42
|
alias_method :oauth_policy, :o_auth_policy
|
41
43
|
|
@@ -105,8 +107,8 @@ class Stormpath::Resource::Application < Stormpath::Resource::Instance
|
|
105
107
|
Stormpath::Authentication::BasicAuthenticator.new(data_store).authenticate(href, request)
|
106
108
|
end
|
107
109
|
|
108
|
-
def get_provider_account
|
109
|
-
Stormpath::Provider::AccountResolver.new(data_store
|
110
|
+
def get_provider_account(request)
|
111
|
+
Stormpath::Provider::AccountResolver.new(data_store, href, request).resolve_provider_account
|
110
112
|
end
|
111
113
|
|
112
114
|
def authenticate_oauth(request)
|
@@ -151,7 +153,7 @@ class Stormpath::Resource::Application < Stormpath::Resource::Instance
|
|
151
153
|
when Hash
|
152
154
|
account_store
|
153
155
|
else
|
154
|
-
|
156
|
+
raise ArgumentError, 'Account store has to be passed either as an resource or a hash'
|
155
157
|
end
|
156
158
|
end
|
157
159
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class Stormpath::Resource::ApplicationWebConfig < Stormpath::Resource::Instance
|
2
|
+
ENDPOINTS = [:oauth2, :register, :login, :verify_email, :forgot_password, :change_password, :me].freeze
|
3
|
+
prop_accessor :dns_label, :status, *ENDPOINTS
|
4
|
+
prop_reader :domain_name, :created_at, :modified_at
|
5
|
+
|
6
|
+
has_one :signing_api_key, class_name: :api_key
|
7
|
+
belongs_to :application
|
8
|
+
belongs_to :tenant
|
9
|
+
end
|
@@ -14,7 +14,8 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
class Stormpath::Resource::Field < Stormpath::Resource::Instance
|
17
|
-
|
17
|
+
prop_accessor :required
|
18
|
+
prop_reader :name, :created_at, :modified_at
|
18
19
|
|
19
20
|
belongs_to :schema
|
20
21
|
end
|
@@ -2,7 +2,7 @@ class Stormpath::Resource::Organization < Stormpath::Resource::Instance
|
|
2
2
|
include Stormpath::Resource::CustomDataStorage
|
3
3
|
|
4
4
|
prop_accessor :name, :description, :name_key, :status, :account_store_mappings,
|
5
|
-
|
5
|
+
:default_account_store_mapping, :default_group_store_mapping
|
6
6
|
|
7
7
|
prop_reader :created_at, :modified_at
|
8
8
|
|
@@ -11,4 +11,5 @@ class Stormpath::Resource::Organization < Stormpath::Resource::Instance
|
|
11
11
|
belongs_to :tenant
|
12
12
|
|
13
13
|
has_one :custom_data
|
14
|
+
has_one :account_linking_policy
|
14
15
|
end
|
@@ -1,25 +1,112 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
4
|
-
|
3
|
+
describe 'BasicAuthenticator', vcr: true do
|
4
|
+
let(:application) { test_api_client.applications.create(application_attrs) }
|
5
|
+
let(:directory) { test_api_client.directories.create(directory_attrs) }
|
6
|
+
let(:directory2) { test_api_client.directories.create(directory_attrs) }
|
7
|
+
let(:organization) { test_api_client.organizations.create(organization_attrs) }
|
8
|
+
let(:authenticator) do
|
9
|
+
Stormpath::Authentication::BasicAuthenticator.new(test_api_client.data_store)
|
10
|
+
end
|
11
|
+
let(:password) { 'F00barfoo' }
|
12
|
+
let(:invalid_password) { 'Wr00ngPassw0rd' }
|
13
|
+
let(:dir_account) do
|
14
|
+
directory.accounts.create(account_attrs(username: 'ruby_cilim_dir', password: password))
|
15
|
+
end
|
16
|
+
let(:org_account) do
|
17
|
+
organization.accounts.create(account_attrs(username: 'ruby_cilim_org', password: password))
|
18
|
+
end
|
19
|
+
let(:request) do
|
20
|
+
Stormpath::Authentication::UsernamePasswordRequest.new(account.username,
|
21
|
+
password,
|
22
|
+
account_store: account_store)
|
23
|
+
end
|
24
|
+
let(:authenticate) { authenticator.authenticate(application.href, request) }
|
25
|
+
|
26
|
+
before do
|
27
|
+
map_account_store(application, directory, 0, true, true)
|
28
|
+
map_account_store(application, organization, 1, false, false)
|
29
|
+
map_organization_store(directory, organization, true)
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
application.delete
|
34
|
+
directory.delete
|
35
|
+
organization.delete
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_examples 'an AuthenticationResult' do
|
39
|
+
it 'is an AuthenticationResult' do
|
40
|
+
expect(authenticate).to be_kind_of Stormpath::Authentication::AuthenticationResult
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'has an account' do
|
44
|
+
expect(authenticate.account.email).to eq account.email
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
shared_examples 'an invalid username or password error' do
|
49
|
+
it 'raises a Stormpath::Error' do
|
50
|
+
expect { authenticate }.to raise_error(Stormpath::Error, 'Invalid username or password.')
|
51
|
+
end
|
52
|
+
end
|
5
53
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
auth_result = Stormpath::Authentication::AuthenticationResult.new({}, test_api_client)
|
10
|
-
allow(data_store).to receive(:create).and_return(auth_result)
|
54
|
+
context 'authenticate without account store' do
|
55
|
+
let(:account) { dir_account }
|
56
|
+
let(:account_store) { nil }
|
11
57
|
|
12
|
-
|
58
|
+
context 'successful authentication' do
|
59
|
+
it_should_behave_like 'an AuthenticationResult'
|
13
60
|
end
|
14
61
|
|
15
|
-
context
|
16
|
-
|
17
|
-
|
62
|
+
context 'wrong password' do
|
63
|
+
let(:request) do
|
64
|
+
Stormpath::Authentication::UsernamePasswordRequest.new(org_account.username,
|
65
|
+
invalid_password)
|
18
66
|
end
|
19
67
|
|
20
|
-
|
21
|
-
|
68
|
+
it_behaves_like 'an invalid username or password error'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'authenticate with account store' do
|
73
|
+
let(:account) { org_account }
|
74
|
+
let(:account_store) { organization }
|
75
|
+
|
76
|
+
context 'successful authentication' do
|
77
|
+
let(:request) do
|
78
|
+
Stormpath::Authentication::UsernamePasswordRequest.new(org_account.username,
|
79
|
+
password,
|
80
|
+
account_store: organization)
|
81
|
+
end
|
82
|
+
|
83
|
+
it_should_behave_like 'an AuthenticationResult'
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'wrong password' do
|
87
|
+
let(:request) do
|
88
|
+
Stormpath::Authentication::UsernamePasswordRequest.new(org_account.username,
|
89
|
+
invalid_password,
|
90
|
+
account_store: organization)
|
22
91
|
end
|
92
|
+
|
93
|
+
it_behaves_like 'an invalid username or password error'
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'account not in account store' do
|
97
|
+
before { map_account_store(application, directory2, 1, false, false) }
|
98
|
+
after { directory2.delete }
|
99
|
+
|
100
|
+
let(:another_account) do
|
101
|
+
directory2.accounts.create(account_attrs(username: 'ruby-dir-acc', password: password))
|
102
|
+
end
|
103
|
+
let(:request) do
|
104
|
+
Stormpath::Authentication::UsernamePasswordRequest.new(another_account.username,
|
105
|
+
password,
|
106
|
+
account_store: organization)
|
107
|
+
end
|
108
|
+
|
109
|
+
it_behaves_like 'an invalid username or password error'
|
23
110
|
end
|
24
111
|
end
|
25
112
|
end
|
@@ -1,24 +1,48 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
4
|
-
context
|
3
|
+
describe 'ProviderAccountResolver' do
|
4
|
+
context 'given an instance of ProviderAccountResolver' do
|
5
|
+
let(:data_store) { Stormpath::DataStore.new('', '', {}, '') }
|
6
|
+
let(:provider_account_resolver) do
|
7
|
+
Stormpath::Provider::AccountResolver.new(data_store, 'foo/bar', account_request)
|
8
|
+
end
|
9
|
+
let(:response) do
|
10
|
+
provider_account_resolver.resolve_provider_account
|
11
|
+
end
|
5
12
|
|
6
13
|
before do
|
7
|
-
data_store = Stormpath::DataStore.new "", "", {}, ""
|
8
14
|
allow(test_api_client).to receive(:data_store).and_return(data_store)
|
9
15
|
auth_result = Stormpath::Provider::AccountResult.new({}, test_api_client)
|
10
16
|
allow(data_store).to receive(:create).and_return(auth_result)
|
11
|
-
|
12
|
-
@provider_account_resolver = Stormpath::Provider::AccountResolver.new data_store
|
17
|
+
provider_account_resolver
|
13
18
|
end
|
14
19
|
|
15
|
-
context
|
16
|
-
|
17
|
-
|
20
|
+
context 'when integrating' do
|
21
|
+
context 'without an account store' do
|
22
|
+
let(:account_request) do
|
23
|
+
Stormpath::Provider::AccountRequest.new(:facebook, :access_token, 'some-token')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'a ProviderResult is returned' do
|
27
|
+
expect(response).to be_a Stormpath::Provider::AccountResult
|
28
|
+
end
|
18
29
|
end
|
19
30
|
|
20
|
-
|
21
|
-
|
31
|
+
context 'with account store as a parameter' do
|
32
|
+
let(:account_request) do
|
33
|
+
Stormpath::Provider::AccountRequest.new(:facebook,
|
34
|
+
:access_token,
|
35
|
+
'some-token',
|
36
|
+
account_store: { name_key: 'app1' })
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'a ProviderResult is returned' do
|
40
|
+
expect(response).to be_a Stormpath::Provider::AccountResult
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should have account_store parameter' do
|
44
|
+
expect(provider_account_resolver.provider_data.keys).to include 'accountStore'
|
45
|
+
end
|
22
46
|
end
|
23
47
|
end
|
24
48
|
end
|
@@ -41,7 +41,7 @@ describe Stormpath::Provider::Provider, :vcr do
|
|
41
41
|
provider_clazz = "Stormpath::Provider::#{provider_id.capitalize}Provider".constantize
|
42
42
|
expect(provider).to be_instance_of(provider_clazz)
|
43
43
|
|
44
|
-
if
|
44
|
+
if %w(google facebook twitter).include?(provider_id)
|
45
45
|
expect(provider.client_id).to eq(client_id)
|
46
46
|
expect(provider.client_secret).to eq(client_secret)
|
47
47
|
end
|
@@ -52,7 +52,7 @@ describe Stormpath::Provider::Provider, :vcr do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
shared_examples 'a
|
55
|
+
shared_examples 'a synchronizable directory' do
|
56
56
|
it 'should be able to store provider accounts' do
|
57
57
|
account_store_mapping
|
58
58
|
|
@@ -118,7 +118,7 @@ describe Stormpath::Provider::Provider, :vcr do
|
|
118
118
|
end
|
119
119
|
|
120
120
|
it_behaves_like 'a provider directory'
|
121
|
-
it_behaves_like 'a
|
121
|
+
it_behaves_like 'a synchronizable directory'
|
122
122
|
end
|
123
123
|
|
124
124
|
describe 'create google directory with provider credentials' do
|
@@ -139,7 +139,7 @@ describe Stormpath::Provider::Provider, :vcr do
|
|
139
139
|
end
|
140
140
|
|
141
141
|
it_behaves_like 'a provider directory'
|
142
|
-
it_behaves_like 'a
|
142
|
+
it_behaves_like 'a synchronizable directory'
|
143
143
|
end
|
144
144
|
|
145
145
|
describe 'create linkedin directory with provider credentials' do
|
@@ -154,7 +154,7 @@ describe Stormpath::Provider::Provider, :vcr do
|
|
154
154
|
end
|
155
155
|
|
156
156
|
it_behaves_like 'a provider directory'
|
157
|
-
it_behaves_like 'a
|
157
|
+
it_behaves_like 'a synchronizable directory'
|
158
158
|
end
|
159
159
|
|
160
160
|
describe 'create github directory with provider credentials' do
|
@@ -169,6 +169,21 @@ describe Stormpath::Provider::Provider, :vcr do
|
|
169
169
|
end
|
170
170
|
|
171
171
|
it_behaves_like 'a provider directory'
|
172
|
-
it_behaves_like 'a
|
172
|
+
it_behaves_like 'a synchronizable directory'
|
173
|
+
end
|
174
|
+
|
175
|
+
describe 'create twitter directory with provider credentials' do
|
176
|
+
let(:name) { 'Twitter' }
|
177
|
+
let(:description) { 'Directory for testing Twitter directories.' }
|
178
|
+
|
179
|
+
let(:provider_id) { 'twitter' }
|
180
|
+
let(:client_id) { 'TWITTER_APP_ID' }
|
181
|
+
let(:client_secret) { 'TWITTER_APP_SECRET' }
|
182
|
+
let(:provider_info) do
|
183
|
+
{ provider_id: provider_id, client_id: client_id, client_secret: client_secret }
|
184
|
+
end
|
185
|
+
|
186
|
+
it_behaves_like 'a provider directory'
|
187
|
+
it_behaves_like 'a synchronizable directory'
|
173
188
|
end
|
174
189
|
end
|