aikotoba 0.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +334 -0
- data/Rakefile +18 -0
- data/app/controllers/aikotoba/accounts_controller.rb +61 -0
- data/app/controllers/aikotoba/application_controller.rb +13 -0
- data/app/controllers/aikotoba/confirms_controller.rb +103 -0
- data/app/controllers/aikotoba/recoveries_controller.rb +120 -0
- data/app/controllers/aikotoba/sessions_controller.rb +78 -0
- data/app/controllers/aikotoba/unlocks_controller.rb +103 -0
- data/app/controllers/concerns/aikotoba/authenticatable.rb +40 -0
- data/app/controllers/concerns/aikotoba/protection/session_fixation_attack.rb +21 -0
- data/app/controllers/concerns/aikotoba/protection/timing_atack.rb +23 -0
- data/app/mailers/aikotoba/account_mailer.rb +24 -0
- data/app/mailers/aikotoba/application_mailer.rb +5 -0
- data/app/models/aikotoba/account/confirmation_token.rb +22 -0
- data/app/models/aikotoba/account/recovery_token.rb +22 -0
- data/app/models/aikotoba/account/service/authentication.rb +65 -0
- data/app/models/aikotoba/account/service/confirmation.rb +31 -0
- data/app/models/aikotoba/account/service/lock.rb +42 -0
- data/app/models/aikotoba/account/service/recovery.rb +31 -0
- data/app/models/aikotoba/account/service/registration.rb +30 -0
- data/app/models/aikotoba/account/unlock_token.rb +22 -0
- data/app/models/aikotoba/account/value/password.rb +48 -0
- data/app/models/aikotoba/account/value/token.rb +18 -0
- data/app/models/aikotoba/account.rb +120 -0
- data/app/models/concerns/aikotoba/enabled_feature_checkable.rb +41 -0
- data/app/models/concerns/aikotoba/token_encryptable.rb +27 -0
- data/app/views/aikotoba/account_mailer/confirm.html.erb +3 -0
- data/app/views/aikotoba/account_mailer/recover.html.erb +3 -0
- data/app/views/aikotoba/account_mailer/unlock.html.erb +3 -0
- data/app/views/aikotoba/accounts/new.html.erb +19 -0
- data/app/views/aikotoba/common/_errors.html.erb +9 -0
- data/app/views/aikotoba/common/_message.html.erb +8 -0
- data/app/views/aikotoba/confirms/new.html.erb +14 -0
- data/app/views/aikotoba/recoveries/edit.html.erb +15 -0
- data/app/views/aikotoba/recoveries/new.html.erb +14 -0
- data/app/views/aikotoba/sessions/_links.html.erb +20 -0
- data/app/views/aikotoba/sessions/new.html.erb +16 -0
- data/app/views/aikotoba/unlocks/new.html.erb +21 -0
- data/app/views/layouts/aikotoba/application.html.erb +11 -0
- data/app/views/layouts/aikotoba/mailer.html.erb +13 -0
- data/app/views/layouts/aikotoba/mailer.text.erb +1 -0
- data/config/locales/en.yml +49 -0
- data/config/routes.rb +32 -0
- data/db/migrate/20211204121532_create_aikotoba_accounts.rb +50 -0
- data/lib/aikotoba/constraints/confirmable_constraint.rb +7 -0
- data/lib/aikotoba/constraints/lockable_constraint.rb +7 -0
- data/lib/aikotoba/constraints/recoverable_constraint.rb +7 -0
- data/lib/aikotoba/constraints/registerable_constraint.rb +7 -0
- data/lib/aikotoba/engine.rb +7 -0
- data/lib/aikotoba/errors.rb +7 -0
- data/lib/aikotoba/test/authentication_helper.rb +48 -0
- data/lib/aikotoba/version.rb +5 -0
- data/lib/aikotoba.rb +45 -0
- data/lib/tasks/aikotoba_tasks.rake +4 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b0d639e691f230c3bcb02686f18ecef8dab6a4fecf9310121c8e581a4079ab62
|
4
|
+
data.tar.gz: 169419bd1301489504a2d1049ee9f1c939432b95b0b6eea33df195b43bee0c6d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3bae2cfbf6a1ccacc972abf66057b83b85261ac642800a563ea81b416d83f19f154049d5c9ccc8af0fa6d49c31eec38a0a19cdf3e9ffcd1920e556f02941d0f4
|
7
|
+
data.tar.gz: 347e3fbde3a2af2a02828faabebd94ca67adfe1c71e97f93c7c42e1ab2d83ee6bdee3df30ad5af7d760509f0d41b1f82fedbe9981423c2bec52fbc8ffa9122aa
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Madogiwa
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,334 @@
|
|
1
|
+
[](https://github.com/madogiwa0124/aikotoba/actions/workflows/ci.yml)
|
2
|
+
|
3
|
+
# Aikotoba
|
4
|
+
|
5
|
+
Aikotoba meaning password in Japanese.
|
6
|
+
|
7
|
+
Aikotoba is a Rails engine that makes it easy to implement simple email and password authentication.
|
8
|
+
|
9
|
+
**Motivation**
|
10
|
+
|
11
|
+
- Simple implementation using the Rails engine.
|
12
|
+
- Modern hashing algorithm.
|
13
|
+
- Separate the authentication logic from User.
|
14
|
+
- Implementation for multiple DB.
|
15
|
+
- Encrypting tokens using Active Record Encryption.
|
16
|
+
|
17
|
+
**Features**
|
18
|
+
|
19
|
+
- Authenticatable : Authenticate account using email and password.
|
20
|
+
- Registrable(optional) : Register an account using your email address and password.
|
21
|
+
- Confirmable(optional) : After registration, send an email with a token to confirm account.
|
22
|
+
- Lockable(optional) : Lock account if make a mistake with password more than a certain number of times.
|
23
|
+
- Recoverable(optional) : Recover account by resetting password.
|
24
|
+
|
25
|
+
[For more information](#features)
|
26
|
+
|
27
|
+
## Installation
|
28
|
+
|
29
|
+
Add this line to your application's Gemfile:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
gem 'aikotoba'
|
33
|
+
```
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Getting Start
|
38
|
+
|
39
|
+
Aikotoba use `Aikotoba::Account` for authentication. Add it to the migration for `Aikotoba::Account`.
|
40
|
+
|
41
|
+
```sh
|
42
|
+
$ bin/rails aikotoba:install:migrations
|
43
|
+
```
|
44
|
+
|
45
|
+
Mount `Aikotoba::Engine` your application.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
Rails.application.routes.draw do
|
49
|
+
mount Aikotoba::Engine => "/"
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Aikotoba enabled routes for registration(`/sign_up`) and authentication(`/sign_in`).
|
54
|
+
|
55
|
+
include `Aikotoba::Authenticatable` to the controller(ex. `ApplicationController`) use authentication.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class ApplicationController < ActionController::Base
|
59
|
+
include Aikotoba::Authenticatable
|
60
|
+
|
61
|
+
# NOTE: You can implement the get authenticated account process as follows.
|
62
|
+
alias_method :current_account, :aikotoba_current_account
|
63
|
+
helper_method :current_account
|
64
|
+
|
65
|
+
# NOTE: You can implement the authorization process as follows
|
66
|
+
def authenticate_account!
|
67
|
+
return if current_account
|
68
|
+
redirect_to aikotoba.new_session_path, flash: {alert: "Oops. You need to Signed up or Signed in." }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## Features
|
74
|
+
|
75
|
+
### Authenticatable
|
76
|
+
|
77
|
+
Authenticate an account using email and password.
|
78
|
+
|
79
|
+
| HTTP Verb | Path | Overview |
|
80
|
+
| --------- | --------- | ----------------------------------------- |
|
81
|
+
| GET | /sign_in | Display sign in page. |
|
82
|
+
| POST | /sign_in | Create a login session by authenticating. |
|
83
|
+
| DELETE | /sign_out | Clear aikotoba login session. |
|
84
|
+
|
85
|
+
Aikotoba enable helper methods for authentication. The method name can be changed by `alias_method`.
|
86
|
+
|
87
|
+
- `aikotoba_current_account` : Returns the logged in instance of `Aikotoba::Account`.
|
88
|
+
|
89
|
+
### Registrable
|
90
|
+
|
91
|
+
To enable it, set `Aikotoba.registerable` to `true`. (It is enabled by default.)
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
Aikotoba.registerable = true
|
95
|
+
```
|
96
|
+
|
97
|
+
Register an account using email and password.
|
98
|
+
|
99
|
+
| HTTP Verb | Path | Overview |
|
100
|
+
| --------- | -------- | --------------------- |
|
101
|
+
| GET | /sign_up | Display sign up page. |
|
102
|
+
| POST | /sign_up | Create an account. |
|
103
|
+
|
104
|
+
The password is stored as a hash in [Argon2](https://github.com/technion/ruby-argon2).
|
105
|
+
|
106
|
+
### Confirmable
|
107
|
+
|
108
|
+
To enable it, set `Aikotoba.confirmable` to `true`.
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
Aikotoba.confirmable = true
|
112
|
+
```
|
113
|
+
|
114
|
+
Aikotoba enable routes for confirmation account. Also, when account registers, a confirmation email is sent to the email address. Only accounts that are confirmed will be authenticated.
|
115
|
+
|
116
|
+
| HTTP Verb | Path | Overview |
|
117
|
+
| --------- | --------------- | -------------------------------------- |
|
118
|
+
| GET | /confirm | Display page for create confirm token. |
|
119
|
+
| POST | /confirm | Create a confirm token to account. |
|
120
|
+
| GET | /confirm/:token | Confirm account by token. |
|
121
|
+
|
122
|
+
### Lockable
|
123
|
+
|
124
|
+
To enable it, set `Aikotoba.lockable` to `true`.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
Aikotoba.lockable = true
|
128
|
+
```
|
129
|
+
|
130
|
+
Aikotoba enables a route to unlock an account. Also, if the authentication fails a certain number of times, the account will be locked. Only accounts that are not locked will be authenticated.
|
131
|
+
|
132
|
+
| HTTP Verb | Path | Overview |
|
133
|
+
| --------- | -------------- | ------------------------------------- |
|
134
|
+
| GET | /unlock | Display page for create unlock token. |
|
135
|
+
| POST | /unlock | Create a unlock token to account. |
|
136
|
+
| GET | /unlock/:token | Unlock account by token. |
|
137
|
+
|
138
|
+
### Recoverable
|
139
|
+
|
140
|
+
To enable it, set `Aikotoba.recoverable` to `true`.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
Aikotoba.recoverable = true
|
144
|
+
```
|
145
|
+
|
146
|
+
Aikotoba enables a route to recover an account by password reset.
|
147
|
+
|
148
|
+
| HTTP Verb | Path | Overview |
|
149
|
+
| --------- | --------------- | --------------------------------------------------- |
|
150
|
+
| GET | /recover | Display page for create recover token. |
|
151
|
+
| POST | /recover | Create a recover token to account. |
|
152
|
+
| GET | /recover/:token | Display page for recover account by password reset. |
|
153
|
+
| PATCH | /recover/:token | Recover account by password reset. |
|
154
|
+
|
155
|
+
## Configuration
|
156
|
+
|
157
|
+
The following configuration parameters are supported. You can override it. (ex. `initializers/aikotoba.rb`)
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
require 'aikotoba'
|
161
|
+
|
162
|
+
Aikotoba.parent_controller = "ApplicationController"
|
163
|
+
Aikotoba.parent_mailer = "ActionMailer::Base"
|
164
|
+
Aikotoba.mailer_sender = "from@example.com"
|
165
|
+
Aikotoba.email_format = /\A[^\s]+@[^\s]+\z/
|
166
|
+
Aikotoba.prevent_timing_atack = true
|
167
|
+
Aikotoba.password_pepper = "aikotoba-default-pepper"
|
168
|
+
Aikotoba.password_length_range = 8..100
|
169
|
+
Aikotoba.sign_in_path = "/sign_in"
|
170
|
+
Aikotoba.sign_out_path = "/sign_out"
|
171
|
+
Aikotoba.after_sign_in_path = "/"
|
172
|
+
Aikotoba.after_sign_out_path = "/sign_in"
|
173
|
+
|
174
|
+
# for registerable
|
175
|
+
Aikotoba.registerable = true
|
176
|
+
Aikotoba.sign_up_path = "/sign_up"
|
177
|
+
|
178
|
+
# for confirmable
|
179
|
+
Aikotoba.confirmable = false
|
180
|
+
Aikotoba.confirm_path = "/confirm"
|
181
|
+
Aikotoba.confirmation_token_expiry = 1.day
|
182
|
+
|
183
|
+
# for lockable
|
184
|
+
Aikotoba.lockable = false
|
185
|
+
Aikotoba.unlock_path = "/unlock"
|
186
|
+
Aikotoba.max_failed_attempts = 10
|
187
|
+
Aikotoba.unlock_token_expiry = 1.day
|
188
|
+
|
189
|
+
# for Recoverable
|
190
|
+
Aikotoba.recoverable = false
|
191
|
+
Aikotoba.recover_path = "/recover"
|
192
|
+
Aikotoba.recovery_token_expiry = 4.hours
|
193
|
+
```
|
194
|
+
|
195
|
+
## Tips
|
196
|
+
|
197
|
+
### Customize Message
|
198
|
+
|
199
|
+
All Messages are managed by `i18n` and can be freely overridden.
|
200
|
+
|
201
|
+
### Manually create an `Aikotoba::Account` for authentication.
|
202
|
+
|
203
|
+
By running the following script, you can hash and store passwords.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
Aikotoba::Account.create!(email: "sample@example.com", password: "password")
|
207
|
+
Aikotoba::Account.authenticate_by(attributes: {email: "sample@example.com", password: "password"})
|
208
|
+
# => created account instance.
|
209
|
+
```
|
210
|
+
|
211
|
+
### Create other model with `Aikotoba::Account`.
|
212
|
+
|
213
|
+
You can override `Aikotoba::AccountsController#after_create_account_process` to create the other models together.
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
require 'aikotoba'
|
217
|
+
|
218
|
+
Rails.application.config.to_prepare do
|
219
|
+
Aikotoba::AccountsController.class_eval do
|
220
|
+
def after_create_account_process
|
221
|
+
profile = Profile.new(nickname: "foo")
|
222
|
+
profile.save!
|
223
|
+
@account.update!(authenticate_target: profile)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class Profile < ApplicationRecord
|
229
|
+
has_one :account, class_name: 'Aikotoba::Account'
|
230
|
+
end
|
231
|
+
|
232
|
+
current_account.profile #=> Profile instance
|
233
|
+
profile.account #=> Aikotoba::Account instance
|
234
|
+
```
|
235
|
+
|
236
|
+
### Do something on before, after, failure.
|
237
|
+
|
238
|
+
Controllers provides methods to execute the overridden process.
|
239
|
+
|
240
|
+
For example, if you want to record an error log when the account creation fails, you can do the following.
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
require 'aikotoba'
|
244
|
+
|
245
|
+
Rails.application.config.to_prepare do
|
246
|
+
Aikotoba::AccountsController.class_eval do
|
247
|
+
def failed_create_account_process(e)
|
248
|
+
logger.error(e)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
### Using encrypted token
|
255
|
+
|
256
|
+
Tokens can be encrypted using Active Record Encryption, introduced in Active Record 7 and later.
|
257
|
+
To use it, enable Aikotoba.encipted_token in the initializer.
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
Aikotoba.encypted_token = true
|
261
|
+
```
|
262
|
+
|
263
|
+
### How to identify the controller provided.
|
264
|
+
|
265
|
+
The controller provided by Aikotoba is designed to inherit from `Aikotoba::ApplicationController`.
|
266
|
+
|
267
|
+
Therefore, when implementing authorization based on login status, you can disable only the controllers provided by Aikotoba as follows.
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
class ApplicationController < ApplicationController
|
271
|
+
include Aikotoba::Authenticatable
|
272
|
+
|
273
|
+
alias_method :current_account, :aikotoba_current_account
|
274
|
+
|
275
|
+
before_action :authenticate_account!, unless: :aikotoba_controller?
|
276
|
+
|
277
|
+
def authenticate_account!
|
278
|
+
return if current_account
|
279
|
+
redirect_to aikotoba.new_session_path, flash: {alert: "Oops. You need to Signed up or Signed in." }
|
280
|
+
end
|
281
|
+
|
282
|
+
private
|
283
|
+
|
284
|
+
def aikotoba_controller?
|
285
|
+
is_a?(::Aikotoba::ApplicationController)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
290
|
+
### Testing
|
291
|
+
|
292
|
+
You can use a helper to login/logout by Aikotoba.
|
293
|
+
:warning: It only supports rack testing.
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
require "aikotoba/test/authentication_helper"
|
297
|
+
require "test_helper"
|
298
|
+
|
299
|
+
class HelperTest < ActionDispatch::SystemTestCase
|
300
|
+
include Aikotoba::Test::AuthenticationHelper::System
|
301
|
+
driven_by :rack_test
|
302
|
+
|
303
|
+
def setup
|
304
|
+
email, password = ["email@example.com", "password"]
|
305
|
+
@account = ::Aikotoba::Account.build_by(attributes: {email: email, password: password})
|
306
|
+
@account.save
|
307
|
+
end
|
308
|
+
|
309
|
+
test "sign_in by helper" do
|
310
|
+
aikotoba_sign_in(@account)
|
311
|
+
visit "/sensitives"
|
312
|
+
assert_selector "h1", text: "Sensitive Page"
|
313
|
+
click_on "Sign out"
|
314
|
+
assert_selector ".message", text: "Signed out."
|
315
|
+
end
|
316
|
+
|
317
|
+
test "sign_out by helper" do
|
318
|
+
aikotoba_sign_in(@account)
|
319
|
+
visit "/sensitives"
|
320
|
+
aikotoba_sign_out
|
321
|
+
visit "/sensitives"
|
322
|
+
assert_selector "h1", text: "Sign in"
|
323
|
+
assert_selector ".message", text: "Oops. You need to Signed up or Signed in."
|
324
|
+
end
|
325
|
+
end
|
326
|
+
```
|
327
|
+
|
328
|
+
## Contributing
|
329
|
+
|
330
|
+
Bug reports and pull requests are welcome on GitHub.
|
331
|
+
|
332
|
+
## License
|
333
|
+
|
334
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
|
6
|
+
load "rails/tasks/statistics.rake"
|
7
|
+
|
8
|
+
require "bundler/gem_tasks"
|
9
|
+
|
10
|
+
require "rake/testtask"
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << "test"
|
14
|
+
t.pattern = "test/**/*_test.rb"
|
15
|
+
t.verbose = false
|
16
|
+
end
|
17
|
+
|
18
|
+
task default: :test
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aikotoba
|
4
|
+
class AccountsController < ApplicationController
|
5
|
+
def new
|
6
|
+
@account = build_account({email: "", password: ""})
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
@account = build_account(accounts_params.to_h.symbolize_keys)
|
11
|
+
ActiveRecord::Base.transaction do
|
12
|
+
before_create_account_process
|
13
|
+
save_with_callbacks!(@account)
|
14
|
+
after_create_account_process
|
15
|
+
end
|
16
|
+
redirect_to after_sign_up_path, flash: {notice: successed_message}
|
17
|
+
rescue ActiveRecord::RecordInvalid => e
|
18
|
+
failed_create_account_process(e)
|
19
|
+
flash[:alert] = failed_message
|
20
|
+
render :new, status: :unprocessable_entity
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def accounts_params
|
26
|
+
params.require(:account).permit(:email, :password)
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_account(params)
|
30
|
+
Account.build_by(attributes: params)
|
31
|
+
end
|
32
|
+
|
33
|
+
def save_with_callbacks!(account)
|
34
|
+
Account::Service::Registration.call!(account: account)
|
35
|
+
end
|
36
|
+
|
37
|
+
def after_sign_up_path
|
38
|
+
aikotoba.new_session_path
|
39
|
+
end
|
40
|
+
|
41
|
+
def successed_message
|
42
|
+
I18n.t(".aikotoba.messages.registration.success")
|
43
|
+
end
|
44
|
+
|
45
|
+
def failed_message
|
46
|
+
I18n.t(".aikotoba.messages.registration.failed")
|
47
|
+
end
|
48
|
+
|
49
|
+
# NOTE: Methods to override if you want to do something before account creation.
|
50
|
+
def before_create_account_process
|
51
|
+
end
|
52
|
+
|
53
|
+
# NOTE: Methods to override if you want to do something after account creation.
|
54
|
+
def after_create_account_process
|
55
|
+
end
|
56
|
+
|
57
|
+
# NOTE: Methods to override if you want to do something failed account creation.
|
58
|
+
def failed_create_account_process(e)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aikotoba
|
4
|
+
class ApplicationController < Aikotoba.parent_controller.constantize
|
5
|
+
include EnabledFeatureCheckable
|
6
|
+
|
7
|
+
helper_method :confirmable?, :lockable?, :recoverable?, :registerable?
|
8
|
+
|
9
|
+
def aikotoba_controller?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aikotoba
|
4
|
+
class ConfirmsController < ApplicationController
|
5
|
+
include Protection::TimingAtack
|
6
|
+
|
7
|
+
before_action :prevent_timing_atack, only: [:update]
|
8
|
+
|
9
|
+
def new
|
10
|
+
@account = build_account({email: "", password: ""})
|
11
|
+
end
|
12
|
+
|
13
|
+
def create
|
14
|
+
account = find_by_send_token_account!(confirm_accounts_params)
|
15
|
+
before_send_confirmation_token_process
|
16
|
+
send_token_account!(account)
|
17
|
+
after_send_confirmation_token_process
|
18
|
+
redirect_to success_send_confirmation_token_path, flash: {notice: success_send_confirmation_token_message}
|
19
|
+
rescue ActiveRecord::RecordNotFound => e
|
20
|
+
failed_send_confirmation_token_process(e)
|
21
|
+
@account = build_account({email: "", password: ""})
|
22
|
+
flash[:alert] = failed_send_confirmation_token_message
|
23
|
+
render :new, status: :unprocessable_entity
|
24
|
+
end
|
25
|
+
|
26
|
+
def update
|
27
|
+
account = find_by_has_token_account!(params)
|
28
|
+
before_confirm_process
|
29
|
+
confirm_account!(account)
|
30
|
+
after_confirm_process
|
31
|
+
redirect_to after_confirmed_path, flash: {notice: confirmed_message}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def confirm_accounts_params
|
37
|
+
params.require(:account).permit(:email)
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_account(params)
|
41
|
+
Account.build_by(attributes: params)
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_by_send_token_account!(params)
|
45
|
+
Account.unconfirmed.find_by!(email: params[:email])
|
46
|
+
end
|
47
|
+
|
48
|
+
def send_token_account!(account)
|
49
|
+
Account::Service::Confirmation.create_token!(account: account, notify: true)
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_by_has_token_account!(params)
|
53
|
+
Account::ConfirmationToken.active.find_by!(token: params[:token]).account
|
54
|
+
end
|
55
|
+
|
56
|
+
def confirm_account!(account)
|
57
|
+
# NOTE: Confirmation is done using URL tokens, so it is done in the writing role.
|
58
|
+
ActiveRecord::Base.connected_to(role: :writing) do
|
59
|
+
Account::Service::Confirmation.confirm!(account: account)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def after_confirmed_path
|
64
|
+
aikotoba.new_session_path
|
65
|
+
end
|
66
|
+
|
67
|
+
def success_send_confirmation_token_path
|
68
|
+
aikotoba.new_session_path
|
69
|
+
end
|
70
|
+
|
71
|
+
def confirmed_message
|
72
|
+
I18n.t(".aikotoba.messages.confirmation.success")
|
73
|
+
end
|
74
|
+
|
75
|
+
def success_send_confirmation_token_message
|
76
|
+
I18n.t(".aikotoba.messages.confirmation.sent")
|
77
|
+
end
|
78
|
+
|
79
|
+
def failed_send_confirmation_token_message
|
80
|
+
I18n.t(".aikotoba.messages.confirmation.failed")
|
81
|
+
end
|
82
|
+
|
83
|
+
# NOTE: Methods to override if you want to do something before send confirm token.
|
84
|
+
def before_send_confirmation_token_process
|
85
|
+
end
|
86
|
+
|
87
|
+
# NOTE: Methods to override if you want to do something after send confirm token.
|
88
|
+
def after_send_confirmation_token_process
|
89
|
+
end
|
90
|
+
|
91
|
+
# NOTE: Methods to override if you want to do something failed send confirm token.
|
92
|
+
def failed_send_confirmation_token_process(e)
|
93
|
+
end
|
94
|
+
|
95
|
+
# NOTE: Methods to override if you want to do something before confirm.
|
96
|
+
def before_confirm_process
|
97
|
+
end
|
98
|
+
|
99
|
+
# NOTE: Methods to override if you want to do something after confirm.
|
100
|
+
def after_confirm_process
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aikotoba
|
4
|
+
class RecoveriesController < ApplicationController
|
5
|
+
include Protection::TimingAtack
|
6
|
+
|
7
|
+
before_action :prevent_timing_atack, only: [:edit, :update]
|
8
|
+
|
9
|
+
def new
|
10
|
+
@account = build_account({email: "", password: ""})
|
11
|
+
end
|
12
|
+
|
13
|
+
def create
|
14
|
+
account = find_by_send_token_account!(send_recovery_token_params)
|
15
|
+
before_send_recovery_token_process
|
16
|
+
send_recovery_token!(account)
|
17
|
+
after_send_recovery_token_process
|
18
|
+
redirect_to success_send_recovery_token_path, flash: {notice: success_send_recovery_token_message}
|
19
|
+
rescue ActiveRecord::RecordNotFound => e
|
20
|
+
failed_send_recovery_token_process(e)
|
21
|
+
@account = build_account({email: "", password: ""})
|
22
|
+
flash[:alert] = failed_send_recovery_token_message
|
23
|
+
render :new, status: :unprocessable_entity
|
24
|
+
end
|
25
|
+
|
26
|
+
def edit
|
27
|
+
@account = find_by_has_token_account!(params)
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
@account = find_by_has_token_account!(params)
|
32
|
+
before_recover_process
|
33
|
+
recover_account!(@account, recover_accounts_params[:password])
|
34
|
+
after_recover_process
|
35
|
+
redirect_to success_recovered_path, flash: {notice: success_recovered_message}
|
36
|
+
rescue ActiveRecord::RecordInvalid => e
|
37
|
+
failed_recover_process(e)
|
38
|
+
flash[:alert] = failed_message
|
39
|
+
render :edit, status: :unprocessable_entity
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def send_recovery_token_params
|
45
|
+
params.require(:account).permit(:email)
|
46
|
+
end
|
47
|
+
|
48
|
+
def recover_accounts_params
|
49
|
+
params.require(:account).permit(:password)
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_account(params)
|
53
|
+
Account.build_by(attributes: params)
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_by_send_token_account!(params)
|
57
|
+
Account.find_by!(email: params[:email])
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_by_has_token_account!(params)
|
61
|
+
Account::RecoveryToken.active.find_by!(token: params[:token]).account
|
62
|
+
end
|
63
|
+
|
64
|
+
def send_recovery_token!(account)
|
65
|
+
Account::Service::Recovery.create_token!(account: account, notify: true)
|
66
|
+
end
|
67
|
+
|
68
|
+
def recover_account!(account, new_password)
|
69
|
+
Account::Service::Recovery.recover!(account: account, new_password: new_password)
|
70
|
+
end
|
71
|
+
|
72
|
+
def success_recovered_path
|
73
|
+
aikotoba.new_session_path
|
74
|
+
end
|
75
|
+
|
76
|
+
def success_send_recovery_token_path
|
77
|
+
aikotoba.new_session_path
|
78
|
+
end
|
79
|
+
|
80
|
+
def failed_message
|
81
|
+
I18n.t(".aikotoba.messages.recovery.failed")
|
82
|
+
end
|
83
|
+
|
84
|
+
def success_recovered_message
|
85
|
+
I18n.t(".aikotoba.messages.recovery.success")
|
86
|
+
end
|
87
|
+
|
88
|
+
def success_send_recovery_token_message
|
89
|
+
I18n.t(".aikotoba.messages.recovery.sent")
|
90
|
+
end
|
91
|
+
|
92
|
+
def failed_send_recovery_token_message
|
93
|
+
I18n.t(".aikotoba.messages.recovery.sent_failed")
|
94
|
+
end
|
95
|
+
|
96
|
+
# NOTE: Methods to override if you want to do something before send recover token.
|
97
|
+
def before_send_recovery_token_process
|
98
|
+
end
|
99
|
+
|
100
|
+
# NOTE: Methods to override if you want to do something after send recover token.
|
101
|
+
def after_send_recovery_token_process
|
102
|
+
end
|
103
|
+
|
104
|
+
# NOTE: Methods to override if you want to do something failed send recover token.
|
105
|
+
def failed_send_recovery_token_process(e)
|
106
|
+
end
|
107
|
+
|
108
|
+
# NOTE: Methods to override if you want to do something before recover.
|
109
|
+
def before_recover_process
|
110
|
+
end
|
111
|
+
|
112
|
+
# NOTE: Methods to override if you want to do something after recover.
|
113
|
+
def after_recover_process
|
114
|
+
end
|
115
|
+
|
116
|
+
# NOTE: Methods to override if you want to do something failed recover.
|
117
|
+
def failed_recover_process(e)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|