stormpath-sdk 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +3 -3
  4. data/CHANGES.md +8 -0
  5. data/lib/stormpath-sdk.rb +8 -0
  6. data/lib/stormpath-sdk/auth/create_factor.rb +64 -0
  7. data/lib/stormpath-sdk/data_store.rb +17 -1
  8. data/lib/stormpath-sdk/oauth/authenticator.rb +2 -1
  9. data/lib/stormpath-sdk/oauth/challenge_factor_grant.rb +25 -0
  10. data/lib/stormpath-sdk/oauth/challenge_factor_grant_request.rb +16 -0
  11. data/lib/stormpath-sdk/resource/account.rb +6 -0
  12. data/lib/stormpath-sdk/resource/account_link.rb +1 -1
  13. data/lib/stormpath-sdk/resource/challenge.rb +26 -0
  14. data/lib/stormpath-sdk/resource/collection.rb +0 -1
  15. data/lib/stormpath-sdk/resource/directory.rb +1 -0
  16. data/lib/stormpath-sdk/resource/factor.rb +26 -0
  17. data/lib/stormpath-sdk/resource/field.rb +20 -0
  18. data/lib/stormpath-sdk/resource/linked_account.rb +1 -1
  19. data/lib/stormpath-sdk/resource/phone.rb +21 -0
  20. data/lib/stormpath-sdk/resource/schema.rb +21 -0
  21. data/lib/stormpath-sdk/version.rb +2 -2
  22. data/spec/auth/create_factor_spec.rb +92 -0
  23. data/spec/auth/http_basic_authentication_spec.rb +3 -3
  24. data/spec/auth/http_bearer_authentication_spec.rb +3 -3
  25. data/spec/client_spec.rb +25 -25
  26. data/spec/oauth/access_token_authentication_result_spec.rb +3 -3
  27. data/spec/provider/provider_spec.rb +2 -2
  28. data/spec/resource/account_creation_policy_spec.rb +2 -2
  29. data/spec/resource/account_link_spec.rb +2 -2
  30. data/spec/resource/account_spec.rb +196 -12
  31. data/spec/resource/account_store_mapping_spec.rb +3 -3
  32. data/spec/resource/account_store_spec.rb +4 -4
  33. data/spec/resource/api_key_spec.rb +3 -3
  34. data/spec/resource/application_spec.rb +74 -33
  35. data/spec/resource/challenge_spec.rb +53 -0
  36. data/spec/resource/collection_spec.rb +6 -6
  37. data/spec/resource/custom_data_spec.rb +2 -2
  38. data/spec/resource/directory_spec.rb +27 -21
  39. data/spec/resource/email_template_spec.rb +2 -2
  40. data/spec/resource/factor_spec.rb +124 -0
  41. data/spec/resource/field_spec.rb +35 -0
  42. data/spec/resource/group_membership_spec.rb +3 -3
  43. data/spec/resource/group_spec.rb +3 -3
  44. data/spec/resource/linked_account_spec.rb +2 -2
  45. data/spec/resource/organization_spec.rb +6 -6
  46. data/spec/resource/password_policy_spec.rb +2 -2
  47. data/spec/resource/password_strength_spec.rb +2 -2
  48. data/spec/resource/phone_spec.rb +63 -0
  49. data/spec/resource/schema_spec.rb +39 -0
  50. data/spec/resource/status_spec.rb +4 -4
  51. data/spec/spec_helper.rb +6 -3
  52. data/spec/support/custom_data_storage_behavior.rb +2 -2
  53. data/spec/support/mocked_provider_accounts.rb +106 -0
  54. data/spec/support/resource_helpers.rb +20 -16
  55. metadata +16 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7c94bbec690d16f6c04eda0f7e46dd79c9406747
4
- data.tar.gz: 10271a383fb9537813d22cacc1646df9c67b709d
3
+ metadata.gz: f4ee6a6b7b3e99f340ad7056d571cc497cc47163
4
+ data.tar.gz: b4191c5270f3d690590d288553609fdef02a4174
5
5
  SHA512:
