stormpath-sdk 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/CHANGES.md +11 -0
- data/README.md +234 -3
- data/Rakefile +3 -1
- data/lib/stormpath-sdk.rb +4 -0
- data/lib/stormpath-sdk/auth/basic_login_attempt.rb +4 -0
- data/lib/stormpath-sdk/auth/username_password_request.rb +0 -1
- data/lib/stormpath-sdk/client.rb +1 -1
- data/lib/stormpath-sdk/data_store.rb +17 -9
- data/lib/stormpath-sdk/provider/saml/saml_mapping_rules.rb +22 -0
- data/lib/stormpath-sdk/provider/saml/saml_provider.rb +20 -0
- data/lib/stormpath-sdk/provider/saml/saml_provider_data.rb +19 -0
- data/lib/stormpath-sdk/provider/saml/saml_provider_metadata.rb +19 -0
- data/lib/stormpath-sdk/resource/application.rb +22 -7
- data/lib/stormpath-sdk/resource/directory.rb +16 -0
- data/lib/stormpath-sdk/version.rb +2 -2
- data/spec/client_spec.rb +108 -13
- data/spec/fixtures/response/create_saml_directory.json +26 -0
- data/spec/fixtures/response/create_saml_directory_mapping_rules.json +12 -0
- data/spec/fixtures/response/get_saml_directory_provider.json +16 -0
- data/spec/fixtures/response/get_saml_directory_provider_metadata.json +12 -0
- data/spec/resource/application_spec.rb +378 -33
- data/spec/resource/directory_spec.rb +168 -0
- data/spec/spec_helper.rb +14 -1
- data/stormpath-sdk.gemspec +1 -0
- metadata +25 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d31ad099a140c12a0522f4bca201aebb81838e4
|
4
|
+
data.tar.gz: b1de03018cc6bf723c873dd3c8368cdb197d3ac8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4829ff4c1ac11afff95f83ae2f7adda7ca796bd13369a45635496607da558b6c10e3ca6f8e0e4ec38e7930a3d62b796aad461e157de216381644f3705a60c44
|
7
|
+
data.tar.gz: a437e1bf41aa13588ae014d9dcc9f3730537e31558b2e1b268ca92052098f747228d7f634714b6c9d766e04996cc89a202dade529d6fe9c11d254d0e483dcd56
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
stormpath-sdk-ruby Changelog
|
2
2
|
============================
|
3
3
|
|
4
|
+
Version 1.1.0
|
5
|
+
-------------
|
6
|
+
|
7
|
+
Released on May 11, 2016
|
8
|
+
|
9
|
+
- Saml integration
|
10
|
+
- Support for specifying an organization name_key on login attempts
|
11
|
+
- Support for specifying the account store when reseting passwords
|
12
|
+
- Add delegate to organizations
|
13
|
+
- Add Organization docs to README
|
14
|
+
|
4
15
|
Version 1.0.1
|
5
16
|
-------------
|
6
17
|
|
data/README.md
CHANGED
@@ -304,7 +304,7 @@ application.create_id_site_url({
|
|
304
304
|
});
|
305
305
|
```
|
306
306
|
|
307
|
-
##### Using Subdomains
|
307
|
+
##### Using Subdomains
|
308
308
|
|
309
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
310
|
|
@@ -379,8 +379,8 @@ Again, with all these methods, You will want your application to link to an inte
|
|
379
379
|
> A JWT will expire after 60 seconds of creation.
|
380
380
|
|
381
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].
|
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
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
385
|
more info [here][exchange-id-site-token].
|
386
386
|
|
@@ -486,6 +486,204 @@ rescue Stormpath::Error => e
|
|
486
486
|
end
|
487
487
|
```
|
488
488
|
|
489
|
+
The `UsernamePasswordRequest` can take an optional link to the application’s accountStore (directory or group) or the Organization nameKey. Specifying this attribute can speed up logins if you know exactly which of the application’s assigned account stores contains the account: Stormpath will not have to iterate over the assigned account stores to find the account to authenticate it. This can speed up logins significantly if you have many account stores (> 15) assigned to the application.
|
490
|
+
|
491
|
+
The `UsernamePasswordRequest` can receive the AccountStore in three ways.
|
492
|
+
|
493
|
+
Passing the organization, directory or group instance:
|
494
|
+
|
495
|
+
```ruby
|
496
|
+
auth_request =
|
497
|
+
Stormpath::Authentication::UsernamePasswordRequest.new 'johnsmith', '4P@$$w0rd!', account_store: organization
|
498
|
+
```
|
499
|
+
|
500
|
+
Passing the organization, directory or group href:
|
501
|
+
|
502
|
+
```ruby
|
503
|
+
auth_request =
|
504
|
+
Stormpath::Authentication::UsernamePasswordRequest.new 'johnsmith', '4P@$$w0rd!', account_store: { href: organization.href }
|
505
|
+
```
|
506
|
+
|
507
|
+
Passing the organization name_key:
|
508
|
+
|
509
|
+
```ruby
|
510
|
+
auth_request =
|
511
|
+
Stormpath::Authentication::UsernamePasswordRequest.new 'johnsmith', '4P@$$w0rd!', account_store: { name_key: organization.name_key }
|
512
|
+
```
|
513
|
+
|
514
|
+
### Authentication Against a SAML Directory
|
515
|
+
|
516
|
+
SAML is an XML-based standard for exchanging authentication and authorization data between security domains.
|
517
|
+
Stormpath enables you to allow customers to log-in by authenticating with an external SAML Identity Provider.
|
518
|
+
|
519
|
+
#### Stormpath as a Service Provider
|
520
|
+
|
521
|
+
The specific use case that Stormpath supports is user-initiated single sign-on. In this scenario, a user requests
|
522
|
+
a protected resource (e.g. your application). Your application, with the help of Stormpath, then confirms the users
|
523
|
+
identity in order to determine whether they are able to access the resource. In SAML terminology, the user is the User
|
524
|
+
Agent, your application (along with Stormpath) is the Service Provider, and the third-party SAML authentication site
|
525
|
+
is the Identity Provider or IdP.
|
526
|
+
|
527
|
+
The broad strokes of the process are as follows:
|
528
|
+
|
529
|
+
* User Agent requests access from Service Provider
|
530
|
+
* Service Provider responds with redirect to Identity Provider
|
531
|
+
* Identity Provider authenticates the user
|
532
|
+
* Identity provider redirects user back to Service Provider along with SAML assertions.
|
533
|
+
* Service Provider receives SAML assertions and either creates or retrieves Account information
|
534
|
+
|
535
|
+
#### Configuring Stormpath as a Service Provider
|
536
|
+
|
537
|
+
Configuration is stored in the Directory's Provider resource. Here we will explain to you the steps that are required to configure Stormpath as a SAML Service
|
538
|
+
Provider.
|
539
|
+
|
540
|
+
#### Step 1: Gather IDP Data
|
541
|
+
|
542
|
+
You will need the following information from your IdP:
|
543
|
+
|
544
|
+
- **SSO Login URL** - The URL at the IdP to which SAML authentication requests should be sent. This is often called an "SSO URL", "Login URL" or "Sign-in URL".
|
545
|
+
- **SSO Logout URL** - The URL at the IdP to which SAML logout requests should be sent. This is often called a "Logout URL", "Global Logout URL" or "Single Logout URL".
|
546
|
+
- **Signing Cert** - The IdP will digitally sign auth assertions and Stormpath will need to validate the signature. This will usually be in .pem or .crt format, but Stormpath requires the text value.
|
547
|
+
- **Signing Algorithm** - You will need the name of the signing algorithm that your IdP uses. It will be either "RSA-SHA256" or "RSA-SHA1".
|
548
|
+
|
549
|
+
#### Step 2: Configure Your SAML Directory
|
550
|
+
|
551
|
+
Input the data you gathered in Step 1 above into your Directory's Provider resource, and then pass that along as part of the Directory creation HTTP POST:
|
552
|
+
|
553
|
+
```ruby
|
554
|
+
provider = "saml"
|
555
|
+
request = Stormpath::Provider::AccountRequest.new(provider, :access_token, access_token)
|
556
|
+
application.get_provider_account(request)
|
557
|
+
|
558
|
+
client.directories.create(
|
559
|
+
name: "infinum_directory",
|
560
|
+
description: "random description",
|
561
|
+
provider: {
|
562
|
+
provider_id: "saml"
|
563
|
+
}
|
564
|
+
)
|
565
|
+
```
|
566
|
+
|
567
|
+
to get the data about the provider just type
|
568
|
+
```ruby
|
569
|
+
directory.provider
|
570
|
+
```
|
571
|
+
|
572
|
+
provider method returns instance of SamlProvider and you can access the following data
|
573
|
+
|
574
|
+
```ruby
|
575
|
+
dir_provider = directory.provider
|
576
|
+
|
577
|
+
dir_provider.provider_id
|
578
|
+
dir_provider.sso_login_url
|
579
|
+
dir_provider.sso_logout_url
|
580
|
+
dir_provider.encoded_x509_signing_cert
|
581
|
+
dir_provider.request_signature_algorithm
|
582
|
+
dir_provider.service_provider_metadata
|
583
|
+
dir_provider.attribute_statement_mapping_rules
|
584
|
+
dir_provider.creted_at
|
585
|
+
dir_provider.modified_at
|
586
|
+
```
|
587
|
+
|
588
|
+
##### Retrieve Your Service Provider Metadata
|
589
|
+
|
590
|
+
Next you will have to configure your Stormpath-powered application as a Service Provider in your Identity Provider. This means that you will need to retrieve the correct metadata from Stormpath.
|
591
|
+
|
592
|
+
In order to retrieve the required values, start by sending a GET to the Directory's Provider:
|
593
|
+
|
594
|
+
```ruby
|
595
|
+
directory.provider_metadata
|
596
|
+
```
|
597
|
+
|
598
|
+
provider_metadata method returns instance of SamlProviderMetadata and you can access the following values
|
599
|
+
|
600
|
+
```ruby
|
601
|
+
dir_provider_metadata = directory.provider_metadata
|
602
|
+
|
603
|
+
dir_provider_metadata.href
|
604
|
+
dir_provider_metadata.entity_id
|
605
|
+
dir_provider_metadata.x509_signing_cert
|
606
|
+
dir_provider_metadata.assertion_consumer_service_post_endpoint
|
607
|
+
dir_provider_metadata.created_at
|
608
|
+
dir_provider_metadata.modified_at
|
609
|
+
```
|
610
|
+
|
611
|
+
From this metadata, you will need two values:
|
612
|
+
|
613
|
+
- **Assertion Consumer Service URL**: This is the location the IdP will send its response to.
|
614
|
+
- **X509 Signing Certificate**: The certificate that is used to sign the requests sent to the IdP. If you retrieve XML, the certificate will be embedded. If you retrieve JSON, you'll have to follow a further /x509certificates link to retrieve it.
|
615
|
+
|
616
|
+
You will also need two other values, which will always be the same:
|
617
|
+
|
618
|
+
- **SAML Request Binding**: Set to HTTP-Redirect.
|
619
|
+
- **SAML Response Binding**: Set to HTTP-Post.
|
620
|
+
|
621
|
+
#### Step 4: Configure Your Service Provider in Your Identity Provider
|
622
|
+
|
623
|
+
Log-in to your Identity Provider (Salesforce, OneLogin, etc) and enter the information you retrieved in
|
624
|
+
the previous step into the relevant application configuration fields. The specific steps to follow here
|
625
|
+
will depend entirely on what Identity Provider you use, and for more information you should consult your
|
626
|
+
Identity Provider's SAML documentation.
|
627
|
+
|
628
|
+
#### Step 5: Configure Your Application
|
629
|
+
|
630
|
+
The Stormpath `Application` Resource has two parts that are relevant to SAML:
|
631
|
+
|
632
|
+
- an ``authorizedCallbackUri`` Array that defines the authorized URIs that the IdP can return your user to. These should be URIs that you host yourself.
|
633
|
+
- an embedded ``samlPolicy`` object that contains information about the SAML flow configuration and endpoints.
|
634
|
+
|
635
|
+
```ruby
|
636
|
+
application.authorized_callback_uris = ["https://myapplication.com/whatever/callback", "https://myapplication.com/whatever/callback2"]
|
637
|
+
application.save
|
638
|
+
```
|
639
|
+
|
640
|
+
#### Step 6: Add the SAML Directory as an Account Store
|
641
|
+
|
642
|
+
The next step is to map the new Directory to your Application with an Account Store Mapping.
|
643
|
+
|
644
|
+
##### Step 7: Configure SAML Assertion Mapping
|
645
|
+
|
646
|
+
The Identity Provider's SAML response contains assertions about the user's identity, which Stormpath can use to create and populate a new Account resource.
|
647
|
+
|
648
|
+
```xml
|
649
|
+
<saml:AttributeStatement>
|
650
|
+
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
|
651
|
+
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
|
652
|
+
</saml:Attribute>
|
653
|
+
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
|
654
|
+
<saml:AttributeValue xsi:type="xs:string">jane@example.com</saml:AttributeValue>
|
655
|
+
</saml:Attribute>
|
656
|
+
<saml:Attribute Name="location" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
|
657
|
+
<saml:AttributeValue xsi:type="xs:string">Tampa, FL</saml:AttributeValue>
|
658
|
+
</saml:Attribute>
|
659
|
+
</saml:AttributeStatement>
|
660
|
+
|
661
|
+
```
|
662
|
+
The Attribute Assertions (`<saml:AttributeStatement>`) are brought into Stormpath and become Account and customData attributes.
|
663
|
+
|
664
|
+
SAML Assertion mapping is defined in an **attributeStatementMappingRules** object found inside the Directory's Provider object, or directly: `/v1/attributeStatementMappingRules/$RULES_ID`.
|
665
|
+
|
666
|
+
##### Mapping Rules
|
667
|
+
|
668
|
+
The rules have three different components:
|
669
|
+
|
670
|
+
- **name**: The SAML Attribute name
|
671
|
+
- **nameFormat**: The name format for this SAML Attribute, expressed as a Uniform Resource Name (URN).
|
672
|
+
- **accountAttributes**: This is an array of Stormpath Account or customData (`customData.$KEY_NAME`) attributes that will map to this SAML Attribute.
|
673
|
+
|
674
|
+
In order to create the mapping rules, we simply send the following:
|
675
|
+
|
676
|
+
```ruby
|
677
|
+
mappings = Stormpath::Provider::SamlMappingRules.new(items: [
|
678
|
+
{
|
679
|
+
name: "uid",
|
680
|
+
account_attributes: ["username"]
|
681
|
+
}
|
682
|
+
])
|
683
|
+
|
684
|
+
dir.create_attribute_mappings(mappings)
|
685
|
+
```
|
686
|
+
|
489
687
|
### Password Reset
|
490
688
|
|
491
689
|
A password reset workflow, if configured on the directory the account is
|
@@ -576,6 +774,39 @@ Group membership can be created by:
|
|
576
774
|
|
577
775
|
You will need to reload the account or group resource after these
|
578
776
|
operations to ensure they've picked up the changes.
|
777
|
+
|
778
|
+
### Working with Organizations
|
779
|
+
|
780
|
+
An `Organization` is a top-level container for Account Stores. You can think of an Organization as a tenant for your multi-tenant application. This is different than your Stormpath Tenant, which represents your tenant in the Stormpath system. Organizations are powerful because you can group together account stores that represent a tenant.
|
781
|
+
|
782
|
+
* Locate an organization
|
783
|
+
|
784
|
+
```ruby
|
785
|
+
client.organizations.search(name: 'Finance Organization')
|
786
|
+
```
|
787
|
+
|
788
|
+
* Create an organization
|
789
|
+
|
790
|
+
```ruby
|
791
|
+
client.organizations.create(name: 'Finance Organization', name_key: 'finance-organization')
|
792
|
+
```
|
793
|
+
|
794
|
+
* Adding an account store to an organization
|
795
|
+
|
796
|
+
```ruby
|
797
|
+
client.organization_account_store_mappings.create(
|
798
|
+
account_store: { href: directory_or_group.href },
|
799
|
+
organization: { href: organization.href }
|
800
|
+
)
|
801
|
+
```
|
802
|
+
|
803
|
+
* Adding an Organization to an Application as an Account Store
|
804
|
+
|
805
|
+
```ruby
|
806
|
+
client.account_store_mappings.create application: application, account_store: organization
|
807
|
+
```
|
808
|
+
|
809
|
+
|
579
810
|
### Add Custom Data to Accounts or Groups
|
580
811
|
|
581
812
|
Account and Group resources have predefined fields that are useful to many applications, but you are likely to have your own custom data that you need to associate with an account or group as well.
|
data/Rakefile
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
1
2
|
require 'rubygems'
|
2
3
|
require 'rubygems/package_task'
|
3
4
|
require 'rspec/core/rake_task'
|
4
5
|
require 'stormpath-sdk'
|
5
6
|
require './support/api'
|
6
7
|
|
8
|
+
|
7
9
|
spec = eval(File.read('stormpath-sdk.gemspec'))
|
8
10
|
|
9
11
|
Gem::PackageTask.new(spec) do |p|
|
@@ -27,4 +29,4 @@ namespace :api do
|
|
27
29
|
ENV['STORMPATH_SDK_TEST_DIRECTORY_WITH_VERIFICATION_URL']
|
28
30
|
)
|
29
31
|
end
|
30
|
-
end
|
32
|
+
end
|
data/lib/stormpath-sdk.rb
CHANGED
@@ -87,6 +87,10 @@ module Stormpath
|
|
87
87
|
autoload :LinkedinProviderData, 'stormpath-sdk/provider/linkedin/linkedin_provider_data'
|
88
88
|
autoload :GithubProvider, 'stormpath-sdk/provider/github/github_provider'
|
89
89
|
autoload :GithubProviderData, 'stormpath-sdk/provider/github/github_provider_data'
|
90
|
+
autoload :SamlProvider, 'stormpath-sdk/provider/saml/saml_provider'
|
91
|
+
autoload :SamlProviderData, 'stormpath-sdk/provider/saml/saml_provider_data'
|
92
|
+
autoload :SamlProviderMetadata, 'stormpath-sdk/provider/saml/saml_provider_metadata'
|
93
|
+
autoload :SamlMappingRules, 'stormpath-sdk/provider/saml/saml_mapping_rules'
|
90
94
|
autoload :StormpathProvider, 'stormpath-sdk/provider/stormpath/stormpath_provider'
|
91
95
|
autoload :StormpathProviderData, 'stormpath-sdk/provider/stormpath/stormpath_provider_data'
|
92
96
|
end
|
@@ -28,6 +28,10 @@ module Stormpath
|
|
28
28
|
def account_store=(account_store)
|
29
29
|
if account_store.kind_of? Stormpath::Resource::Base
|
30
30
|
set_property ACCOUNT_STORE, {HREF_PROP_NAME => account_store.href}
|
31
|
+
elsif account_store.kind_of? Hash
|
32
|
+
set_property ACCOUNT_STORE, sanitize(account_store)
|
33
|
+
else
|
34
|
+
fail ArgumentError, 'account_store should be a Stormpath::Resource::Instance or a Hash'
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
data/lib/stormpath-sdk/client.rb
CHANGED
@@ -53,7 +53,7 @@ module Stormpath
|
|
53
53
|
data_store.save token, Stormpath::Resource::Account
|
54
54
|
end
|
55
55
|
end
|
56
|
-
has_many :organizations, href: '/organizations', can: [:get, :create]
|
56
|
+
has_many :organizations, href: '/organizations', can: [:get, :create], delegate: true
|
57
57
|
has_many :groups, href: '/groups', can: :get
|
58
58
|
has_many :group_memberships, href: '/groupMemberships', can: [:get, :create]
|
59
59
|
has_many :account_store_mappings, href: '/accountStoreMappings', can: [:get, :create]
|
@@ -119,7 +119,7 @@ class Stormpath::DataStore
|
|
119
119
|
|
120
120
|
request = Request.new(http_method, href, query, Hash.new, body, @api_key)
|
121
121
|
|
122
|
-
if resource.try(:form_data?)
|
122
|
+
if resource.try(:form_data?)
|
123
123
|
apply_form_data_request_headers request
|
124
124
|
else
|
125
125
|
apply_default_request_headers request
|
@@ -141,7 +141,7 @@ class Stormpath::DataStore
|
|
141
141
|
|
142
142
|
return if http_method == 'delete'
|
143
143
|
|
144
|
-
if result[HREF_PROP_NAME]
|
144
|
+
if result[HREF_PROP_NAME] and !resource_is_saml_mapping_rules? resource
|
145
145
|
cache_walk result
|
146
146
|
else
|
147
147
|
result
|
@@ -213,7 +213,7 @@ class Stormpath::DataStore
|
|
213
213
|
request.http_headers.store 'Content-Type', 'application/json'
|
214
214
|
end
|
215
215
|
end
|
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
219
|
apply_default_user_agent(request)
|
@@ -277,7 +277,7 @@ class Stormpath::DataStore
|
|
277
277
|
form_data = resource.try(:form_data?)
|
278
278
|
|
279
279
|
if form_data
|
280
|
-
form_request_parse(resource)
|
280
|
+
form_request_parse(resource)
|
281
281
|
else
|
282
282
|
MultiJson.dump(to_hash(resource))
|
283
283
|
end
|
@@ -285,7 +285,7 @@ class Stormpath::DataStore
|
|
285
285
|
|
286
286
|
def form_request_parse(resource)
|
287
287
|
data = ""
|
288
|
-
|
288
|
+
|
289
289
|
property_names = resource.get_dirty_property_names
|
290
290
|
property_names.each do |name|
|
291
291
|
if name != "formData"
|
@@ -305,29 +305,37 @@ class Stormpath::DataStore
|
|
305
305
|
property = resource.get_property name, ignore_camelcasing: ignore_camelcasing
|
306
306
|
|
307
307
|
# Special use cases are with Custom Data, Provider and ProviderData, their hashes should not be simplified
|
308
|
-
if property.kind_of?(Hash) and !resource_nested_submittable(resource, name)
|
308
|
+
if property.kind_of?(Hash) and !resource_nested_submittable(resource, name) and name != "items"
|
309
309
|
property = to_simple_reference name, property
|
310
310
|
end
|
311
311
|
|
312
|
+
if name == "items" and resource_is_saml_mapping_rules? resource
|
313
|
+
property = property.map { |item| item.transform_keys { |key| key.to_s.camelize(:lower).to_sym } }
|
314
|
+
end
|
315
|
+
|
312
316
|
properties.store name, property
|
313
317
|
end
|
314
318
|
end
|
315
319
|
end
|
316
320
|
|
317
321
|
def to_simple_reference(property_name, hash)
|
318
|
-
assert_true hash.
|
322
|
+
assert_true hash.key?(HREF_PROP_NAME), "Nested resource '#{property_name}' must have an 'href' property."
|
319
323
|
|
320
324
|
href = hash[HREF_PROP_NAME]
|
321
325
|
|
322
|
-
{HREF_PROP_NAME => href}
|
326
|
+
{ HREF_PROP_NAME => href }
|
323
327
|
end
|
324
328
|
|
325
329
|
def resource_nested_submittable resource, name
|
326
|
-
['provider', 'providerData'].include?(name) or resource_is_custom_data(resource, name)
|
330
|
+
['provider', 'providerData', 'accountStore'].include?(name) or resource_is_custom_data(resource, name)
|
327
331
|
end
|
328
332
|
|
329
333
|
def resource_is_custom_data resource, name
|
330
334
|
resource.is_a? Stormpath::Resource::CustomData or name == 'customData'
|
331
335
|
end
|
332
336
|
|
337
|
+
def resource_is_saml_mapping_rules? resource
|
338
|
+
resource.is_a? Stormpath::Provider::SamlMappingRules
|
339
|
+
end
|
340
|
+
|
333
341
|
end
|