stormpath-sdk 0.4.0 → 1.0.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. data/.gitignore +6 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +27 -0
  5. data/CHANGES.md +21 -1
  6. data/Gemfile +1 -2
  7. data/README.md +457 -11
  8. data/Rakefile +15 -1
  9. data/lib/stormpath-sdk.rb +52 -33
  10. data/lib/stormpath-sdk/{resource/group_list.rb → api_key.rb} +5 -9
  11. data/lib/stormpath-sdk/auth/authentication_result.rb +3 -13
  12. data/lib/stormpath-sdk/auth/basic_authenticator.rb +5 -11
  13. data/lib/stormpath-sdk/auth/basic_login_attempt.rb +6 -8
  14. data/lib/stormpath-sdk/auth/username_password_request.rb +2 -5
  15. data/lib/stormpath-sdk/cache/cache.rb +54 -0
  16. data/lib/stormpath-sdk/cache/cache_entry.rb +33 -0
  17. data/lib/stormpath-sdk/cache/cache_manager.rb +22 -0
  18. data/lib/stormpath-sdk/cache/cache_stats.rb +35 -0
  19. data/lib/stormpath-sdk/cache/memory_store.rb +29 -0
  20. data/lib/stormpath-sdk/cache/redis_store.rb +32 -0
  21. data/lib/stormpath-sdk/client.rb +111 -0
  22. data/lib/stormpath-sdk/data_store.rb +241 -0
  23. data/lib/stormpath-sdk/{client/api_key.rb → error.rb} +16 -10
  24. data/lib/stormpath-sdk/{util → ext}/hash.rb +1 -2
  25. data/lib/stormpath-sdk/http/authc/sauthc1_signer.rb +8 -4
  26. data/lib/stormpath-sdk/http/http_client_request_executor.rb +8 -7
  27. data/lib/stormpath-sdk/http/request.rb +4 -8
  28. data/lib/stormpath-sdk/{util/request_utils.rb → http/utils.rb} +17 -38
  29. data/lib/stormpath-sdk/resource/account.rb +12 -108
  30. data/lib/stormpath-sdk/resource/application.rb +35 -171
  31. data/lib/stormpath-sdk/resource/associations.rb +97 -0
  32. data/lib/stormpath-sdk/resource/base.rb +256 -0
  33. data/lib/stormpath-sdk/resource/collection.rb +94 -0
  34. data/lib/stormpath-sdk/resource/directory.rb +11 -68
  35. data/lib/stormpath-sdk/resource/email_verification_token.rb +3 -9
  36. data/lib/stormpath-sdk/resource/error.rb +4 -38
  37. data/lib/stormpath-sdk/resource/expansion.rb +28 -0
  38. data/lib/stormpath-sdk/resource/group.rb +8 -66
  39. data/lib/stormpath-sdk/resource/group_membership.rb +4 -55
  40. data/lib/stormpath-sdk/resource/{application_list.rb → instance.rb} +7 -13
  41. data/lib/stormpath-sdk/resource/password_reset_token.rb +5 -23
  42. data/lib/stormpath-sdk/resource/status.rb +22 -28
  43. data/lib/stormpath-sdk/resource/tenant.rb +5 -52
  44. data/lib/stormpath-sdk/resource/utils.rb +43 -13
  45. data/lib/stormpath-sdk/util/assert.rb +5 -15
  46. data/lib/stormpath-sdk/version.rb +3 -3
  47. data/spec/api_key_spec.rb +19 -0
  48. data/spec/auth/basic_authenticator_spec.rb +25 -0
  49. data/spec/auth/sauthc1_signer_spec.rb +42 -0
  50. data/spec/cache/cache_entry_spec.rb +157 -0
  51. data/spec/cache/cache_spec.rb +89 -0
  52. data/spec/cache/cache_stats_spec.rb +106 -0
  53. data/spec/client_spec.rb +538 -0
  54. data/spec/data_store_spec.rb +130 -0
  55. data/spec/resource/account_spec.rb +74 -0
  56. data/spec/resource/application_spec.rb +148 -0
  57. data/spec/resource/base_spec.rb +114 -0
  58. data/spec/resource/collection_spec.rb +169 -0
  59. data/spec/resource/directory_spec.rb +30 -0
  60. data/spec/resource/expansion_spec.rb +100 -0
  61. data/spec/resource/group_spec.rb +49 -0
  62. data/spec/spec_helper.rb +135 -0
  63. data/spec/support/resource_factory.rb +48 -0
  64. data/spec/support/resource_matchers.rb +27 -0
  65. data/spec/support/test_cache_stores.rb +9 -0
  66. data/spec/support/test_request_executor.rb +11 -0
  67. data/stormpath-sdk.gemspec +14 -4
  68. data/support/api.rb +55 -0
  69. metadata +214 -44
  70. data/lib/stormpath-sdk/client/client.rb +0 -38
  71. data/lib/stormpath-sdk/client/client_application.rb +0 -38
  72. data/lib/stormpath-sdk/client/client_application_builder.rb +0 -351
  73. data/lib/stormpath-sdk/client/client_builder.rb +0 -305
  74. data/lib/stormpath-sdk/ds/data_store.rb +0 -210
  75. data/lib/stormpath-sdk/ds/resource_factory.rb +0 -37
  76. data/lib/stormpath-sdk/resource/account_list.rb +0 -32
  77. data/lib/stormpath-sdk/resource/collection_resource.rb +0 -91
  78. data/lib/stormpath-sdk/resource/directory_list.rb +0 -30
  79. data/lib/stormpath-sdk/resource/group_membership_list.rb +0 -32
  80. data/lib/stormpath-sdk/resource/instance_resource.rb +0 -28
  81. data/lib/stormpath-sdk/resource/resource.rb +0 -327
  82. data/lib/stormpath-sdk/resource/resource_error.rb +0 -47
  83. data/test/client/client.yml +0 -16
  84. data/test/client/client_application_builder_spec.rb +0 -114
  85. data/test/client/client_builder_spec.rb +0 -176
  86. data/test/client/read_spec.rb +0 -254
  87. data/test/client/write_spec.rb +0 -420
  88. data/test/resource/resource_spec.rb +0 -41
  89. data/test/resource/test_resource.rb +0 -28
