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.
- data/.gitignore +6 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +27 -0
- data/CHANGES.md +21 -1
- data/Gemfile +1 -2
- data/README.md +457 -11
- data/Rakefile +15 -1
- data/lib/stormpath-sdk.rb +52 -33
- data/lib/stormpath-sdk/{resource/group_list.rb → api_key.rb} +5 -9
- data/lib/stormpath-sdk/auth/authentication_result.rb +3 -13
- data/lib/stormpath-sdk/auth/basic_authenticator.rb +5 -11
- data/lib/stormpath-sdk/auth/basic_login_attempt.rb +6 -8
- data/lib/stormpath-sdk/auth/username_password_request.rb +2 -5
- data/lib/stormpath-sdk/cache/cache.rb +54 -0
- data/lib/stormpath-sdk/cache/cache_entry.rb +33 -0
- data/lib/stormpath-sdk/cache/cache_manager.rb +22 -0
- data/lib/stormpath-sdk/cache/cache_stats.rb +35 -0
- data/lib/stormpath-sdk/cache/memory_store.rb +29 -0
- data/lib/stormpath-sdk/cache/redis_store.rb +32 -0
- data/lib/stormpath-sdk/client.rb +111 -0
- data/lib/stormpath-sdk/data_store.rb +241 -0
- data/lib/stormpath-sdk/{client/api_key.rb → error.rb} +16 -10
- data/lib/stormpath-sdk/{util → ext}/hash.rb +1 -2
- data/lib/stormpath-sdk/http/authc/sauthc1_signer.rb +8 -4
- data/lib/stormpath-sdk/http/http_client_request_executor.rb +8 -7
- data/lib/stormpath-sdk/http/request.rb +4 -8
- data/lib/stormpath-sdk/{util/request_utils.rb → http/utils.rb} +17 -38
- data/lib/stormpath-sdk/resource/account.rb +12 -108
- data/lib/stormpath-sdk/resource/application.rb +35 -171
- data/lib/stormpath-sdk/resource/associations.rb +97 -0
- data/lib/stormpath-sdk/resource/base.rb +256 -0
- data/lib/stormpath-sdk/resource/collection.rb +94 -0
- data/lib/stormpath-sdk/resource/directory.rb +11 -68
- data/lib/stormpath-sdk/resource/email_verification_token.rb +3 -9
- data/lib/stormpath-sdk/resource/error.rb +4 -38
- data/lib/stormpath-sdk/resource/expansion.rb +28 -0
- data/lib/stormpath-sdk/resource/group.rb +8 -66
- data/lib/stormpath-sdk/resource/group_membership.rb +4 -55
- data/lib/stormpath-sdk/resource/{application_list.rb → instance.rb} +7 -13
- data/lib/stormpath-sdk/resource/password_reset_token.rb +5 -23
- data/lib/stormpath-sdk/resource/status.rb +22 -28
- data/lib/stormpath-sdk/resource/tenant.rb +5 -52
- data/lib/stormpath-sdk/resource/utils.rb +43 -13
- data/lib/stormpath-sdk/util/assert.rb +5 -15
- data/lib/stormpath-sdk/version.rb +3 -3
- data/spec/api_key_spec.rb +19 -0
- data/spec/auth/basic_authenticator_spec.rb +25 -0
- data/spec/auth/sauthc1_signer_spec.rb +42 -0
- data/spec/cache/cache_entry_spec.rb +157 -0
- data/spec/cache/cache_spec.rb +89 -0
- data/spec/cache/cache_stats_spec.rb +106 -0
- data/spec/client_spec.rb +538 -0
- data/spec/data_store_spec.rb +130 -0
- data/spec/resource/account_spec.rb +74 -0
- data/spec/resource/application_spec.rb +148 -0
- data/spec/resource/base_spec.rb +114 -0
- data/spec/resource/collection_spec.rb +169 -0
- data/spec/resource/directory_spec.rb +30 -0
- data/spec/resource/expansion_spec.rb +100 -0
- data/spec/resource/group_spec.rb +49 -0
- data/spec/spec_helper.rb +135 -0
- data/spec/support/resource_factory.rb +48 -0
- data/spec/support/resource_matchers.rb +27 -0
- data/spec/support/test_cache_stores.rb +9 -0
- data/spec/support/test_request_executor.rb +11 -0
- data/stormpath-sdk.gemspec +14 -4
- data/support/api.rb +55 -0
- metadata +214 -44
- data/lib/stormpath-sdk/client/client.rb +0 -38
- data/lib/stormpath-sdk/client/client_application.rb +0 -38
- data/lib/stormpath-sdk/client/client_application_builder.rb +0 -351
- data/lib/stormpath-sdk/client/client_builder.rb +0 -305
- data/lib/stormpath-sdk/ds/data_store.rb +0 -210
- data/lib/stormpath-sdk/ds/resource_factory.rb +0 -37
- data/lib/stormpath-sdk/resource/account_list.rb +0 -32
- data/lib/stormpath-sdk/resource/collection_resource.rb +0 -91
- data/lib/stormpath-sdk/resource/directory_list.rb +0 -30
- data/lib/stormpath-sdk/resource/group_membership_list.rb +0 -32
- data/lib/stormpath-sdk/resource/instance_resource.rb +0 -28
- data/lib/stormpath-sdk/resource/resource.rb +0 -327
- data/lib/stormpath-sdk/resource/resource_error.rb +0 -47
- data/test/client/client.yml +0 -16
- data/test/client/client_application_builder_spec.rb +0 -114
- data/test/client/client_builder_spec.rb +0 -176
- data/test/client/read_spec.rb +0 -254
- data/test/client/write_spec.rb +0 -420
- data/test/resource/resource_spec.rb +0 -41
- 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.
|
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,
|
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
|
-
|
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
|
-
|
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
|
36
|
-
|
37
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
16
|
+
class Stormpath::Resource::Account < Stormpath::Resource::Instance
|
17
|
+
include Stormpath::Resource::Status
|
17
18
|
|
18
|
-
|
19
|
+
prop_accessor :username, :email, :given_name, :middle_name,
|
20
|
+
:surname
|
21
|
+
prop_writer :password
|
22
|
+
prop_non_printable :password
|
19
23
|
|
20
|
-
|
24
|
+
belongs_to :directory
|
25
|
+
has_one :email_verification_token
|
21
26
|
|
22
|
-
|
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
|
-
|
16
|
+
class Stormpath::Resource::Application < Stormpath::Resource::Instance
|
17
|
+
include Stormpath::Resource::Status
|
17
18
|
|
18
|
-
|
19
|
+
class LoadError < Stormpath::Error; end
|
19
20
|
|
20
|
-
|
21
|
+
prop_accessor :name, :description
|
21
22
|
|
22
|
-
|
23
|
+
belongs_to :tenant
|
24
|
+
has_many :accounts, can: [:create]
|
25
|
+
has_many :password_reset_tokens, can: [:get, :create]
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
32
|
+
client = Stormpath::Client.new api_key: {
|
33
|
+
id: api_key_id,
|
34
|
+
secret: api_key_secret
|
35
|
+
}
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
188
|
-
|
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
|
-
|
49
|
+
def verify_password_reset_token token
|
50
|
+
password_reset_tokens.get(token).account
|
51
|
+
end
|
191
52
|
|
192
|
-
|
53
|
+
def authenticate_account request
|
54
|
+
response = Stormpath::Authentication::BasicAuthenticator.new data_store
|
55
|
+
response.authenticate href, request
|
56
|
+
end
|
193
57
|
|
194
|
-
|
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
|
-
|