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.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2014 Stormpath, Inc.
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
- attr_accessor :provider, :token_type, :token_value
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 data_store
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 parent_href, request
26
- assert_not_nil parent_href, "parent_href argument must be specified"
27
- assert_kind_of AccountRequest, request, "Only #{AccountRequest} instances are supported."
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
- attempt = @data_store.instantiate AccountAccess
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
- attempt.provider_data = {
32
- request.token_type.to_s.camelize(:lower) => request.token_value,
33
- "providerId" => request.provider
34
- }
43
+ private
35
44
 
36
- href = parent_href + '/accounts'
45
+ def attempt
46
+ @attempt ||= data_store.instantiate(AccountAccess)
47
+ end
37
48
 
38
- @data_store.create href, attempt, Stormpath::Provider::AccountResult
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 2012 Stormpath, Inc.
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 request
109
- Stormpath::Provider::AccountResolver.new(data_store).resolve_provider_account(href, request)
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
- fail ArgumentError, 'Account store has to be passed either as an resource or a hash'
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
- prop_reader :name, :required, :created_at, :modified_at
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
- :default_account_store_mapping, :default_group_store_mapping
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
@@ -14,6 +14,6 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module Stormpath
17
- VERSION = '1.4.0'
18
- VERSION_DATE = '2016-11-22'
17
+ VERSION = '1.5.0'
18
+ VERSION_DATE = '2017-01-24'
19
19
  end
@@ -1,25 +1,112 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "BasicAuthenticator" do
4
- context "given an instance of BasicAuthenticator" do
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
- before do
7
- data_store = Stormpath::DataStore.new "", "", {}, ""
8
- allow(test_api_client).to receive(:data_store).and_return(data_store)
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
- @basic_authenticator = Stormpath::Authentication::BasicAuthenticator.new data_store
58
+ context 'successful authentication' do
59
+ it_should_behave_like 'an AuthenticationResult'
13
60
  end
14
61
 
15
- context "when authenticating" do
16
- before do
17
- @response = @basic_authenticator.authenticate "foo/bar", Stormpath::Authentication::UsernamePasswordRequest.new("fake-username", "fake-password")
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
- it "an AuthenticationResult is returned" do
21
- expect(@response).to be_a Stormpath::Authentication::AuthenticationResult
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 "ProviderAccountResolver" do
4
- context "given an instance of ProviderAccountResolver" do
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 "when integrating" do
16
- before do
17
- @response = @provider_account_resolver.resolve_provider_account "foo/bar", Stormpath::Provider::AccountRequest.new(:facebook, :access_token, "some-token")
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
- it "an ProviderResult is returned" do
21
- expect(@response).to be_a Stormpath::Provider::AccountResult
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 provider_id == 'google' || provider_id == 'facebook'
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 syncrhonizeable directory' do
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 syncrhonizeable directory'
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 syncrhonizeable directory'
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 syncrhonizeable directory'
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 syncrhonizeable directory'
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