@@ -22,11 +22,10 @@ module Stormpath
22
22
  include Stormpath::Http::Authc
23
23
  include Stormpath::Util::Assert
24
24
 
25
- def initialize(api_key)
25
+ def initialize(api_key, options = {})
26
26
  @signer = Sauthc1Signer.new
27
27
  @api_key = api_key
28
- @http_client = HTTPClient.new
29
-
28
+ @http_client = HTTPClient.new options[:proxy]
30
29
  end
31
30
 
32
31
  def execute_request(request)
@@ -35,18 +34,20 @@ module Stormpath
35
34
 
36
35
  @signer.sign_request request, @api_key
37
36
 
38
- domain = request.href
37
+ domain = if request.query_string.present?
38
+ [request.href, request.to_s_query_string(true)].join '?'
39
+ else
40
+ request.href
41
+ end
39
42
 
40
43
  method = @http_client.method(request.http_method.downcase)
41
44
 
42
45
  if request.body.nil?
43
46
 
44
- response = method.call domain, request.query_string, request.http_headers
47
+ response = method.call domain, nil, request.http_headers
45
48
 
46
49
  else
47
50
 
48
- add_query_string domain, request.query_string
49
-
50
51
  response = method.call domain, request.body, request.http_headers
51
52
 
52
53
  end
@@ -14,12 +14,9 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module Stormpath
17
-
18
17
  module Http
19
-
20
18
  class Request
21
-
22
- include Stormpath::Util
19
+ include Stormpath::Http::Utils
23
20
 
24
21
  attr_accessor :http_method, :href, :query_string, :http_headers, :body
25
22
 
@@ -72,11 +69,10 @@ module Stormpath
72
69
  result = ''
73
70
 
74
71
  if !@query_string.empty?
72
+ Hash[@query_string.sort].each do |key, value|
75
73
 
76
- @query_string.each do |key, value|
77
-
78
- enc_key = RequestUtils.encode_url key, false, canonical
79
- enc_value = RequestUtils.encode_url value, false, canonical
74
+ enc_key = encode_url key, false, canonical
75
+ enc_value = encode_url value, false, canonical
80
76
 
81
77
  if !result.empty?
82
78
  result << '&'
