stormpath-sdk 1.0.0.beta.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ffd1ce3b14483c0ba0f564a704d5e7d6d079a99e
4
- data.tar.gz: b59460eaea481ca8e83d4a4bf4accb8728481554
3
+ metadata.gz: b211d2e9b37f2df77845364b2739620106da3820
4
+ data.tar.gz: 610cf6e36422987c042c6f60cb591427eee156c7
5
5
  SHA512:
6
- metadata.gz: 6fa8d912dd6323c2dcfe19d08bbc7a2efce1c8eea0d74ab2f34dfbbb1d891e78126ee4022045bc4d39faae429a98784ec9cfdd479cd920eafca443271cd10d32
7
- data.tar.gz: 08e1230337d60e8e55026ca367ac2103cd9c5414a1cc075301384d8550b78a1b2f2f35ea6001bd60296e03e2715582fc3fff77c516fb04924aece923a15a70ce
6
+ metadata.gz: 21b0d6b070b40ef895ad054eb49d31d254338ccc16e8c6dd62b7365a93d9de004c1497494e2f4168e6956fb8a1fa30ee1237e51a0207884efd5f92a57703fcfc
7
+ data.tar.gz: 2aedc019f6b3b03fa385962a914dd9d31dd99e9a24dd40e9547f8334b1cc5a0780d88c2d91f3969ea35c05b4c8928d7784e657399a956e96fd87d87960f1e45c
@@ -5,6 +5,8 @@ rvm:
5
5
  - 2.1.2
6
6
  services:
7
7
  - redis-server
8
+ before_install:
9
+ - gem install bundler
8
10
  env:
9
11
  global:
10
12
  - secure: MeyjZGgySZvpYVRm2i7wkrhvu9KUeU7eQ5aKj7FXqIYlQe3T67k+KKA3Ejp6WC3wUEVPUFseWMZCuNkRc0UeGA5/4e4LLc++XuaZn1jLTm1yNofh/CFAAjt5VdIYz4R7sKxpyLxs0orK2+HcaiCoqSDj7ehw2SlDca9VEi0GoQk=
data/README.md CHANGED
@@ -292,6 +292,44 @@ end
292
292
 
