stormpath-sdk 0.4.0 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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
-