@@ -14,59 +14,38 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module Stormpath
17
+ module Http
18
+ module Utils
17
19
 
18
- module Util
19
-
20
- class RequestUtils
21
-
22
- ##
23
- # Returns true if the specified URI uses a standard port (i.e. http == 80 or https == 443),
24
- # false otherwise.
25
- #
26
- # param uri
27
- # return true if the specified URI is using a non-standard port, false otherwise
28
- #
29
- def self.default_port? uri
20
+ def default_port?(uri)
30
21
  scheme = uri.scheme.downcase
31
22
  port = uri.port
32
23
  port <= 0 || (port == 80 && scheme.eql?("http")) || (port == 443 && scheme.eql?("https"))
33
24
  end
34
25
 
35
- def self.encode_url value, path, canonical
36
-
37
- encoded = URI.escape value
38
-
39
- if canonical
26
+ def encode_url(value, path, canonical)
27
+ URI.escape(value.to_s).tap do |encoded|
28
+ if canonical
29
+ str_map = {'+' => '%20', '*' => '%2A', '%7E' => '~'}
40
30
 
41
- str_map = {'+' => '%20', '*' => '%2A', '%7E' => '~'}
42
-
43
- str_map.each do |key, str_value|
44
-
45
- if encoded.include? key
46
- encoded[key] = str_value
31
+ str_map.each do |key, str_value|
32
+ if encoded.include? key
33
+ encoded[key] = str_value
34
+ end
47
35
  end
48
36
 
49
- end
50
-
51
- # encoded['%7E'] = '~' --> yes, this is reversed (compared to the other two) intentionally
37
+ # encoded['%7E'] = '~' --> yes, this is reversed (compared to the other two) intentionally
52
38
 
53
- if path
54
-
55
- str = '%2F'
56
- if encoded.include? str
57
- encoded[str] = '/'
39
+ if path
40
+ str = '%2F'
41
+ if encoded.include? str
42
+ encoded[str] = '/'
43
+ end
58
44
  end
59
-
60
45
  end
61
-
62
46
  end
63
-
64
- encoded
65
-
66
47
  end
67
48
 
68
49
  end
69
-
70
50
  end
71
-
72
51
  end
@@ -13,117 +13,21 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- module Stormpath
16
+ class Stormpath::Resource::Account < Stormpath::Resource::Instance
17
+ include Stormpath::Resource::Status
17
18
 
18
- module Resource
19
+ prop_accessor :username, :email, :given_name, :middle_name,
20
+ :surname
21
+ prop_writer :password
22
+ prop_non_printable :password
19
23
 
20
- class Account < InstanceResource
24
+ belongs_to :directory
25
+ has_one :email_verification_token
21
26
 
22
- include Status
23
-
24
- USERNAME = "username"
25
- EMAIL = "email"
26
- PASSWORD = "password"
27
- GIVEN_NAME = "givenName"
28
- MIDDLE_NAME = "middleName"
29
- SURNAME = "surname"
30
- STATUS = "status"
31
- GROUPS = "groups"
32
- DIRECTORY = "directory"
33
- EMAIL_VERIFICATION_TOKEN = "emailVerificationToken"
34
- GROUP_MEMBERSHIPS = "groupMemberships"
35
-
36
- def get_username
37
- get_property USERNAME
38
- end
39
-
40
- def set_username username
41
- set_property USERNAME, username
42
- end
43
-
44
- def get_email
45
- get_property EMAIL
46
- end
47
-
48
- def set_email email
49
- set_property EMAIL, email
50
- end
51
-
52
- def set_password password
53
- set_property PASSWORD, password
54
- end
55
-
56
- def get_given_name
57
- get_property GIVEN_NAME
58
- end
59
-
60
- def set_given_name given_name
61
- set_property GIVEN_NAME, given_name
62
- end
63
-
64
- def get_middle_name
65
- get_property MIDDLE_NAME
66
- end
67
-
68
- def set_middle_name middle_name
69
- set_property MIDDLE_NAME, middle_name
70
- end
71
-
72
- def get_surname
73
- get_property SURNAME
74
- end
75
-
76
- def set_surname surname
77
- set_property SURNAME, surname
78
- end
79
-
80
- def get_status
81
- value = get_property STATUS
82
-
83
- if !value.nil?
84
- value = value.upcase
85
- end
86
-
87
- value
88
- end
89
-
90
- def set_status status
91
-
92
- if get_status_hash.has_key? status
93
- set_property STATUS, get_status_hash[status]
94
- end
95
-
96
- end
97
-
98
- def get_groups
99
- get_resource_property GROUPS, GroupList
100
- end
101
-
102
- def get_directory
103
- get_resource_property DIRECTORY, Directory
104
- end
105
-
106
- def get_email_verification_token
107
- get_resource_property EMAIL_VERIFICATION_TOKEN, EmailVerificationToken
108
- end
109
-
110
- def add_group group
111
-
112
- GroupMembership::_create self, group, data_store
113
-
114
- end
115
-
116
- def get_group_memberships
117
- get_resource_property GROUP_MEMBERSHIPS, GroupMembershipList
118
- end
119
-
120
- protected
121
- def printable_property? property_name
122
- PASSWORD != property_name
123
- end
124
-
125
- end
27
+ has_many :groups
28
+ has_many :group_memberships
126
29
 
