monban-core 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.
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