6
- metadata.gz: 284ad0976afabc1777161def9266669f855f2eb3bcb6eaaa026bb66e195de22537da218f7cea5b33d68554202d28d5f4714e39e9c7e0e01f91de2b94dabf68e0
7
- data.tar.gz: dc7e80f1a5501e989c7fab8f5b1f2ee889b3c71ae9f75bb1a91bb023e593bed70c83d8ee6bf1da8288556f73a709405c9f6245a12d2019f6d469326a1ff07c59
6
+ metadata.gz: f7600ac17e42f60a5b62df5d396a5534fa9907b19cbd1ce77115846c7df88dbfe0409e9cba0781f5e9261743cd36d4e3de8844aa7d91a03a84213c4a93f42f6c
7
+ data.tar.gz: 8762d8e4df8a3f557a4f2c48505593d89fd27050a5dd9dc6c2a3e6b3d058c1c18bdf69353412b8608dd90cc55f276b4bae6d4e6c11148cac98aec892b507af68
data/.gitignore CHANGED
@@ -13,4 +13,4 @@ tmp/*
13
13
  *.gem
14
14
  .bundle/
15
15
  dump.rdb
16
- .ruby-version
16
+ .ruby-version
@@ -6,8 +6,8 @@ services:
6
6
  - redis-server
7
7
  - memcached
8
8
  before_install:
9
- - gem install bundler
9
+ - gem install bundler
10
10
  env:
11
11
  global:
12
- - secure: MeyjZGgySZvpYVRm2i7wkrhvu9KUeU7eQ5aKj7FXqIYlQe3T67k+KKA3Ejp6WC3wUEVPUFseWMZCuNkRc0UeGA5/4e4LLc++XuaZn1jLTm1yNofh/CFAAjt5VdIYz4R7sKxpyLxs0orK2+HcaiCoqSDj7ehw2SlDca9VEi0GoQk=
13
- - secure: MrtbhfH1IfwDkkvf0pq6trgj68to/fK4c+L1daKvMaNiNvwggbA3s0ea9QjHxXfWmPqL5bf8Q5N7YqXUw8cLpRPrKUA65JkyhyzIju8FgzXz83aCvjJD+5XhJIGzpLM8qHeKh7F0K6/iURTKIUgZXbguK4HpEeCMrrdPYcof3Ew=
12
+ - secure: JObfQKlPYrwupzv2ASkCQWD7TMqD6D9OfbVdUe/sot91fVgoQIs7vGlQPYNz6aTgoAZfEKptrzXzA0KOsGKRDjN0MSkY/PevPxdb+81YTDdldKD5GCc5srWVDpwhRJvKU8ufHdrTcvf+Weulu4egUHoqOerlBcexTO/Erbe8/M4=
13
+ - secure: K24I2/DC4Z6SeosQaR4suZij4yacteVzXqLexJ+LSlxZP7j1T72O3JLtc7/SLm8RFOJkiH/csTgbZmLNAWZO8c0Y4fWAvIC/I1xfoCr57udfQPIe83qdtpVetyYs1BGcqlRboGpqxtOewGHHuHtIge4bjD5l1hHA+WaIdVa4GfI=
data/CHANGES.md CHANGED
@@ -1,6 +1,14 @@
1
1
  stormpath-sdk-ruby Changelog
2
2
  ============================
3
3
 
4
+ Version 1.4.0
5
+ -------------
6
+
7
+ Released on November 22, 2016
8
+
9
+ - Implement Multifactor Authentication with SMS and google-authenticator
10
+
11
+
4
12
  Version 1.3.1
5
13
  -------------
6
14
 
@@ -60,6 +60,11 @@ module Stormpath
60
60
  autoload :PasswordPolicy, 'stormpath-sdk/resource/password_policy'
61
61
  autoload :EmailTemplate, 'stormpath-sdk/resource/email_template'
62
62
  autoload :PasswordStrength, 'stormpath-sdk/resource/password_strength'
63
+ autoload :Phone, 'stormpath-sdk/resource/phone'
64
+ autoload :Factor, 'stormpath-sdk/resource/factor'
65
+ autoload :Challenge, 'stormpath-sdk/resource/challenge'
66
+ autoload :Schema, 'stormpath-sdk/resource/schema'
67
+ autoload :Field, 'stormpath-sdk/resource/field'
63
68
  end
64
69
 
65
70
  module Cache
@@ -80,6 +85,7 @@ module Stormpath
80
85
  autoload :BasicAuthenticator, 'stormpath-sdk/auth/basic_authenticator'
81
86
  autoload :HttpBasicAuthentication, 'stormpath-sdk/auth/http_basic_authentication'
82
87
  autoload :HttpBearerAuthentication, 'stormpath-sdk/auth/http_bearer_authentication'
88
+ autoload :CreateFactor, 'stormpath-sdk/auth/create_factor'
83
89
  end
84
90
 
85
91
  module Provider
@@ -140,5 +146,7 @@ module Stormpath
140
146
  autoload :ClientCredentialsGrant, 'stormpath-sdk/oauth/client_credentials_grant'
141
147
  autoload :SocialGrantRequest, 'stormpath-sdk/oauth/social_grant_request'
142
148
  autoload :SocialGrant, 'stormpath-sdk/oauth/social_grant'
149
+ autoload :ChallengeFactorGrantRequest, 'stormpath-sdk/oauth/challenge_factor_grant_request'
150
+ autoload :ChallengeFactorGrant, 'stormpath-sdk/oauth/challenge_factor_grant'
143
151
  end
144
152
  end
@@ -0,0 +1,64 @@
1
+ #
2
+ # Copyright 2012 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
+ module Stormpath
17
+ module Authentication
18
+ class CreateFactor < Stormpath::Resource::Base
19
+ attr_reader :client, :account, :type, :phone, :challenge, :custom_options
20
+
21
+ def initialize(client, account, type, options = {})
22
+ @client = client
23
+ @account = account
24
+ @type = determine_type(type)
25
+ @phone = options[:phone] || nil
26
+ @challenge = options[:challenge] || nil
27
+ @custom_options = options[:custom_options] || nil
28
+ end
29
+
30
+ def save
31
+ data_store.execute_raw_request(href, resource, Stormpath::Resource::Factor)
32
+ end
33
+
34
+ private
35
+
36
+ def href
37
+ "#{account.href}/factors#{'?challenge=true' if challenge}"
38
+ end
39
+
40
+ def resource
41
+ {}.tap do |body|
42
+ body[:type] = type
43
+ body[:phone] = phone if phone
44
+ body[:challenge] = { message: "#{challenge[:message]} ${code}" } if challenge
45
+ add_custom_options(body)
46
+ end
47
+ end
48
+
49
+ def determine_type(type)
50
+ raise Stormpath::Error unless type == :sms || type == :google_authenticator
51
+ type.to_s.sub('_', '-')
52
+ end
53
+
54
+ def add_custom_options(body)
55
+ if custom_options
56
+ body[:accountName] = custom_options[:account_name] if custom_options[:account_name]
57
+ body[:issuer] = custom_options[:issuer] if custom_options[:issuer]
58
+ body[:status] = custom_options[:status] if custom_options[:status]
59
+ end
60
+ body
61
+ end
62
+ end
63
+ end
64
+ end
@@ -101,6 +101,21 @@ class Stormpath::DataStore
101
101
  return nil
102
102
  end
103
103
 
104
+ def execute_raw_request(href, body, klass)
105
+ request = Request.new('POST', href, nil, {}, body.to_json, @api_key)
106
+ apply_default_request_headers(request)
107
+ response = @request_executor.execute_request(request)
108
+ result = response.body.length > 0 ? MultiJson.load(response.body) : ''
109
+
110
+ if response.error?
111
+ error = Stormpath::Resource::Error.new result
112
+ raise Stormpath::Error.new(error)
113
+ end
114
+
115
+ cache_walk(result)
116
+ instantiate(klass, result)
117
+ end
118
+
104
119
  private
105
120
 
106
121
  def needs_to_be_fully_qualified?(href)
@@ -296,7 +311,8 @@ class Stormpath::DataStore
296
311
  property = resource.get_property name, ignore_camelcasing: ignore_camelcasing
297
312
 
298
313
  # Special use cases are with Custom Data, Provider and ProviderData, their hashes should not be simplified
299
- if property.kind_of?(Hash) and !resource_nested_submittable(resource, name) and name != "items"
314
+ # As of the implementation for MFA, Phone resource is added too
315
+ if property.kind_of?(Hash) and !resource_nested_submittable(resource, name) and name != "items" and name != 'phone'
300
316
  property = to_simple_reference name, property
301
317
  end
302
318
 
@@ -24,7 +24,8 @@ module Stormpath
24
24
  id_site_token: IdSiteGrant,
25
25
  stormpath_token: StormpathTokenGrant,
26
26
  client_credentials: ClientCredentialsGrant,
27
- stormpath_social: SocialGrant
27
+ stormpath_social: SocialGrant,
28
+ stormpath_factor_challenge: ChallengeFactorGrant
28
29
  }.freeze
29
30
  end
30
31
  end
@@ -0,0 +1,25 @@
1
+ module Stormpath
2
+ module Oauth
3
+ class ChallengeFactorGrant < Stormpath::Resource::Base
4
+ prop_accessor :grant_type, :challenge, :code
5
+
6
+ def form_properties
7
+ {
8
+ grant_type: grant_type,
9
+ challenge: challenge,
10
+ code: code
11
+ }
12
+ end
13
+
14
+ def set_options(request)
15
+ set_property :grant_type, request.grant_type
16
+ set_property :challenge, request.challenge
17
+ set_property :code, request.code
18
+ end
19
+
20
+ def form_data?
21
+ true
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module Stormpath
2
+ module Oauth
3
+ class ChallengeFactorGrantRequest
4
+ attr_reader :challenge, :code
5
+
6
+ def initialize(challenge, code)
7
+ @challenge = challenge
8
+ @code = code
9
+ end
10
+
11
+ def grant_type
12
+ 'stormpath_factor_challenge'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -40,6 +40,9 @@ class Stormpath::Resource::Account < Stormpath::Resource::Instance
40
40
 
41
41
  has_many :api_keys, can: [:create]
42
42
 
43
+ has_many :phones, can: [:get, :create]
44
+ has_many :factors, can: [:get, :create]
45
+
43
46
  def add_group group
44
47
  client.group_memberships.create group: group, account: self
45
48
  end
@@ -64,4 +67,7 @@ class Stormpath::Resource::Account < Stormpath::Resource::Instance
64
67
  instance_variable_set "@_provider_data", provider_data
65
68
  end
66
69
 
70
+ def create_factor(type, options = {})
71
+ Stormpath::Authentication::CreateFactor.new(client, self, type, options).save
72
+ end
67
73
  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.
@@ -0,0 +1,26 @@
1
+ #
2
+ # Copyright 2012 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::Challenge < Stormpath::Resource::Instance
17
+ prop_accessor :message
18
+ prop_reader :status, :created_at, :modified_at
19
+
20
+ belongs_to :factor
21
+ belongs_to :account
22
+
23
+ def validate(code)
24
+ data_store.execute_raw_request href, { code: code }, Stormpath::Resource::Challenge
25
+ end
26
+ end
@@ -80,7 +80,6 @@ class Stormpath::Resource::Collection
80
80
  iterate(collection_href, client, item_class, criteria, &block)
81
81
  end
82
82
  end
83
-
84
83
  end
85
84
 
86
85
  class CollectionPage < Stormpath::Resource::Base
@@ -28,6 +28,7 @@ class Stormpath::Resource::Directory < Stormpath::Resource::Instance
28
28
  has_one :custom_data
29
29
  has_one :password_policy
30
30
  has_one :account_creation_policy
31
+ has_one :account_schema, class_name: :schema
31
32
 
32
33
  def provider
33
34
  internal_instance = instance_variable_get "@_provider"
@@ -0,0 +1,26 @@
1
+ #
2
+ # Copyright 2012 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::Factor < Stormpath::Resource::Instance
17
+ prop_accessor :type, :account_name, :issuer, :status
18
+ prop_reader :verification_status, :secret, :key_uri, :base64_q_r_image
19
+ alias_method :qr_code, :base64_q_r_image
20
+
21
+ has_many :challenges, can: [:get, :create]
22
+ has_one :most_recent_challenge, class_name: :challenge
23
+ has_one :phone
24
+
25
+ belongs_to :account
26
+ end
@@ -0,0 +1,20 @@
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::Field < Stormpath::Resource::Instance
17
+ prop_reader :name, :required, :created_at, :modified_at
18
+
19
+ belongs_to :schema
20
+ 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.
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright 2012 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::Phone < Stormpath::Resource::Instance
17
+ prop_accessor :number, :name, :description
18
+ prop_reader :verification_status, :status, :created_at, :modified_at
19
+
20
+ belongs_to :account
21
+ 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::Schema < Stormpath::Resource::Instance
17
+ prop_reader :created_at, :modified_at
18
+
19
+ has_many :fields
20
+ belongs_to :directory
21
+ end
@@ -14,6 +14,6 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module Stormpath
17
- VERSION = '1.3.1'
18
- VERSION_DATE = '2016-11-07'
17
+ VERSION = '1.4.0'
18
+ VERSION_DATE = '2016-11-22'
19
19
  end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'CreateFactor', vcr: true do
4
+ let(:client) { test_api_client }
5
+ let(:directory) { client.directories.create(directory_attrs) }
6
+ let(:account) { directory.accounts.create(account_attrs) }
7
+
8
+ after { directory.delete }
9
+
10
+ context 'type sms' do
11
+ context 'with challenge' do
12
+ before do
13
+ stub_request(:post, "#{account.href}/factors?challenge=true")
14
+ .to_return(body: Stormpath::Test.mocked_factor_response)
15
+ stub_request(:get, "#{factor.href}/challenges")
16
+ .to_return(body: Stormpath::Test.mocked_challenges_response)
17
+ stub_request(:get, "#{factor.href}/challenges?offset=25")
18
+ .to_return(body: Stormpath::Test.mocked_challenges_response)
19
+ end
20
+ let(:factor) do
21
+ Stormpath::Authentication::CreateFactor.new(
22
+ client,
23
+ account,
24
+ :sms,
25
+ phone: { number: '+12025550173',
26
+ name: 'Rspec test phone',
27
+ description: 'This is a testing phone number' },
28
+ challenge: { message: 'Enter code please: ' }
29
+ ).save
30
+ end
31
+
32
+ it 'should create factor' do
33
+ expect(factor.href).to be
34
+ end
35
+ end
36
+
37
+ context 'without challenge' do
38
+ before do
39
+ stub_request(:post, "#{account.href}/factors")
40
+ .to_return(body: Stormpath::Test.mocked_factor_response)
41
+ stub_request(:get, "#{factor.href}/challenges")
42
+ .to_return(body: Stormpath::Test.mocked_empty_challenge_response)
43
+ stub_request(:get, "#{factor.href}/challenges?offset=25")
44
+ .to_return(body: Stormpath::Test.mocked_empty_challenge_response)
45
+ end
46
+ let(:factor) do
47
+ Stormpath::Authentication::CreateFactor.new(
48
+ client,
49
+ account,
50
+ :sms,
51
+ phone: { number: '+12025550173',
52
+ name: 'Rspec test phone',
53
+ description: 'This is a testing phone number' }
54
+ ).save
55
+ end
56
+
57
+ it 'should create factor without challenge' do
58
+ expect(factor.href).to be
59
+ expect(factor.challenges.count).to eq 0
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'type google-authenticator' do
65
+ let(:factor) do
66
+ Stormpath::Authentication::CreateFactor.new(
67
+ client,
68
+ account,
69
+ :google_authenticator,
70
+ custom_options: {
71
+ account_name: "marko.cilimkovic#{default_domain}",
72
+ issuer: 'ACME',
73
+ status: 'ENABLED'
74
+ }
75
+ ).save
76
+ end
77
+
78
+ it 'should create factor' do
79
+ expect(factor.href).to be
80
+ end
81
+ end
82
+
83
+ context 'bad type' do
84
+ let(:factor) do
85
+ Stormpath::Authentication::CreateFactor.new(client, account, :invalid_factor_type).save
86
+ end
87
+
88
+ it 'should raise error' do
89
+ expect { factor }.to raise_error(Stormpath::Error)
90
+ end
91
+ end
92
+ end