30
+ def add_group group
31
+ client.group_memberships.create group: group, account: self
127
32
  end
128
-
129
33
  end
@@ -13,187 +13,51 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- module Stormpath
16
+ class Stormpath::Resource::Application < Stormpath::Resource::Instance
17
+ include Stormpath::Resource::Status
17
18
 
18
- module Resource
19
+ class LoadError < Stormpath::Error; end
19
20
 
20
- class Application < InstanceResource
21
+ prop_accessor :name, :description
21
22
 
22
- include Status
23
+ belongs_to :tenant
24
+ has_many :accounts, can: [:create]
25
+ has_many :password_reset_tokens, can: [:get, :create]
23
26
 
24
- NAME = "name"
25
- DESCRIPTION = "description"
26
- STATUS = "status"
27
- TENANT = "tenant"
28
- ACCOUNTS = "accounts"
29
- PASSWORD_RESET_TOKENS = "passwordResetTokens"
27
+ def self.load composite_url
28
+ begin
29
+ uri = URI(composite_url)
30
+ api_key_id, api_key_secret = uri.userinfo.split(':')
30
31
 
31
- def get_name
32
- get_property NAME
33
- end
32
+ client = Stormpath::Client.new api_key: {
33
+ id: api_key_id,
34
+ secret: api_key_secret
35
+ }
34
36
 
