monban-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +5 -0
  3. data/.git_release_request.rc.sh +7 -0
  4. data/.gitignore +9 -0
  5. data/.gitlab-ci.yml +12 -0
  6. data/.travis.yml +12 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +36 -0
  9. data/LICENSE +21 -0
  10. data/README.md +51 -0
  11. data/Rakefile +10 -0
  12. data/lib/monban/core/version.rb +5 -0
  13. data/lib/monban/domain/auth.rb +273 -0
  14. data/lib/monban/domain/password.rb +51 -0
  15. data/lib/monban/use_case/account/admin.rb +49 -0
  16. data/lib/monban/use_case/account/change/email.rb +60 -0
  17. data/lib/monban/use_case/account/change/login_id.rb +57 -0
  18. data/lib/monban/use_case/account/change/password.rb +45 -0
  19. data/lib/monban/use_case/account/change/roles.rb +51 -0
  20. data/lib/monban/use_case/account/fetch.rb +36 -0
  21. data/lib/monban/use_case/account/register.rb +50 -0
  22. data/lib/monban/use_case/account/search.rb +48 -0
  23. data/lib/monban/use_case/account/unregister.rb +39 -0
  24. data/lib/monban/use_case/auth/account.rb +43 -0
  25. data/lib/monban/use_case/auth/change/authy.rb +50 -0
  26. data/lib/monban/use_case/auth/change/password.rb +49 -0
  27. data/lib/monban/use_case/auth/token/authy.rb +51 -0
  28. data/lib/monban/use_case/auth/token/full.rb +51 -0
  29. data/lib/monban/use_case/auth/token/general.rb +33 -0
  30. data/lib/monban/use_case/auth/token/renew.rb +70 -0
  31. data/lib/monban/use_case/auth/token/reset.rb +99 -0
  32. data/lib/monban/use_case/auth/token.rb +39 -0
  33. data/lib/monban/use_case/auth/verify/authy.rb +43 -0
  34. data/lib/monban/use_case/auth/verify/password.rb +65 -0
  35. data/lib/monban/use_case/auth/verify/reset_token.rb +41 -0
  36. data/lib/monban/use_case/base.rb +15 -0
  37. data/monban-core.gemspec +39 -0
  38. metadata +184 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: da14b037f6f5171c93f2ec483393120901d721468871940fd676cade914bfc34