293
293
  The application will be an instance of your application. callback_uri is a url with which you want to handle the ID Site information, this url also needs to be set in the Stormpath’s dashboard on [ID Site settings page](https://api.stormpath.com/ui2/index.html#/id-site) as Authorized Redirect URLs.
294
294
 
295
+ ##### Using ID Site for [multitenancy][id-site-multitenancy]
296
+
297
+ When a user wants to login to your application, you may want to specify an organization for the user to login to. Stormpath ID Site is configurable to support multitenancy with Organization resources
298
+
299
+ ```ruby
300
+ application.create_id_site_url({
301
+ callback_uri: 'https://trooperapp.com/callback',
302
+ organization_name_key: 'stormtrooper',
303
+ show_organization_field: true
304
+ });
305
+ ```
306
+
307
+ ##### Using Subdomains
308
+
309
+ In some cases, you may want to show the organization that the user is logging into as a subdomain instead of an form field. To configure this, you need to use a [wildcard certificate][wildcard-certificate] when setting up your [custom domain with ID Site][custom-domain-with-id-site]. Otherwise, the Stormpath infrastructure will cause browser SSL errors.
310
+
311
+ Once a wildcard certificate is configured on your domain, you can tell ID Site to use a subdomain to represent the organization:
312
+
313
+ ```ruby
314
+ application.create_id_site_url({
315
+ callback_uri: 'https://trooperapp.com/callback',
316
+ organization_name_key: 'stormtrooper',
317
+ use_subdomain: true
318
+ });
319
+ ```
320
+
321
+ ##### Specifying the Organization
322
+
323
+ In the case where you are using a subdomain to designate the organization, you can tell ID Site which organization the user is logging into to.
324
+
325
+ ```ruby
326
+ application.create_id_site_url({
327
+ callback_uri: 'https://trooperapp.com/callback',
328
+ organization_name_key: 'stormtrooper',
329
+ show_organization_field: true
330
+ })
331
+ ```
332
+
295
333
  #### Handle ID Site Callback
296
334
 
297
335
  For any request you make for ID Site, you need to specify a callback uri. To parse the information from the servers response and to decode the data from the JWT token you need to call the handle_id_site_callback method and pass the Request URI.
@@ -311,7 +349,7 @@ end
311
349
 
312
350
  There are a few other methods that you will need to concern yourself with when using ID Site. Logging out a User, Registering a User, and a User who has forgotten their password. These methods will use the same information from the login method but a few more items will need to be passed into the array. For example if you have a sinatra application.
313
351
 
314
- Logging Out a User
352
+ ##### Logging Out a User
315
353
  ```ruby
316
354
  app.get ‘/logout' do
317
355
  user_data = application.handle_id_site_callback(request.url)
@@ -319,7 +357,7 @@ app.get ‘/logout' do
319
357
  end
320
358
  ```
321
359
 
322
- Registering a User
360
+ ##### Registering a User
323
361
  ```ruby
324
362
  app.get ‘/register' do
325
363
  user_data = application.handle_id_site_callback(request.url)
@@ -327,7 +365,7 @@ app.get ‘/register' do
327
365
  end
328
366
  ```
329
367
 
330
- Forgot Link
368
+ ##### Forgot Link
331
369
  ```ruby
332
370
  app.get ‘/forgot' do
333
371
  user_data = application.handle_id_site_callback(request.url)
@@ -340,6 +378,18 @@ Again, with all these methods, You will want your application to link to an inte
340
378
  > NOTE:
341
379
  > A JWT will expire after 60 seconds of creation.
342
380
 
381
+ #### Exchange ID Site token for a Stormpath Access Token
382
+ After the user has been authenticated via ID Site, a developer may want to control their authorization with an OAuth 2.0 Token.
383
+ This is done by passing the JWT similar to the way we passed the user’s credentials as described in [Generating an OAuth 2.0 Access Token][generate-oauth-access-token].
384
+ The difference is that instead of using the password grant type and passing credentials, we will use the id_site_token type and pass the JWT we got from the ID Site
385
+ more info [here][exchange-id-site-token].
386
+
387
+ To exchange ID Site token for the oauth token use the following snippet
388
+ ```ruby
389
+ grant_request = Stormpath::Oauth::IdSiteGrantRequest.new jwt_token
390
+ response = application.authenticate_oauth grant_request
391
+ ```
392
+
343
393
  ### Registering Accounts
344
394
 
345
395
  Accounts are created on a directory instance. They can be created in two
@@ -399,6 +449,21 @@ get '/users/verify' do
399
449
  end
400
450
  ```
401
451
 
452
+ #### Create an Account with an Existing Password Hash
453
+
454
+ If you are moving from an existing user repository to Stormpath, you may have existing password hashes that you want to reuse to provide a seamless upgrade path for your end users.
455
+ More info about this feature can be found [here][mcf-hash-password-doc]
456
+
457
+ Example of creating an account with existing SHA-512 password hash. For details on other hashing algorithms chech the [documentation][stormpaht-hash-algorithm]
458
+
459
+ directory.accounts.create({
460
+ username: "jlucpicard",
461
+ email: "captain@enterprise.com",
462
+ given_name: "Jean-Luc",
463
+ surname: "Picard",
464
+ password: "$stormpath2$SHA-512$1$ZFhBRmpFSnEwVEx2ekhKS0JTMDJBNTNmcg==$Q+sGFg9e+pe9QsUdfnbJUMDtrQNf27ezTnnGllBVkQpMRc9bqH6WkyE3y0svD/7cBk8uJW9Wb3dolWwDtDLFjg=="
465
+ }, password_format: 'mcf')
466
+
402
467
  ### Authentication
403
468
 
404
469
  Authentication is accomplished by passing a username or an email and a
@@ -457,6 +522,19 @@ With the account acquired you can then update the password:
457
522
  _NOTE :_ Confirming a new password is left up to the web application
458
523
  code calling the Stormpath SDK. The SDK does not require confirmation.
459
524
 
525
+ ### Social Providers
526
+
527
+ To access or create an account in an already created social Directory (facebook, google, github, linkedin),
528
+ it is required to gather Authorization Code on behalf of the user. This requires leveraging Oauth 2.0
529
+ protocol and the user's consent for your applications permissions. Once you have the access_token you can
530
+ access the account via get_provider_account method.
531
+
532
+ ```ruby
533
+ provider = ‘facebook’ # can also be google, github, linkedin
534
+ request = Stormpath::Provider::AccountRequest.new(provider, :access_token, access_token)
535
+ application.get_provider_account(request)
536
+ ```
537
+
460
538
  ### ACL through Groups
461
539
 
462
540
  Memberships of accounts in certain groups can be used as an
@@ -641,3 +719,10 @@ For additional information, please see the full [Project Documentation](https://
641
719
  [stormpath-admin-login]: http://api.stormpath.com/login
642
720
  [create-api-keys]: http://www.stormpath.com/docs/ruby/product-guide#AssignAPIkeys
643
721
  [concepts]: http://www.stormpath.com/docs/stormpath-basics#keyConcepts
722
+ [exchange-id-site-token]: https://docs.stormpath.com/rest/product-guide/latest/008_idsite.html#exchanging-the-id-site-jwt-for-an-oauth-token
723
+ [generate-oauth-access-token]: https://docs.stormpath.com/rest/product-guide/latest/005_auth_n.html#generate-oauth-token
724
+ [mcf-hash-password-doc]: http://docs.stormpath.com/rest/product-guide/latest/004_accnt_mgmt.html#importing-accounts-with-mcf-hash-passwords
725
+ [stormpath-hash-algorithm]: http://docs.stormpath.com/rest/product-guide/latest/004_accnt_mgmt.html#the-stormpath2-hashing-algorithm
726
+ [wildcard-certificate]: https://en.wikipedia.org/wiki/Wildcard_certificate
727
+ [custom-domain-with-id-site]: https://docs.stormpath.com/guides/using-id-site/#setting-your-own-custom-domain-name-and-ssl-certificate
728
+ [id-site-multitenancy]: https://docs.stormpath.com/guides/using-id-site/#using-id-site-for-multitenancy
@@ -52,6 +52,7 @@ module Stormpath
52
52
  autoload :AccessToken, 'stormpath-sdk/resource/access_token'
53
53
  autoload :Organization, 'stormpath-sdk/resource/organization'
54
54
  autoload :OrganizationAccountStoreMapping, 'stormpath-sdk/resource/organization_account_store_mapping'
55
+ autoload :AccountOverrides, 'stormpath-sdk/resource/account_overrides'
55
56
  end
56
57
 
57
58
  module Cache
@@ -82,6 +83,10 @@ module Stormpath
82
83
  autoload :FacebookProviderData, 'stormpath-sdk/provider/facebook/facebook_provider_data'
83
84
  autoload :GoogleProvider, 'stormpath-sdk/provider/google/google_provider'
84
85
  autoload :GoogleProviderData, 'stormpath-sdk/provider/google/google_provider_data'
86
+ autoload :LinkedinProvider, 'stormpath-sdk/provider/linkedin/linkedin_provider'
87
+ autoload :LinkedinProviderData, 'stormpath-sdk/provider/linkedin/linkedin_provider_data'
88
+ autoload :GithubProvider, 'stormpath-sdk/provider/github/github_provider'
89
+ autoload :GithubProviderData, 'stormpath-sdk/provider/github/github_provider_data'
85
90
  autoload :StormpathProvider, 'stormpath-sdk/provider/stormpath/stormpath_provider'
86
91
  autoload :StormpathProviderData, 'stormpath-sdk/provider/stormpath/stormpath_provider_data'
87
92
  end
@@ -110,5 +115,7 @@ module Stormpath
110
115
  autoload :RefreshGrantRequest, "stormpath-sdk/oauth/refresh_grant_request"
111
116
  autoload :VerifyAccessToken, "stormpath-sdk/oauth/verify_access_token"
112
117
  autoload :VerifyToken, "stormpath-sdk/oauth/verify_token"
118
+ autoload :IdSiteGrantRequest, "stormpath-sdk/oauth/id_site_grant_request"
119
+ autoload :IdSiteGrant, "stormpath-sdk/oauth/id_site_grant"
113
120
  end
114
121
  end
@@ -207,7 +207,7 @@ class Stormpath::DataStore
207
207
 
208
208
  def apply_default_request_headers(request)
209
209
  request.http_headers.store 'Accept', 'application/json'
210
- request.http_headers.store 'User-Agent', 'Stormpath-RubySDK/' + Stormpath::VERSION
210
+ apply_default_user_agent(request)
211
211
 
212
212
  if request.body and request.body.length > 0
213
213
  request.http_headers.store 'Content-Type', 'application/json'
@@ -216,7 +216,13 @@ class Stormpath::DataStore
216
216
 
217
217
  def apply_form_data_request_headers(request)
218
218
  request.http_headers.store 'Content-Type', 'application/x-www-form-urlencoded'
219
- request.http_headers.store 'User-Agent', 'Stormpath-RubySDK/' + Stormpath::VERSION
219
+ apply_default_user_agent(request)
220
+ end
221
+
222
+ def apply_default_user_agent(request)
223
+ request.http_headers.store 'User-Agent', 'stormpath-sdk-ruby/' + Stormpath::VERSION +
224
+ " ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" +
225
+ " " + Gem::Platform.local.os.to_s + "/" + Gem::Platform.local.version.to_s
220
226
  end
221
227
 
222
228
  def save_resource(href, resource, return_type)
@@ -14,6 +14,8 @@ module Stormpath
14
14
  attempt = @data_store.instantiate PasswordGrant
15
15
  elsif request.grant_type == 'refresh_token'
16
16
  attempt = @data_store.instantiate RefreshToken
17
+ elsif request.grant_type == 'id_site_token'
18
+ attempt = @data_store.instantiate IdSiteGrant
17
19
  end
18
20
 
19
21
  attempt.set_options(request)
@@ -0,0 +1,16 @@
1
+ module Stormpath
2
+ module Oauth
3
+ class IdSiteGrant < Stormpath::Resource::Base
4
+ prop_accessor :grant_type, :token
5
+
6
+ def set_options(request)
7
+ set_property :grant_type, request.grant_type
8
+ set_property :token, request.token
9
+ end
10
+
11
+ def form_data?
12
+ true
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ module Stormpath
2
+ module Oauth
3
+ class IdSiteGrantRequest
4
+ attr_accessor :grant_type, :token
5
+
6
+ def initialize(token)
7
+ @token = token
8
+ @grant_type = "id_site_token"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Copyright 2014 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::GithubProvider < Stormpath::Provider::Provider
17
+ prop_reader :client_id, :client_secret
18
+ end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Copyright 2014 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::GithubProviderData < Stormpath::Provider::ProviderData
17
+ prop_reader :access_token
18
+ end
@@ -0,0 +1,19 @@
1
+
2
+ #
3
+ # Copyright 2014 Stormpath, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ class Stormpath::Provider::LinkedinProvider < Stormpath::Provider::Provider
18
+ prop_reader :client_id, :client_secret
19
+ end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Copyright 2014 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::LinkedinProviderData < Stormpath::Provider::ProviderData
17
+ prop_reader :access_token
18
+ end
@@ -0,0 +1,22 @@
1
+ module Stormpath::Resource::AccountOverrides
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ def create_account account, registration_workflow_enabled=nil
6
+ href = accounts.href
7
+ if registration_workflow_enabled == false
8
+ href += "?registrationWorkflowEnabled=#{registration_workflow_enabled.to_s}"
9
+ end
10
+
11
+ resource = case account
12
+ when Stormpath::Resource::Base
13
+ account
14
+ else
15
+ Stormpath::Resource::Account.new account, client
16
+ end
17
+
18
+ resource.apply_custom_data_updates_if_necessary
19
+ data_store.create href, resource, Stormpath::Resource::Account
20
+ end
21
+ end
22
+ end
@@ -16,6 +16,7 @@
16
16
  class Stormpath::Resource::Application < Stormpath::Resource::Instance
17
17
  include Stormpath::Resource::Status
18
18
  include Stormpath::Resource::CustomDataStorage
19
+ include Stormpath::Resource::AccountOverrides
19
20
  include UUIDTools
20
21
 
21
22
  class LoadError < Stormpath::Error; end
@@ -62,16 +63,7 @@ class Stormpath::Resource::Application < Stormpath::Resource::Instance
62
63
  raise Stormpath::IdSite::Error.new(:jwt_cb_uri_incorrect)
63
64
  end
64
65
 
65
- token = JWT.encode({
66
- 'iat' => Time.now.to_i,
67
- 'jti' => UUID.method(:random_create).call.to_s,
68
- 'iss' => client.data_store.api_key.id,
69
- 'sub' => href,
70
- 'cb_uri' => options[:callback_uri],
71
- 'path' => options[:path] || '',
72
- 'state' => options[:state] || ''
73
- }, client.data_store.api_key.secret, 'HS256')
74
-
66
+ token = JWT.encode(jwt_token_payload(options), client.data_store.api_key.secret, 'HS256')
75
67
  base + '?jwtRequest=' + token
76
68
  end
77
69
 
@@ -122,6 +114,23 @@ class Stormpath::Resource::Application < Stormpath::Resource::Instance
122
114
 
123
115
  private
124
116
 
117
+ def jwt_token_payload(options)
118
+ payload = {
119
+ 'iat' => Time.now.to_i,
120
+ 'jti' => UUID.method(:random_create).call.to_s,
121
+ 'iss' => client.data_store.api_key.id,
122
+ 'sub' => href,
123
+ 'cb_uri' => options[:callback_uri],
124
+ 'path' => options[:path] || '',
125
+ 'state' => options[:state] || '',
126
+ }
127
+
128
+ payload["sof"] = options[:show_organization_field] if options[:show_organization_field]
129
+ payload["onk"] = options[:organization_name_key] if options[:organization_name_key]
130
+ payload["usd"] = options[:use_subdomain] if options[:use_subdomain]
131
+ payload
132
+ end
133
+
125
134
  def api_key_id
126
135
  client.data_store.api_key.id
127
136
  end
@@ -16,6 +16,7 @@
16
16
  class Stormpath::Resource::Directory < Stormpath::Resource::Instance
17
17
  include Stormpath::Resource::Status
18
18
  include Stormpath::Resource::CustomDataStorage
19
+ include Stormpath::Resource::AccountOverrides
19
20
 
20
21
  prop_accessor :name, :description
21
22
 
@@ -25,15 +26,6 @@ class Stormpath::Resource::Directory < Stormpath::Resource::Instance
25
26
  has_many :groups, can: [:get, :create]
26
27
  has_one :custom_data
27
28
 
28
- def create_account account, registration_workflow_enabled=nil
29
- href = accounts.href
30
- if registration_workflow_enabled == false
31
- href += "?registrationWorkflowEnabled=#{registration_workflow_enabled.to_s}"
32
- end
33
- account.apply_custom_data_updates_if_necessary
34
- data_store.create href, account, Stormpath::Resource::Account
35
- end
36
-
37
29
  def provider
38
30
  internal_instance = instance_variable_get "@_provider"
39
31
  return internal_instance if internal_instance
@@ -14,6 +14,6 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module Stormpath
17
- VERSION = '1.0.0.beta.9'
18
- VERSION_DATE = '2015-12-02'
17
+ VERSION = '1.0.0'
18
+ VERSION_DATE = '2016-01-03'
19
19
  end
@@ -148,4 +148,22 @@ describe Stormpath::DataStore do
148
148
  end
149
149
 
150
150
  end
151
+
152
+ context '#apply_default_user_agent' do
153
+ let(:request) do
154
+ Stormpath::Http::Request.new 'get', 'http://example.com/resources/abc123', nil, Hash.new, nil, test_api_key
155
+ end
156
+
157
+ before do
158
+ allow(Gem::Platform.local).to receive(:os) { "darwin" }
159
+ allow(Gem::Platform.local).to receive(:version) { "14" }
160
+
161
+ data_store.send(:apply_default_user_agent, request)
162
+ end
163
+
164
+ it 'adds User-Agent to header' do
165
+ expect(request.http_headers["User-Agent"]).to include("darwin")
166
+ expect(request.http_headers["User-Agent"]).to include("14")
167
+ end
168
+ end
151
169
  end
@@ -149,4 +149,34 @@ describe Stormpath::Provider::Provider, :vcr do
149
149
  it_behaves_like 'a provider directory'
150
150
  it_behaves_like 'a syncrhonizeable directory'
151
151
  end
152
+
153
+ describe 'create linkedin directory with provider credentials' do
154
+ let(:name) { random_directory_name('Linkedin') }
155
+ let(:description) { 'Directory for testing Linkedin directories.' }
156
+
157
+ let(:provider_id) { "linkedin" }
158
+ let(:client_id) { 'LINKEDIN_APP_ID' }
159
+ let(:client_secret) { 'LINKEDIN_APP_SECRET' }
160
+ let(:provider_info) do
161
+ { provider_id: provider_id, client_id: client_id, client_secret: client_secret }
162
+ end
163
+
164
+ it_behaves_like 'a provider directory'
165
+ it_behaves_like 'a syncrhonizeable directory'
166
+ end
167
+
168
+ describe 'create github directory with provider credentials' do
169
+ let(:name) { random_directory_name('Github') }
170
+ let(:description) { 'Directory for testing Github directories.' }
171
+
172
+ let(:provider_id) { "github" }
173
+ let(:client_id) { 'GITHUB_APP_ID' }
174
+ let(:client_secret) { 'GITHUB_APP_SECRET' }
175
+ let(:provider_info) do
176
+ { provider_id: provider_id, client_id: client_id, client_secret: client_secret }
177
+ end
178
+
179
+ it_behaves_like 'a provider directory'
180
+ it_behaves_like 'a syncrhonizeable directory'
181
+ end
152
182
  end
@@ -99,6 +99,37 @@ describe Stormpath::Resource::Application, :vcr do
99
99
 
100
100
  end
101
101
 
102
+
103
+ describe '#create_account' do
104
+ let(:account) do
105
+ Stormpath::Resource::Account.new({
106
+ email: random_email,
107
+ given_name: 'Ruby SDK',
108
+ password: 'P@$$w0rd',
109
+ surname: 'SDK',
110
+ username: random_user_name
111
+ })
112
+ end
113
+
114
+ context 'with registration workflow' do
115
+ it 'creates an account with worflow enabled' do
116
+ response = application.create_account account, true
117
+
118
+ expect(response).to be_kind_of Stormpath::Resource::Account
119
+ expect(response.email).to eq(account.email)
120
+ end
121
+ end
122
+
123
+ context 'without registration workflow' do
124
+ it 'creates an account with workflow disabled' do
125
+ response = application.create_account account
126
+
127
+ expect(response).to be_kind_of Stormpath::Resource::Account
128
+ expect(response.email).to eq(account.email)
129
+ end
130
+ end
131
+ end
132
+
102
133
  describe '#authenticate_account' do
103
134
  let(:account) do
104
135
  directory.accounts.create build_account(password: 'P@$$w0rd')
@@ -626,6 +657,30 @@ describe Stormpath::Resource::Application, :vcr do
626
657
  }.to raise_error(JWT::DecodeError)
627
658
  end
628
659
  end
660
+
661
+ context 'with show_organization_field key specified' do
662
+ let(:jwt_token) { JWT.encode({
663
+ 'iat' => Time.now.to_i,
664
+ 'aud' => test_api_key_id,
665
+ 'sub' => application.href,
666
+ 'path' => '',
667
+ 'state' => '',
668
+ 'isNewSub' => true,
669
+ 'status' => "REGISTERED",
670
+ 'organization_name_key' => 'stormtroopers',
671
+ 'usd' => true,
672
+ 'sof' => true
673
+ }, test_api_key_secret, 'HS256')
674
+ }
675
+
676
+ before do
677
+ @site_result = application.handle_id_site_callback(callback_uri_base + jwt_token)
678
+ end
679
+
680
+ it 'should return IdSiteResult object' do
681
+ expect(@site_result).to be_kind_of(Stormpath::IdSite::IdSiteResult)
682
+ end
683
+ end
629
684
  end
630
685
 
631
686
  describe '#authenticate_oauth' do
@@ -654,6 +709,44 @@ describe Stormpath::Resource::Application, :vcr do
654
709
  end
655
710
  end
656
711
 
712
+ context 'exchange id site token for access_token with invalid jwt' do
713
+ let(:invalid_jwt_token) { 'invalid_token' }
714
+
715
+ let(:id_site_grant_request) { Stormpath::Oauth::IdSiteGrantRequest.new invalid_jwt_token }
716
+ let(:authenticate_oauth) { application.authenticate_oauth(id_site_grant_request) }
717
+
718
+ it 'should raise invalid token error' do
719
+ expect {
720
+ authenticate_oauth
721
+ }.to raise_error(Stormpath::Error)
722
+ end
723
+ end
724
+
725
+ context 'echange id site token for access_token with valid jwt' do
726
+ let(:jwt_token) { JWT.encode({
727
+ 'iat' => Time.now.to_i,
728
+ 'jti' => UUID.method(:random_create).call.to_s,
729
+ 'iss' => test_api_client.data_store.api_key.id,
730
+ 'sub' => application.href,
731
+ 'cb_uri' => 'http://localhost:9292/redirect',
732
+ 'path' => '',
733
+ 'state' => ''
734
+ }, test_api_client.data_store.api_key.secret, 'HS256')
735
+ }
736
+
737
+ it 'should create a jwtRequest that is signed wit the client secret' do
738
+ allow(application.client.data_store).to receive(:create).and_return(Stormpath::Resource::AccessToken)
739
+ expect(application.client.data_store).to receive(:instantiate)
740
+ .with(Stormpath::Oauth::IdSiteGrant)
741
+ .and_return(Stormpath::Oauth::IdSiteGrant.new({}, application.client))
742
+
743
+ grant_request = Stormpath::Oauth::IdSiteGrantRequest.new jwt_token
744
+ response = application.authenticate_oauth(grant_request)
745
+
746
+ expect(response).to be(Stormpath::Resource::AccessToken)
747
+ end
748
+ end
749
+
657
750
  context 'refresh token' do
658
751
  let(:refresh_grant_request) { Stormpath::Oauth::RefreshGrantRequest.new aquire_token.refresh_token }
659
752
  let(:authenticate_oauth) { application.authenticate_oauth(refresh_grant_request) }
@@ -1,6 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Stormpath::Resource::Directory, :vcr do
4
+ def create_account_store_mapping(application, account_store, is_default_group_store=false)
5
+ test_api_client.account_store_mappings.create({
6
+ application: application,
7
+ account_store: account_store,
8
+ list_index: 0,
9
+ is_default_account_store: true,
10
+ is_default_group_store: is_default_group_store
11
+ })
12
+ end
4
13
 
5
14
  describe "instances should respond to attribute property methods" do
6
15
  let(:app) { test_api_client.applications.create name: random_application_name, description: 'Dummy desc.' }
@@ -35,7 +44,11 @@ describe Stormpath::Resource::Directory, :vcr do
35
44
  end
36
45
 
37
46
  describe 'directory_associations' do
38
- let(:directory) { test_directory }
47
+ let(:directory) { test_api_client.directories.create name: random_directory_name, description: 'description_for_some_test_directory' }
48
+
49
+ after do
50
+ directory.delete if directory
51
+ end
39
52
 
40
53
  context '#accounts' do
41
54
  let(:account) { directory.accounts.create build_account}
@@ -72,7 +85,7 @@ describe Stormpath::Resource::Directory, :vcr do
72
85
  end
73
86
 
74
87
  describe '#create_account' do
75
- let(:directory) { test_directory }
88
+ let(:directory) { test_api_client.directories.create name: random_directory_name, description: 'description_for_some_test_directory' }
76
89
 
77
90
  let(:account) do
78
91
  Stormpath::Resource::Account.new({
@@ -84,6 +97,10 @@ describe Stormpath::Resource::Directory, :vcr do
84
97
  })
85
98
  end
86
99
 
100
+ after do
101
+ directory.delete if directory
102
+ end
103
+
87
104
  context 'without registration workflow' do
88
105
 
89
106
  let(:created_account) { directory.create_account account }
@@ -134,6 +151,106 @@ describe Stormpath::Resource::Directory, :vcr do
134
151
  expect(created_account_with_reg_workflow.email_verification_token).not_to be
135
152
  end
136
153
  end
154
+ end
155
+
156
+ describe 'create account with password import MCF feature' do
157
+ let(:app) { test_api_client.applications.create name: random_application_name, description: 'Dummy desc.' }
158
+ let(:application) { test_api_client.applications.get app.href }
159
+ let(:directory) { test_api_client.directories.create name: random_directory_name, description: 'description_for_some_test_directory' }
160
+ let!(:account_store_mapping) {create_account_store_mapping(application,directory,true)}
161
+
162
+ after do
163
+ application.delete if application
164
+ directory.delete if directory
165
+ @account.delete if @account
166
+ end
167
+
168
+ context "MD5 hashing algorithm" do
169
+ before do
170
+ account_store_mapping
171
+ @account = directory.accounts.create({
172
+ username: "jlucpicard",
173
+ email: "captain@enterprise.com",
174
+ given_name: "Jean-Luc",
175
+ surname: "Picard",
176
+ password: "$stormpath2$MD5$1$OGYyMmM5YzVlMDEwODEwZTg3MzM4ZTA2YjljZjMxYmE=$EuFAr2NTM83PrizVAYuOvw=="
177
+ }, password_format: 'mcf')
178
+ end
179
+
180
+ it 'creates an account' do
181
+ expect(@account).to be_a Stormpath::Resource::Account
182
+ expect(@account.username).to eq("jlucpicard")
183
+ expect(@account.email).to eq("captain@enterprise.com")
184
+ expect(@account.given_name).to eq("Jean-Luc")
185
+ expect(@account.surname).to eq("Picard")
186
+ end
187
+
188
+ it 'can authenticate with the account credentials' do
189
+ auth_request = Stormpath::Authentication::UsernamePasswordRequest.new 'jlucpicard', 'qwerty'
190
+ auth_result = application.authenticate_account auth_request
191
+
192
+ expect(auth_result).to be_a Stormpath::Authentication::AuthenticationResult
193
+ expect(auth_result.account).to be_a Stormpath::Resource::Account
194
+ expect(auth_result.account.email).to eq("captain@enterprise.com")
195
+ expect(auth_result.account.given_name).to eq("Jean-Luc")
196
+ expect(auth_result.account.surname).to eq("Picard")
197
+ end
198
+ end
199
+
200
+ context "SHA-512 hashing algorithm" do
201
+ before do
202
+ account_store_mapping
203
+ @account = directory.accounts.create({
204
+ username: "jlucpicard",
205
+ email: "captain@enterprise.com",
206
+ given_name: "Jean-Luc",
207
+ surname: "Picard",
208
+ password: "$stormpath2$SHA-512$1$ZFhBRmpFSnEwVEx2ekhKS0JTMDJBNTNmcg==$Q+sGFg9e+pe9QsUdfnbJUMDtrQNf27ezTnnGllBVkQpMRc9bqH6WkyE3y0svD/7cBk8uJW9Wb3dolWwDtDLFjg=="
209
+ }, password_format: 'mcf')
210
+ end
211
+
212
+ it 'creates an account' do
213
+ expect(@account).to be_a Stormpath::Resource::Account
214
+ expect(@account.username).to eq("jlucpicard")
215
+ expect(@account.email).to eq("captain@enterprise.com")
216
+ expect(@account.given_name).to eq("Jean-Luc")
217
+ expect(@account.surname).to eq("Picard")
218
+ end
219
+
220
+ it 'can authenticate with the account credentials' do
221
+ auth_request = Stormpath::Authentication::UsernamePasswordRequest.new 'jlucpicard', 'testing12'
222
+ auth_result = application.authenticate_account auth_request
223
+
224
+ expect(auth_result).to be_a Stormpath::Authentication::AuthenticationResult
225
+ expect(auth_result.account).to be_a Stormpath::Resource::Account
226
+ expect(auth_result.account.email).to eq("captain@enterprise.com")
227
+ expect(auth_result.account.given_name).to eq("Jean-Luc")
228
+ expect(auth_result.account.surname).to eq("Picard")
229
+ end
230
+ end
231
+
232
+ context 'with account data as hash' do
233
+ let(:created_account_with_hash) do
234
+ directory.create_account({
235
+ email: random_email,
236
+ given_name: 'Ruby SDK',
237
+ password: 'P@$$w0rd',
238
+ surname: 'SDK',
239
+ username: random_user_name
240
+ })
241
+ end
242
+
243
+ after do
244
+ created_account_with_hash.delete if created_account_with_hash
245
+ end
246
+
247
+ it 'creates an account with status ENABLED' do
248
+ expect(created_account_with_hash.email).to eq(random_email)
249
+ expect(created_account_with_hash.given_name).to eq('Ruby SDK')
250
+ expect(created_account_with_hash.surname).to eq('SDK')
251
+ expect(created_account_with_hash.status).to eq("ENABLED")
252
+ end
253
+ end
137
254
 
138
255
  end
139
256
 
@@ -155,7 +272,11 @@ describe Stormpath::Resource::Directory, :vcr do
155
272
  end
156
273
 
157
274
  describe '#create_account_with_custom_data' do
158
- let(:directory) { test_directory }
275
+ let(:directory) { test_api_client.directories.create name: random_directory_name, description: 'description_for_some_test_directory' }
276
+
277
+ after do
278
+ directory.delete if directory
279
+ end
159
280
 
160
281
  it 'creates an account with custom data' do
161
282
  account = Stormpath::Resource::Account.new({
@@ -179,7 +300,11 @@ describe Stormpath::Resource::Directory, :vcr do
179
300
  end
180
301
 
181
302
  describe '#create_group' do
182
- let(:directory) { test_directory }
303
+ let(:directory) { test_api_client.directories.create name: random_directory_name, description: 'description_for_some_test_directory' }
304
+
305
+ after do
306
+ directory.delete if directory
307
+ end
183
308
 
184
309
  context 'given a valid group' do
185
310
  let(:group_name) { "valid_test_group" }
@@ -213,6 +338,7 @@ describe Stormpath::Resource::Directory, :vcr do
213
338
 
214
339
  after do
215
340
  application.delete if application
341
+ directory.delete if directory
216
342
  end
217
343
 
218
344
  it 'and all of its associations' do
@@ -233,8 +359,5 @@ describe Stormpath::Resource::Directory, :vcr do
233
359
  expect(application.accounts).not_to include(account)
234
360
  expect(application.groups).not_to include(group)
235
361
  end
236
-
237
362
  end
238
-
239
-
240
363
  end
@@ -5,6 +5,10 @@ module Stormpath
5
5
  MultiJson.dump(GOOGLE_ACCOUNT)
6
6
  elsif provider.to_sym == :facebook
7
7
  MultiJson.dump(FACEBOOK_ACCOUNT)
8
+ elsif provider.to_sym == :linkedin
9
+ MultiJson.dump(LINKEDIN_ACCOUNT)
10
+ elsif provider.to_sym == :github
11
+ MultiJson.dump(GITHUB_ACCOUNT)
8
12
  end
9
13
  end
10
14
 
@@ -13,6 +17,10 @@ module Stormpath
13
17
  MultiJson.dump(GOOGLE_PROVIDER_DATA)
14
18
  elsif provider.to_sym == :facebook
15
19
  MultiJson.dump(FACEBOOK_PROVIDER_DATA)
20
+ elsif provider.to_sym == :linkedin
21
+ MultiJson.dump(LINKEDIN_PROVIDER_DATA)
22
+ elsif provider.to_sym == :github
23
+ MultiJson.dump(GITHUB_PROVIDER_DATA)
16
24
  end
17
25
  end
18
26
 
@@ -42,6 +50,58 @@ module Stormpath
42
50
  providerId: "facebook"
43
51
  }
44
52
 
53
+ LINKEDIN_ACCOUNT = {
54
+ href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7",
55
+ username: "nenad.nikolic",
56
+ email: "nnikolic87@gmail.com",
57
+ givenName: "Nenad",
58
+ middleName: nil,
59
+ surname: "Nikolic",
60
+ fullName: "Nenad Nikolic",
61
+ status: "ENABLED",
62
+ emailVerificationToken: nil,
63
+ customData: { href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/customData"},
64
+ providerData: { href:"https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/providerData"},
65
+ directory: { href: "https://api.stormpath.com/v1/directories/7ibyn2idP1d9p3qJOomeNP"},
66
+ tenant: { href: "https://api.stormpath.com/v1/tenants/60bD3bKLej6JoFhyKFHiOk"},
67
+ groups: { href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/groups"},
68
+ groupMemberships: { href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/groupMemberships"}
69
+ }
70
+
71
+ LINKEDIN_PROVIDER_DATA = {
72
+ href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/providerData",
73
+ createdAt: "2014-05-19T13:32:16.884Z",
74
+ modifiedAt: "2014-05-19T13:32:16.927Z",
75
+ accessToken: "CAATmZBgxF6rMBAPYbfBhGrVPRw27nn9fAz6bR0DBV1XGfOcSYXSBrhZCkE1y1lWue348fboRxqX7nz88KBYi05qCHw4AQoZCqyIaWedEXrV2vFVzVHo2glq6Vb1ofAWcEHva7baZAaojA8KN5DVz4UTToKgvoIMa1kjyvZCmFZBpYXoG7H3aIKoyWJzUGCDIUrcFjvjnNZBvAZDZD",
76
+ providerId: "linkedin"
77
+ }
78
+
79
+ GITHUB_ACCOUNT = {
80
+ href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7",
81
+ username: "nenad.nikolic",
82
+ email: "nnikolic87@gmail.com",
83
+ givenName: "Nenad",
84
+ middleName: nil,
85
+ surname: "Nikolic",
86
+ fullName: "Nenad Nikolic",
87
+ status: "ENABLED",
88
+ emailVerificationToken: nil,
89
+ customData: { href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/customData"},
90
+ providerData: { href:"https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/providerData"},
91
+ directory: { href: "https://api.stormpath.com/v1/directories/7ibyn2idP1d9p3qJOomeNP"},
92
+ tenant: { href: "https://api.stormpath.com/v1/tenants/60bD3bKLej6JoFhyKFHiOk"},
93
+ groups: { href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/groups"},
94
+ groupMemberships: { href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/groupMemberships"}
95
+ }
96
+
97
+ GITHUB_PROVIDER_DATA = {
98
+ href: "https://api.stormpath.com/v1/accounts/7jdiPam0PWES317hwRR5a7/providerData",
99
+ createdAt: "2014-05-19T13:32:16.884Z",
100
+ modifiedAt: "2014-05-19T13:32:16.927Z",
101
+ accessToken: "CAATmZBgxF6rMBAPYbfBhGrVPRw27nn9fAz6bR0DBV1XGfOcSYXSBrhZCkE1y1lWue348fboRxqX7nz88KBYi05qCHw4AQoZCqyIaWedEXrV2vFVzVHo2glq6Vb1ofAWcEHva7baZAaojA8KN5DVz4UTToKgvoIMa1kjyvZCmFZBpYXoG7H3aIKoyWJzUGCDIUrcFjvjnNZBvAZDZD",
102
+ providerId: "github"
103
+ }
104
+
45
105
  GOOGLE_ACCOUNT = {
46
106
  href: "https://api.stormpath.com/v1/accounts/2XdHmcyFG8HJCYBTEL1dJj",
47
107
  username: "damir.svrtan@gmail.com",
@@ -69,4 +129,4 @@ module Stormpath
69
129
  refreshToken: "Ox8AAACn"
70
130
  }
71
131
  end
72
- end
132
+ end
@@ -9,6 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Stormpath, Inc", "Elder Crisostomo"]
10
10
  s.email = 'support@stormpath.com'
11
11
  s.homepage = 'https://github.com/stormpath/stormpath-sdk-ruby'
12
+ s.license = 'Apache-2.0'
12
13
 
13
14
  s.platform = Gem::Platform::RUBY
14
15
  s.require_paths = %w[lib]
@@ -19,6 +20,7 @@ Gem::Specification.new do |s|
19
20
  s.add_dependency('uuidtools', '>= 2.1.3')
20
21
  s.add_dependency('activesupport', '>= 3.2.8')
21
22
  s.add_dependency('properties-ruby', "~> 0.0.4")
23
+ s.add_dependency('http-cookie', "~> 1.0.2")
22
24
  s.add_dependency('java_properties')
23
25
  s.add_dependency('jwt', '>= 1.5.0')
24
26
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stormpath-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta.9
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stormpath, Inc
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-12-02 00:00:00.000000000 Z
12
+ date: 2016-01-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -81,6 +81,20 @@ dependencies:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: 0.0.4
84
+ - !ruby/object:Gem::Dependency
85
+ name: http-cookie
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 1.0.2
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 1.0.2
84
98
  - !ruby/object:Gem::Dependency
85
99
  name: java_properties
86
100
  requirement: !ruby/object:Gem::Requirement
@@ -286,6 +300,8 @@ files:
286
300
  - lib/stormpath-sdk/id_site/error.rb
287
301
  - lib/stormpath-sdk/id_site/id_site_result.rb
288
302
  - lib/stormpath-sdk/oauth/authenticator.rb
303
+ - lib/stormpath-sdk/oauth/id_site_grant.rb
304
+ - lib/stormpath-sdk/oauth/id_site_grant_request.rb
289
305
  - lib/stormpath-sdk/oauth/password_grant.rb
290
306
  - lib/stormpath-sdk/oauth/password_grant_request.rb
291
307
  - lib/stormpath-sdk/oauth/refresh_grant_request.rb
@@ -298,8 +314,12 @@ files:
298
314
  - lib/stormpath-sdk/provider/account_result.rb
299
315
  - lib/stormpath-sdk/provider/facebook/facebook_provider.rb
300
316
  - lib/stormpath-sdk/provider/facebook/facebook_provider_data.rb
317
+ - lib/stormpath-sdk/provider/github/github_provider.rb
318
+ - lib/stormpath-sdk/provider/github/github_provider_data.rb
301
319
  - lib/stormpath-sdk/provider/google/google_provider.rb
302
320
  - lib/stormpath-sdk/provider/google/google_provider_data.rb
321
+ - lib/stormpath-sdk/provider/linkedin/linkedin_provider.rb
322
+ - lib/stormpath-sdk/provider/linkedin/linkedin_provider_data.rb
303
323
  - lib/stormpath-sdk/provider/provider.rb
304
324
  - lib/stormpath-sdk/provider/provider_data.rb
305
325
  - lib/stormpath-sdk/provider/stormpath/stormpath_provider.rb
@@ -307,6 +327,7 @@ files:
307
327
  - lib/stormpath-sdk/resource/access_token.rb
308
328
  - lib/stormpath-sdk/resource/account.rb
309
329
  - lib/stormpath-sdk/resource/account_membership.rb
330
+ - lib/stormpath-sdk/resource/account_overrides.rb
310
331
  - lib/stormpath-sdk/resource/account_status.rb
311
332
  - lib/stormpath-sdk/resource/account_store.rb
312
333
  - lib/stormpath-sdk/resource/account_store_mapping.rb
@@ -368,7 +389,8 @@ files:
368
389
  - stormpath-sdk.gemspec
369
390
  - support/api.rb
370
391
  homepage: https://github.com/stormpath/stormpath-sdk-ruby
371
- licenses: []
392
+ licenses:
393
+ - Apache-2.0
372
394
  metadata: {}
373
395
  post_install_message:
374
396
  rdoc_options:
@@ -386,9 +408,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
386
408
  version: '0'
387
409
  required_rubygems_version: !ruby/object:Gem::Requirement
388
410
  requirements:
389
- - - ">"
411
+ - - ">="
390
412
  - !ruby/object:Gem::Version
391
- version: 1.3.1
413
+ version: '0'
392
414
  requirements: []
393
415
  rubyforge_project:
394
416
  rubygems_version: 2.4.6