35
- def set_name name
36
- set_property NAME, name
37
- end
38
-
39
- def get_description
40
- get_property DESCRIPTION
41
- end
42
-
43
- def set_description description
44
- set_property DESCRIPTION, description
45
- end
46
-
47
- def get_status
48
- value = get_property STATUS
49
-
50
- if !value.nil?
51
- value = value.upcase
52
- end
53
-
54
- value
55
- end
56
-
57
- def set_status status
58
-
59
- if get_status_hash.has_key? status
60
- set_property STATUS, get_status_hash[status]
61
- end
62
-
63
- end
64
-
65
- def get_tenant
66
- get_resource_property TENANT, Tenant
67
- end
68
-
69
- def get_accounts
70
-
71
- get_resource_property ACCOUNTS, AccountList
72
- end
73
-
74
-
75
- #
76
- # Sends a password reset email for the specified account username or email address. The email will contain
77
- # a password reset link that the user can click or copy into their browser address bar.
78
- # <p/>
79
- # This method merely sends the password reset email that contains the link and nothing else. You will need to
80
- # handle the link requests and then reset the account's password as described in the
81
- # {@link #verify_password_reset_token} RDoc.
82
- #
83
- # @param account_username_or_email a username or email address of an Account that may login to the application.
84
- # @return the account corresponding to the specified username or email address.
85
- # @see #account_username_or_email
86
- #
87
- def send_password_reset_email account_username_or_email
88
-
89
- password_reset_token = create_password_reset_token account_username_or_email;
90
- password_reset_token.get_account
91
-
92
- end
93
-
94
- # Verifies a password reset token in a user-clicked link within an email.
95
- # <p/>
96
- # <h2>Base Link Configuration</h2>
97
- # You need to define the <em>Base</em> link that will process HTTP requests when users click the link in the
98
- # email as part of your Application's Workflow Configuration within the Stormpath UI Console. It must be a URL
99
- # served by your application's web servers. For example:
100
- # <pre>
101
- # https://www.myApplication.com/passwordReset
102
- # </pre>
103
- # <h2>Runtime Link Processing</h2>
104
- # When an application user clicks on the link in the email at runtime, your web server needs to process the request
105
- # and look for an {@code spToken} request parameter. You can then verify the {@code spToken}, and then finally
106
- # change the Account's password.
107
- # <p/>
108
- # Usage Example:
109
- # <p/>
110
- # Browser:
111
- # {@code GET https://www.myApplication/passwordReset?spToken=someTokenValueHere}
112
- # <p/>
113
- # Your code:
114
- # <pre>
115
- # token = #get the spToken query parameter
116
- #
117
- # account = application.verify_password_reset_token token
118
- #
119
- # //token has been verified - now set the new password with what the end-user submits:
120
- # account.set_password user_submitted_new_password
121
- # account.save
122
- # </pre>
123
- #
124
- # @param token the verification token, usually obtained as a request parameter by your application.
125
- # @return the Account matching the specified token.
126
- def verify_password_reset_token token
127
-
128
- href = get_password_reset_token_href
129
- href += '/' + token
130
-
131
- password_reset_props = Hash.new
132
- password_reset_props.store HREF_PROP_NAME, href
133
-
134
- password_reset_token = data_store.instantiate PasswordResetToken, password_reset_props
135
-
136
- password_reset_token.get_account
137
-
138
- end
139
-
140
- #
141
- # Authenticates an account's submitted principals and credentials (e.g. username and password). The account must
142
- # be in one of the Application's
143
- # <a href="http://www.stormpath.com/docs/managing-applications-login-sources">assigned Login Sources</a>. If not
144
- # in an assigned login source, the authentication attempt will fail.
145
- # <h2>Example</h2>
146
- # Consider the following username/password-based example:
147
- # <p/>
148
- # <pre>
149
- # request = UsernamePasswordRequest.new username, submittedRawPlaintextPassword, nil
150
- # account = appToTest.authenticate_account(request).get_account
151
- # </pre>
152
- #
153
- # @param request the authentication request representing an account's principals and credentials (e.g.
154
- # username/password) used to verify their identity.
155
- # @return the result of the authentication. The authenticated account can be obtained from
156
- # {@code result.}{@link Stormpath::Authentication::AuthenticationResult#get_account}.
157
- # @throws ResourceError if the authentication attempt fails.
158
- #
159
- def authenticate_account request
160
- response = Stormpath::Authentication::BasicAuthenticator.new data_store
161
- response.authenticate get_href, request
162
- end
163
-
164
- private
165
-
166
- def create_password_reset_token email
167
-
168
- href = get_password_reset_token_href
169
-
170
- password_reset_props = Hash.new
171
- password_reset_props.store 'email', email
172
-
173
- password_reset_token = data_store.instantiate PasswordResetToken, password_reset_props
174
-
175
- data_store.create href, password_reset_token, PasswordResetToken
176
-
177
- end
178
-
179
- def get_password_reset_token_href
180
-
181
- password_reset_tokens_href = get_property PASSWORD_RESET_TOKENS
182
-
183
- if !password_reset_tokens_href.nil? and
184
- password_reset_tokens_href.respond_to? 'empty?' and
185
- !password_reset_tokens_href.empty?
37
+ application_path = uri.path.slice(/\/applications(.)*$/)
38
+ client.applications.get(application_path)
39
+ rescue
40
+ raise LoadError
41
+ end
42
+ end
186
43
 
187
- return password_reset_tokens_href[HREF_PROP_NAME]
188
- end
44
+ def send_password_reset_email email
45
+ password_reset_token = create_password_reset_token email;
46
+ password_reset_token.account
47
+ end
189
48
 
190
- nil
49
+ def verify_password_reset_token token
50
+ password_reset_tokens.get(token).account
51
+ end
191
52
 
192
- end
53
+ def authenticate_account request
54
+ response = Stormpath::Authentication::BasicAuthenticator.new data_store
55
+ response.authenticate href, request
56
+ end
193
57
 
194
- end
58
+ private
195
59
 
60
+ def create_password_reset_token email
61
+ password_reset_tokens.create email: email
196
62
  end
197
-
198
63
  end
199
-