4
+ data.tar.gz: 91806b518b767aedbacd499c10179513b9ac9a309d21e030ecca9acd83b06f36
5
+ SHA512:
6
+ metadata.gz: a7250d275a21b7b0a49ac33426a2312bfcf28320c58de424a9ca52e4b8c139428625bb1385e6a1030475130dfeb9131ad63172d482ef81e69aa8ca1c552d4c2a
7
+ data.tar.gz: fd6d37edbcf0cd6041ae4c9617ed68bc724f1ede672d8829de4cc0d61d89e4dc78ec6ed8b58fff007c95198b31c1e8e44f23ad151456641549a4a4ccbed41a61
data/.envrc ADDED
@@ -0,0 +1,5 @@
1
+ export APP_ROOT=$(pwd)
2
+
3
+ export DOCKER_WRAPPER_IMAGE_ruby=2.5.1
4
+
5
+ export GIT_RELEASE_VERSION_FILE=lib/monban/core/version.rb
@@ -0,0 +1,7 @@
1
+ git_release_request_dump_version_local(){
2
+ bundle
3
+ git add Gemfile.lock
4
+ }
5
+ git_release_request_after_tag(){
6
+ git push github master --tags
7
+ }
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /vendor/
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,12 @@
1
+ image: ruby:2.5.1
2
+
3
+ cache:
4
+ paths:
5
+ - vendor/
6
+
7
+ test:
8
+ except:
9
+ - tags
10
+ script:
11
+ - bundle
12
+ - bundle exec rake
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.2
6
+ deploy:
7
+ provider: rubygems
8
+ gem: monban-core
9
+ on:
10
+ tags: true
11
+ api_key:
12
+ secure: "DdkHZ3zEXiaetZt7r36tbT0+ECCE6meR//2sJo0AgVE5rpWS1tCd0/D/bjrYKyZha7NlGyyXp0/2A5RPdfJVIZiapVHpB2XozZNKcNVvXoeQRchAlHHIbpapiseo+28gCQK5h/K30m7OExCTtoXSGSIUCLeOkIz0AFC/7Aor1e/+38DLK2NvFleYJfAtWedNSe75exygTpca48zwgkwnNyRwenLoA8poDXJKjkElS9Y/Wf/L4HBiCBu2C/9pGZz0Tdf2lMxD9lNsGYbCxCtA3381f6zFt7cNhGY6a6GWvoZInep6C11PrYUT97TmLpgeTqB7hgYtsXxVP6kEEVlPmrNg22nR91YFSohFpvWAlZ7kCUOiQntwo4n0vLtPVvW0nXx0sAk0I2/mIH41Lnj7ekz5BQW0/4K1SQjjqlTQ0J9FVMGd7Y3ewjRmjcJ99kf0vPTqs5zERe9pfnaYzhYkyQggp4ZeGfeOYzYGdx0wtLQDFzMp8KlbpNTSIcpIFhVzvW40or4CdMMDZqn8iFBvfvvRsEpClUnRaybIJJJltIDnb3qCDNoIebDSbuVINjQufApCDXmpoWz3Ec2Gy1YWraaqzWEqfWhKUbTxuEkOxyXFUDtXrWTwKJ67J+JDx/lH5vQFU61Wt8O2lVswuvqhCxO24qFQiYpT8Onjw+GgCSI="
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in monban-core.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ monban-core (0.1.0)
5
+ getto-initialize_with (~> 1.0)
6
+ getto-params (~> 1.0)
7
+ jwt (~> 2.1)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ docile (1.3.1)
13
+ getto-initialize_with (1.0.1)
14
+ getto-params (1.0.1)
15
+ json (2.1.0)
16
+ jwt (2.1.0)
17
+ minitest (5.11.3)
18
+ rake (10.5.0)
19
+ simplecov (0.16.1)
20
+ docile (~> 1.1)
21
+ json (>= 1.8, < 3)
22
+ simplecov-html (~> 0.10.0)
23
+ simplecov-html (0.10.2)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ bundler (~> 1.16)
30
+ minitest (~> 5.0)
31
+ monban-core!
32
+ rake (~> 10.0)
33
+ simplecov (~> 0.16)
34
+
35
+ BUNDLED WITH
36
+ 1.16.6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 shun@getto.systems
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # monban-core
2
+
3
+ [rubygems: monban-core](https://rubygems.org/gems/monban-core)
4
+
5
+ [Monban](https://github.com/getto-systems/rubygems-monban-core) - core
6
+
7
+ The authentication plugin for web api based on jwt
8
+
9
+
10
+ ###### Table of Contents
11
+
12
+ - [Requirements](#Requirements)
13
+ - [Usage](#Usage)
14
+ - [License](#License)
15
+
16
+ <a id="Requirements"></a>
17
+ ## Requirements
18
+
19
+ - developed on ruby: 2.5.1
20
+
21
+
22
+ <a id="Usage"></a>
23
+ ## Usage
24
+
25
+ ## Install
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ ```ruby
30
+ gem 'monban-core'
31
+ ```
32
+
33
+ And then execute:
34
+
35
+ ```
36
+ $ bundle
37
+ ```
38
+
39
+ Or install it yourself as:
40
+
41
+ ```
42
+ $ gem install monban-core
43
+ ```
44
+
45
+
46
+ <a id="License"></a>
47
+ ## License
48
+
49
+ monban/core is licensed under the [MIT](LICENSE) license.
50
+
51
+ Copyright &copy; since 2018 shun@getto.systems
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,5 @@
1
+ module Monban
2
+ module Core
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,273 @@
1
+ require "getto/initialize_with"
2
+
3
+ require "getto/params"
4
+ require "jwt"
5
+
6
+ module Monban
7
+ module Domain
8
+ module Auth
9
+ class DecodeError < RuntimeError; end
10
+ class SettingError < RuntimeError; end
11
+
12
+ module Claim
13
+ def iss(type)
14
+ "#{type}@#{jwt_service}"
15
+ end
16
+
17
+ def authy_roles
18
+ ["registered","unregistered"]
19
+ end
20
+ end
21
+
22
+ class Encoder
23
+ include Getto::InitializeWith
24
+ include Claim
25
+
26
+ initialize_with :jwt_service, :jwt_secret, :jwt_algorithm, :time
27
+
28
+ def full(public_id:, roles:, expired_at:)
29
+ to_jwt full_payload(
30
+ public_id: public_id,
31
+ roles: roles,
32
+ expired_at: expired_at,
33
+ )
34
+ end
35
+
36
+ def authy(public_id:, authy_id:, expired_at:)
37
+ to_jwt authy_payload(
38
+ public_id: public_id,
39
+ authy_id: authy_id,
40
+ expired_at: expired_at,
41
+ )
42
+ end
43
+
44
+ def reset(public_id:, reset_token:, expired_at:)
45
+ to_jwt reset_payload(
46
+ public_id: public_id,
47
+ reset_token: reset_token,
48
+ expired_at: expired_at,
49
+ )
50
+ end
51
+
52
+ private
53
+
54
+ def to_jwt(payload)
55
+ ::JWT.encode(payload, jwt_secret, jwt_algorithm)
56
+ end
57
+
58
+ def full_payload(public_id:, roles:, expired_at:)
59
+ {
60
+ iss: iss(:full),
61
+ iat: time.now.to_i,
62
+ exp: expired_at.to_i,
63
+
64
+ sub: public_id,
65
+ aud: roles,
66
+ }
67
+ end
68
+
69
+ def authy_payload(public_id:, authy_id:, expired_at:)
70
+ {
71
+ iss: iss(:authy),
72
+ iat: time.now.to_i,
73
+ exp: expired_at.to_i,
74
+
75
+ sub: public_id,
76
+ aud: authy_id ? authy_roles.first : authy_roles.last,
77
+ }
78
+ end
79
+
80
+ def reset_payload(public_id:, reset_token:, expired_at:)
81
+ {
82
+ iss: iss(:reset),
83
+ iat: time.now.to_i,
84
+ exp: expired_at.to_i,
85
+
86
+ sub: public_id,
87
+ aud: reset_token,
88
+ }
89
+ end
90
+ end
91
+
92
+ class Decoder
93
+ include Getto::InitializeWith
94
+ include Claim
95
+
96
+ initialize_with :jwt_service, :jwt_secret, :jwt_algorithm,
97
+ :public_id_length, :reset_token_length,
98
+ :all_roles, :allow_full_access
99
+
100
+ def decode(token, type:, roles:)
101
+ case type
102
+ when :full
103
+ full_format ::JWT.decode(
104
+ token,
105
+ jwt_secret,
106
+ true,
107
+
108
+ algorithm: jwt_algorithm,
109
+
110
+ verify_iss: true, iss: iss(type),
111
+ verify_iat: true,
112
+ verify_sub: true,
113
+
114
+ verify_aud: !!roles, aud: ([*roles] + allow_full_access).map(&:to_s),
115
+ )
116
+ when :authy
117
+ authy_format ::JWT.decode(
118
+ token,
119
+ jwt_secret,
120
+ true,
121
+
122
+ algorithm: jwt_algorithm,
123
+
124
+ verify_iss: true, iss: iss(type),
125
+ verify_iat: true,
126
+ verify_sub: true,
127
+
128
+ verify_aud: roles[:only_registered], aud: authy_roles.first,
129
+ )
130
+ when :reset
131
+ reset_format ::JWT.decode(
132
+ token,
133
+ jwt_secret,
134
+ true,
135
+
136
+ algorithm: jwt_algorithm,
137
+
138
+ verify_iss: true, iss: iss(type),
139
+ verify_sub: true,
140
+ verify_iat: true,
141
+ )
142
+ else
143
+ raise SettingError, "decode: invalid type: #{type}"
144
+ end
145
+ rescue ::JWT::DecodeError => e
146
+ error! e.message
147
+ end
148
+
149
+ private
150
+
151
+ def full_format(result)
152
+ payload, header = result
153
+
154
+ validate_header! header
155
+ validate_full! payload
156
+
157
+ {
158
+ public_id: payload["sub"],
159
+ roles: payload["aud"],
160
+ }
161
+ end
162
+
163
+ def authy_format(result)
164
+ payload, header = result
165
+
166
+ validate_header! header
167
+ validate_authy! payload
168
+
169
+ {
170
+ public_id: payload["sub"],
171
+ }
172
+ end
173
+
174
+ def reset_format(result)
175
+ payload, header = result
176
+
177
+ validate_header! header
178
+ validate_reset! payload
179
+
180
+ {
181
+ public_id: payload["sub"],
182
+ reset_token: payload["aud"],
183
+ }
184
+ end
185
+
186
+
187
+ def validate_header!(header)
188
+ Getto::Params.new.validate(header) do |v|
189
+ v.hash_strict(
190
+ "alg" => v.string, # verified by jwt
191
+ )
192
+ end or error! "header: #{header}"
193
+ end
194
+
195
+ def validate_full!(payload)
196
+ aud = (all_roles + allow_full_access).map(&:to_s)
197
+
198
+ Getto::Params.new.validate(payload) do |v|
199
+ v.hash_strict(
200
+ "iss" => v.string, # verified by jwt
201
+ "iat" => v.integer, # verified by jwt
202
+ "exp" => v.integer, # verified by jwt
203
+
204
+ "sub" => v.combine([v.string, v.length(public_id_length)]){|val| error! "sub: #{val}" },
205
+ "aud" => v.array_include(aud) {|val| error! "aud: #{val}" },
206
+ )
207
+ end or error! "full: #{payload}"
208
+ end
209
+
210
+ def validate_authy!(payload)
211
+ Getto::Params.new.validate(payload) do |v|
212
+ v.hash_strict(
213
+ "iss" => v.string, # verified by jwt
214
+ "iat" => v.integer, # verified by jwt
215
+ "exp" => v.integer, # verified by jwt
216
+
217
+ "sub" => v.combine([v.string, v.length(public_id_length)]){|val| error! "sub: #{val}" },
218
+ "aud" => v.combine([v.string, v.in(authy_roles)]) {|val| error! "aud: #{val}" },
219
+ )
220
+ end or error! "authy: #{payload}"
221
+ end
222
+
223
+ def validate_reset!(payload)
224
+ Getto::Params.new.validate(payload) do |v|
225
+ v.hash_strict(
226
+ "iss" => v.string, # verified by jwt
227
+ "iat" => v.integer, # verified by jwt
228
+ "exp" => v.integer, # verified by jwt
229
+
230
+ "sub" => v.combine([v.string, v.length(public_id_length)]) {|val| error! "sub: #{val}" },
231
+ "aud" => v.combine([v.string, v.length(reset_token_length)]){|val| error! "aud: #{val}" },
232
+ )
233
+ end or error! "reset: #{payload}"
234
+ end
235
+
236
+ def error!(message)
237
+ raise DecodeError, message
238
+ end
239
+ end
240
+
241
+ class Authorized
242
+ include Getto::InitializeWith
243
+
244
+ initialize_with :authorized_secret, :authorized_algorithm
245
+
246
+ def decode(token)
247
+ ::JWT.decode(
248
+ token,
249
+ authorized_secret,
250
+ true,
251
+ {
252
+ algorithm: authorized_algorithm,
253
+ }
254
+ ).first
255
+ rescue ::JWT::DecodeError => e
256
+ error! e.message
257
+ end
258
+
259
+ def encode(account)
260
+ ::JWT.encode(
261
+ account,
262
+ authorized_secret,
263
+ authorized_algorithm
264
+ )
265
+ end
266
+
267
+ def error!(message)
268
+ raise DecodeError, message
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,51 @@
1
+ require "getto/initialize_with"
2
+
3
+ module Monban
4
+ module Domain
5
+ module Password
6
+ module Helper
7
+ private
8
+
9
+ def digest(password:)
10
+ password_digest.call(password + password_secret)
11
+ end
12
+ end
13
+
14
+ class Creater
15
+ include Getto::InitializeWith
16
+ include Helper
17
+
18
+ initialize_with(
19
+ password_creater: [:create],
20
+ password_digest: [:call],
21
+ password_secret: String,
22
+ )
23
+
24
+ def create(password:)
25
+ password_creater.create(
26
+ password: digest(password: password),
27
+ )
28
+ end
29
+ end
30
+
31
+ class Checker
32
+ include Getto::InitializeWith
33
+ include Helper
34
+
35
+ initialize_with(
36
+ password_checker: [:hash_secret],
37
+ password_digest: [:call],
38
+ password_secret: String,
39
+ )
40
+
41
+ def hash_secret(password:, salt:)
42
+ password_checker.hash_secret(
43
+ password: digest(password: password),
44
+ salt: salt,
45
+ )
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,49 @@
1
+ require "monban/use_case/base"
2
+
3
+ require "getto/params"
4
+
5
+ module Monban
6
+ module UseCase
7
+ module Account
8
+ class Admin < Base
9
+
10
+ initialize_with(
11
+ time: [:now],
12
+ repository: [
13
+ :transaction,
14
+ :reset_password_email_account,
15
+ :insert_account,
16
+ :update_roles,
17
+ :update_reset_password_email,
18
+ ],
19
+
20
+ admin_email: String,
21
+ admin_roles: Array,
22
+ )
23
+
24
+ def register
25
+ repository.transaction do
26
+ unless account_id = repository.reset_password_email_account(email: admin_email)
27
+ account_id = repository.insert_account(now: time.now)
28
+ end
29
+
30
+ repository.update_roles(
31
+ account_id: account_id,
32
+ roles: admin_roles,
33
+ now: time.now,
34
+ )
35
+
36
+ repository.update_reset_password_email(
37
+ account_id: account_id,
38
+ email: admin_email,
39
+ now: time.now,
40
+ )
41
+
42
+ nil
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ require "monban/use_case/base"
2
+
3
+ require "getto/params"
4
+
5
+ module Monban
6
+ module UseCase
7
+ module Account
8
+ module Change
9
+ class Email < Base
10
+
11
+ initialize_with(
12
+ error: [:invalid_params!, :not_found!, :conflict!],
13
+ time: [:now],
14
+ repository: [
15
+ :transaction,
16
+ :account_exists?,
17
+ :reset_password_email_account,
18
+ :update_reset_password_email,
19
+ :reset_password_email,
20
+ ],
21
+ )
22
+
23
+ def change(params)
24
+ Getto::Params.new.validate(params) do |v|
25
+ v.hash(
26
+ account_id: v.integer {|val| param_error!(account_id: val) },
27
+ email: v.combine([v.string]){|val| param_error!(email: val) },
28
+ )
29
+ end or param_error!(params: params)
30
+
31
+ repository.transaction do
32
+ unless repository.account_exists?(account_id: params[:account_id])
33
+ error.not_found! "account_id: #{params[:account_id]}"
34
+ end
35
+
36
+ email_account = repository.reset_password_email_account(email: params[:email])
37
+
38
+ if email_account && email_account != params[:account_id]
39
+ error.conflict! "email: #{params[:email]}"
40
+ end
41
+
42
+ unless email_account
43
+ repository.update_reset_password_email(
44
+ account_id: params[:account_id],
45
+ email: params[:email],
46
+ now: time.now,
47
+ )
48
+ end
49
+
50
+ {
51
+ email: repository.reset_password_email(account_id: params[:account_id]),
52
+ }
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,57 @@
1
+ require "monban/use_case/base"
2
+
3
+ require "getto/params"
4
+
5
+ module Monban
6
+ module UseCase
7
+ module Account
8
+ module Change
9
+ class LoginId < Base
10
+
11
+ initialize_with(
12
+ error: [:invalid_params!, :not_found!, :conflict!],
13
+ time: [:now],
14
+ repository: [
15
+ :transaction,
16
+ :account_exists?,
17
+ :login_id_account,
18
+ :update_login_id,
19
+ :login_id,
20
+ ],
21
+ )
22
+
23
+ def change(params)
24
+ Getto::Params.new.validate(params) do |v|
25
+ v.hash(
26
+ account_id: v.integer{|val| param_error!(account_id: val) },
27
+ login_id: v.string {|val| param_error!(login_id: val) },
28
+ )
29
+ end or param_error!(params: params)
30
+
31
+ repository.transaction do
32
+ unless repository.account_exists?(account_id: params[:account_id])
33
+ error.not_found! "account_id: #{params[:account_id]}"
34
+ end
35
+
36
+ login_id_account = repository.login_id_account(login_id: params[:login_id])
37
+ if login_id_account && login_id_account != params[:account_id]
38
+ error.conflict! "login_id: #{params[:login_id]}"
39
+ end
40
+
41
+ unless login_id_account
42
+ repository.update_login_id(
43
+ account_id: params[:account_id],
44
+ login_id: params[:login_id],
45
+ now: time.now,
46
+ )
47
+ end
48
+
49
+ repository.login_id(account_id: params[:account_id])
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end