login-control 0.0.1
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/README.md +64 -0
- data/lib/login_control_module.rb +56 -0
- data/lib/login_control_view_helper.rb +45 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 576dfb778fef6f08e892aee929fb44b15ac51de82b8bca41a6d5b7fa5291cf1e
|
4
|
+
data.tar.gz: 15f8547991b86ae2f90b212cfc639ad67e8fd2556acd5851b7fc2b9894d6a337
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5668048e7774db39f276303cc32d9bf6e1538a3300c4118bea7c520eb32ee263203c9afb7b8a60a9ba48a76be4cdcf430029015ddd1ee334d587d1c52dcd7bd1
|
7
|
+
data.tar.gz: 480d115794fb3726822b090718eb5dd941aed63db411bb1931290944307cd8db0f6742db01c1ebac8a748d312372e05f474cbb2a29f19c08b0845d41d66b6f33
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
## Description
|
2
|
+
|
3
|
+
The Attempt here is to give more flexibility on multiple signup barriers, like captacha or 2fa. Based on the requests it gives you a decisive criteria of the barriers a user have to take for sign in. It stores a permanent cookie and a table for tracking requests for specific routes you track within a controller method.
|
4
|
+
|
5
|
+
its built for systems on higher security requirements, for users which have sign-in multiple times on same login.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
The `RequestControlViewHelper.captcha_tag?` delivers true if captcha is necessary and showed on a login form.
|
10
|
+
|
11
|
+
This decision is based on the status of failed login attempts, which is stored in rails-session-cookie.
|
12
|
+
|
13
|
+
For a user that had never a successfully login, `captcha_tag?` is always true. After first successful login, `captcha_tag?` is true if `attempts` are less than configured (default: 10) and `last_attempt` is older than `config.x.login_control.retry_after_seconds`
|
14
|
+
|
15
|
+
On localhost captcha is never required.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
`gem 'request-control'
|
20
|
+
|
21
|
+
run
|
22
|
+
```
|
23
|
+
$ bundle
|
24
|
+
$ rails g model login_control session_id:string login_name:string scope:string sign_in_success:integer attempts:integer last_attempt:datetime validate_captcha:boolean
|
25
|
+
$ rails db:migrate
|
26
|
+
```
|
27
|
+
|
28
|
+
Login Form
|
29
|
+
|
30
|
+
`include RequestControlViewHelper`
|
31
|
+
|
32
|
+
```
|
33
|
+
- if captcha_tag?
|
34
|
+
= hcaptcha xxx
|
35
|
+
```
|
36
|
+
|
37
|
+
Controller example for subclassed devise controller
|
38
|
+
|
39
|
+
```
|
40
|
+
class SessionsController < Devise::SessionsController
|
41
|
+
|
42
|
+
include RequestControlModule
|
43
|
+
|
44
|
+
def create
|
45
|
+
notice_request_attempt
|
46
|
+
if (captcha_validation? ? verify_hcaptcha(secret_key: ...) : true) && credentials-matched
|
47
|
+
super
|
48
|
+
notice_successful_request
|
49
|
+
else
|
50
|
+
redirect_to login_path, alert: 'captcha failed'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
## Configs
|
57
|
+
|
58
|
+
`config.x.login_control.attempts_allowed` integer, default: 10
|
59
|
+
|
60
|
+
`config.x.login_control.retry_after_seconds` integer, default: 30 (seconds) # => if, after a failed login, within status `:known`, within `attempts_allowed`, within `retry_after_seconds` `RequestControlViewHelper.captcha_tag?` returns true
|
61
|
+
|
62
|
+
`config.x.login_control.debug` boolean, default: false only for production
|
63
|
+
|
64
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module LoginControlModule
|
2
|
+
|
3
|
+
# check if captcha is to validate (does not store a cookie)
|
4
|
+
def captcha_validation?(scope: :global, login_name: nil)
|
5
|
+
rec = rc_record(scope, login_name)
|
6
|
+
rec ? rec.validate_captcha : true
|
7
|
+
end
|
8
|
+
|
9
|
+
# stores cookie, count-up sign_in_success, set attempts count to 1
|
10
|
+
|
11
|
+
def notice_successful_request(scope: :global, login_name: nil)
|
12
|
+
rec = find_or_build_rc_record(scope, login_name)
|
13
|
+
rec.sign_in_success = rec.sign_in_success.to_i + 1
|
14
|
+
rec.attempts = 1
|
15
|
+
rec.save!
|
16
|
+
logger.info "REQUEST-CONTROL => #{rec.sign_in_success}. successful request noticed" if debug_request_control
|
17
|
+
end
|
18
|
+
|
19
|
+
# stores cookie, counts up attempts
|
20
|
+
|
21
|
+
def notice_request_attempt(scope: :global, login_name: nil)
|
22
|
+
rec = find_or_build_rc_record(scope, login_name)
|
23
|
+
rec.attempts = rec.attempts.to_i + 1
|
24
|
+
rec.save!
|
25
|
+
logger.info "REQUEST-CONTROL => #{rec.attempts}. failed request noticed" if debug_request_control
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# read or store encrypted permanent cookie «login_control»
|
31
|
+
|
32
|
+
def find_or_build_rc_record(scope, login_name)
|
33
|
+
id = cookies.encrypted.permanent[:login_control]
|
34
|
+
if id.present?
|
35
|
+
rc_record(scope, login_name, id: id)
|
36
|
+
else
|
37
|
+
id = SecureRandom.hex(20)
|
38
|
+
cookies.encrypted.permanent[:login_control] = id
|
39
|
+
LoginControl.new(session_id: id, scope: scope, login_name: login_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# read cookie «login_control»
|
44
|
+
def rc_record(scope, login_name, id: cookies.encrypted.permanent[:login_control])
|
45
|
+
if id
|
46
|
+
LoginControl.find_by(session_id: id, scope: scope, login_name: login_name)
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def debug_request_control
|
53
|
+
Rails.configuration.x.login_control.debug || !Rails.env.production?
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module LoginControlViewHelper
|
2
|
+
|
3
|
+
# true if captcha tag is showed on a login form
|
4
|
+
# the result is saved in rails-session-cookie and later picked up by LoginControlModule.captcha_validation?
|
5
|
+
|
6
|
+
def captcha_tag?(scope: :global, login_name: nil)
|
7
|
+
debug = (Rails.configuration.x.login_control.debug || !Rails.env.production?)
|
8
|
+
rc_id = cookies.encrypted.permanent[:login_control]
|
9
|
+
if request.host == 'localhost'
|
10
|
+
logger.info 'REQUEST-CONTROL => no captcha because of localhost' if debug
|
11
|
+
false
|
12
|
+
elsif !rc_id.present?
|
13
|
+
logger.info 'REQUEST-CONTROL => captcha because no cookie stored yet' if debug
|
14
|
+
true
|
15
|
+
else
|
16
|
+
logger.info 'REQUEST-CONTROL => cookie found ...' if debug
|
17
|
+
rec = LoginControl.find_by(session_id: rc_id, scope: scope, login_name: login_name)
|
18
|
+
if !rec
|
19
|
+
logger.info 'REQUEST-CONTROL => captcha required because no record found(!)' if debug
|
20
|
+
true
|
21
|
+
else
|
22
|
+
logger.info 'REQUEST-CONTROL => record found ...' if debug
|
23
|
+
captcha_requested = true
|
24
|
+
|
25
|
+
attempts_allowed = (Rails.configuration.x.login_control.attempts_allowed || 10)
|
26
|
+
retry_after_seconds = (Rails.configuration.x.login_control.retry_after_seconds || 30)
|
27
|
+
logger.info "REQUEST-CONTROL => #{rec.attempts.to_i}. attempt (config.x.attempts_allowed: #{attempts_allowed})" if debug
|
28
|
+
|
29
|
+
if rec.attempts.to_i <= attempts_allowed
|
30
|
+
secs = Time.now - rec.updated_at
|
31
|
+
captcha_requested = retry_after_seconds.to_f >= secs
|
32
|
+
logger.info "REQUEST-CONTROL => captcha #{captcha_requested ? '' : 'NOT '}requested: config.x.retry_after_seconds(#{retry_after_seconds}) >= secs(#{secs})" if debug
|
33
|
+
end
|
34
|
+
|
35
|
+
rec.update!(validate_captcha: captcha_requested)
|
36
|
+
captcha_requested
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Text for ask a user for storing a cookie
|
42
|
+
|
43
|
+
REQUEST_CONTROL_COOKIE_TXT = "On first login attempt this service stores a permanent cookie. It tracks login attempts. Only aim is securing and, on the other hand, simplifying access."
|
44
|
+
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: login-control
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christian Sedlmair
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Based on Login Attempts check if captcha is necessary. It stores a permanent
|
14
|
+
cookie and uses a table for tracking login requests.
|
15
|
+
email: christian@sedlmair.ch
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- lib/login_control_module.rb
|
22
|
+
- lib/login_control_view_helper.rb
|
23
|
+
homepage: https://rubygems.org/gems/request-control
|
24
|
+
licenses:
|
25
|
+
- MIT
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubygems_version: 3.3.17
|
43
|
+
signing_key:
|
44
|
+
specification_version: 4
|
45
|
+
summary: Lib for simplifying and securing login.
|
46
|
+
test_files: []
|