booth 0.0.8 → 0.0.9
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/config/locales/de.yml +3 -0
- data/config/locales/en.yml +3 -0
- data/lib/booth/adminland/incognitos/start.rb +114 -0
- data/lib/booth/adminland.rb +8 -0
- data/lib/booth/request.rb +2 -1
- data/lib/booth/requests/authentication.rb +1 -1
- data/lib/booth/testing/support/assert_partial.rb +1 -1
- data/lib/booth/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88710c6c007c94f19ab956a66dc1592665f9de10f786ef5cd5eb8fb2b386fbee
|
|
4
|
+
data.tar.gz: 70f45c4a38f515c779ebda2705c142df7ca8ffb1279d5c5f49c2cf90fdacebfb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d08a6b4a1fa1f7b520952777509041f46232b31152c281d0851dbbbf32296fb84b8f4b62883ecb0e657800edbaec3e7a9fd12af09d753e65090e1dd505d355a
|
|
7
|
+
data.tar.gz: f5273c88aab4cb7425755b58f2e0eade47307748ae9fbbeae0e0fa0ab2ea8cff24e668dc3278bb83adf701ee1fa865780bd08e284c7a34366d079b745c4b6dea
|
data/CHANGELOG.md
CHANGED
data/config/locales/de.yml
CHANGED
|
@@ -21,6 +21,7 @@ de:
|
|
|
21
21
|
domain_mismatch: Dieser Benutzername kann sich nicht hier einloggen.
|
|
22
22
|
email_too_long: The provided email is too long. It must be less at most %{maximum} characters long.
|
|
23
23
|
email_too_short: The provided email is too short. It should be at least %{minimum} characters long.
|
|
24
|
+
incognito_started: Sie sind jetzt im Namen eines anderen Kontos angemeldet.
|
|
24
25
|
incompatible_webauth_device: Sorry, this device cannot be used. Please try another webauth device.
|
|
25
26
|
invalid_domain: Die Domain "%{domain}" ist keine gültige Domain.
|
|
26
27
|
invalid_email_characters: The provided email address contains invalid characters.
|
|
@@ -33,6 +34,7 @@ de:
|
|
|
33
34
|
missing_secret_key: Missing the secret key parameter. Is the URL correct?
|
|
34
35
|
mode_username_and_webauth_description: Auch bekannt als WebAuthentication (WebAuthn), FIDO2, CTAP2, Apple Passkey (Touch ID, Face ID).
|
|
35
36
|
mode_username_and_webauth_title: Hardwareschlüssel (empfohlen)
|
|
37
|
+
not_logged_in_as_impersonator: Sie müssen angemeldet sein, um sich als jemand anderen auszugeben.
|
|
36
38
|
onboarding_timeout: Sie hatten %{lifespan_hours} Stunden to access this onboarding link, but it took too long.
|
|
37
39
|
permanently_blocked: You failed too many times for this account. Please contact customer service.
|
|
38
40
|
remote_login_path: fernlogin
|
|
@@ -41,6 +43,7 @@ de:
|
|
|
41
43
|
session_revoked: Das Gerät mit der IP %{ip} wurde erfolgreich ausgeloggt.
|
|
42
44
|
some_authenticator_removed: Hardware key successfully deleted.
|
|
43
45
|
successfully_logged_out: Sie haben sich erfolgreich ausgeloggt.
|
|
46
|
+
target_credential_not_found: Das Zielkonto wurde nicht gefunden.
|
|
44
47
|
try_again_cooldown: Sie können es es in %{distance_of_time_until_cooldown} erneut probieren.
|
|
45
48
|
uninitialized_credential: This account has not been initialized yet. Please contact support.
|
|
46
49
|
unknown_email: We don't know that email.
|
data/config/locales/en.yml
CHANGED
|
@@ -21,6 +21,7 @@ en:
|
|
|
21
21
|
domain_mismatch: This username cannot log in here.
|
|
22
22
|
email_too_long: The provided email is too long. It must be less at most %{maximum} characters long.
|
|
23
23
|
email_too_short: The provided email is too short. It should be at least %{minimum} characters long.
|
|
24
|
+
incognito_started: You are now acting on behalf of another account.
|
|
24
25
|
incompatible_webauth_device: Sorry, this device cannot be used. Please try another webauth device.
|
|
25
26
|
invalid_domain: The Domain "%{domain}" is not a valid domain name.
|
|
26
27
|
invalid_email_characters: The provided email address contains invalid characters.
|
|
@@ -33,6 +34,7 @@ en:
|
|
|
33
34
|
missing_secret_key: Missing the secret key parameter. Is the URL correct?
|
|
34
35
|
mode_username_and_webauth_description: Also known as WebAuthentication (WebAuthn), FIDO2, CTAP2, Apple Passkey (Touch ID, Face ID).
|
|
35
36
|
mode_username_and_webauth_title: Hardware key (recommended)
|
|
37
|
+
not_logged_in_as_impersonator: You must be logged in to impersonate someone.
|
|
36
38
|
onboarding_timeout: You had %{lifespan_hours} hours to access this onboarding link, but it took too long.
|
|
37
39
|
permanently_blocked: You failed too many times for this account. Please contact customer service.
|
|
38
40
|
remote_login_path: remote
|
|
@@ -41,6 +43,7 @@ en:
|
|
|
41
43
|
session_revoked: The logged in device with the IP %{ip} has successfully been logged out.
|
|
42
44
|
some_authenticator_removed: Hardware key successfully deleted.
|
|
43
45
|
successfully_logged_out: Logout successful.
|
|
46
|
+
target_credential_not_found: The target account could not be found.
|
|
44
47
|
try_again_cooldown: You may try again in %{distance_of_time_until_cooldown}.
|
|
45
48
|
uninitialized_credential: This account has not been initialized yet. Please contact support.
|
|
46
49
|
unknown_email: We don't know that email.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Booth
|
|
4
|
+
module Adminland
|
|
5
|
+
module Incognitos
|
|
6
|
+
# The admin (impersonator) specifies their own +scope+ and a +target_scope+.
|
|
7
|
+
# +request+ is coerced with the impersonator's +scope+ (+request.authentication+ checks the
|
|
8
|
+
# impersonator's scope). We create a new session for the target credential and store the
|
|
9
|
+
# impersonator's credential_id as +incognito_credential_id+. If +target_scope+ matches the
|
|
10
|
+
# impersonator's scope, we revoke the impersonator's session before logging in as the target.
|
|
11
|
+
class Start
|
|
12
|
+
include Calls
|
|
13
|
+
include ::Booth::Logging
|
|
14
|
+
|
|
15
|
+
option :scope, ::Booth::Coercers::Scope
|
|
16
|
+
option :request, ::Booth::Coercers::Request
|
|
17
|
+
option :target_credential_id
|
|
18
|
+
option :target_scope
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
do_check_impersonator_logged_in
|
|
22
|
+
.on_success { do_find_target_credential }
|
|
23
|
+
.on_success { do_compare_domain }
|
|
24
|
+
.on_success { do_compare_scope }
|
|
25
|
+
.on_success { do_create_session }
|
|
26
|
+
.on_success { do_revoke_impersonator_if_same_scope }
|
|
27
|
+
.on_success { do_login }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_accessor :impersonator_credential_id, :target_credential, :session
|
|
33
|
+
|
|
34
|
+
def do_check_impersonator_logged_in
|
|
35
|
+
unless request.authentication.logged_in?
|
|
36
|
+
log { 'No impersonator logged in' }
|
|
37
|
+
return Tron.failure :not_logged_in_as_impersonator,
|
|
38
|
+
public_message: I18n.t('booth.not_logged_in_as_impersonator')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
self.impersonator_credential_id = request.authentication.credential_id
|
|
42
|
+
log { "Impersonator credential_id=#{impersonator_credential_id}" }
|
|
43
|
+
Tron.success :impersonator_captured
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def do_find_target_credential
|
|
47
|
+
self.target_credential = ::Booth::Models::Credential.find_by(id: target_credential_id)
|
|
48
|
+
|
|
49
|
+
if target_credential
|
|
50
|
+
log { "Target credential ##{target_credential.id} found" }
|
|
51
|
+
Tron.success :target_credential_found
|
|
52
|
+
else
|
|
53
|
+
Tron.failure :target_credential_not_found,
|
|
54
|
+
public_message: I18n.t('booth.target_credential_not_found')
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def do_compare_domain
|
|
59
|
+
::Booth::Comparisons::Domain.call(
|
|
60
|
+
expected: target_credential.domain,
|
|
61
|
+
actual: request.host,
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def do_compare_scope
|
|
66
|
+
::Booth::Comparisons::Scope.call(
|
|
67
|
+
expected: target_credential.scope,
|
|
68
|
+
actual: target_scope,
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def do_create_session
|
|
73
|
+
self.session = ::Booth::Models::Session.create!(
|
|
74
|
+
credential: target_credential,
|
|
75
|
+
incognito_credential_id: impersonator_credential_id,
|
|
76
|
+
agent: request.agent,
|
|
77
|
+
most_recent_ip: request.ip,
|
|
78
|
+
)
|
|
79
|
+
log { "Created incognito session ##{session.id}" }
|
|
80
|
+
Tron.success :session_created
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def do_revoke_impersonator_if_same_scope
|
|
84
|
+
return Tron.success(:no_revoke_needed) unless same_scope?
|
|
85
|
+
|
|
86
|
+
impersonator_session = ::Booth::Models::Session.find_by(
|
|
87
|
+
id: request.authentication.session_id,
|
|
88
|
+
)
|
|
89
|
+
return Tron.failure(:impersonator_session_not_found) unless impersonator_session
|
|
90
|
+
|
|
91
|
+
log { "Revoking impersonator session ##{impersonator_session.id}" }
|
|
92
|
+
impersonator_session.update!(revoked_at: Time.current, revoke_reason: :impersonation)
|
|
93
|
+
|
|
94
|
+
Tron.success :impersonator_session_revoked
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def do_login
|
|
98
|
+
target_auth.login(session:)
|
|
99
|
+
log { "Incognito started: #{target_credential.username}" }
|
|
100
|
+
Tron.success :incognito_started,
|
|
101
|
+
public_message: I18n.t('booth.incognito_started')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def same_scope?
|
|
105
|
+
target_scope == request.scope
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def target_auth
|
|
109
|
+
@target_auth ||= request.authentication(scope: target_scope)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
data/lib/booth/adminland.rb
CHANGED
|
@@ -44,6 +44,14 @@ module Booth
|
|
|
44
44
|
::Booth::Adminland::Onboardings::Destroy.call(...)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
# -----------------
|
|
48
|
+
# Incognito Helpers
|
|
49
|
+
# -----------------
|
|
50
|
+
|
|
51
|
+
def self.start_incognito(...)
|
|
52
|
+
::Booth::Adminland::Incognitos::Start.call(...)
|
|
53
|
+
end
|
|
54
|
+
|
|
47
55
|
# ----------------
|
|
48
56
|
# Internal Helpers
|
|
49
57
|
# ----------------
|
data/lib/booth/request.rb
CHANGED
|
@@ -4,6 +4,7 @@ module Booth
|
|
|
4
4
|
# Convenience wrapper for Rack::Request.
|
|
5
5
|
class Request
|
|
6
6
|
include ::Booth::Logging
|
|
7
|
+
|
|
7
8
|
# Initializes a new Booth::Request instance.
|
|
8
9
|
#
|
|
9
10
|
# **Parameters:**
|
|
@@ -77,7 +78,7 @@ module Booth
|
|
|
77
78
|
::Booth::Core::Geolocation.lookup(ip)
|
|
78
79
|
end
|
|
79
80
|
|
|
80
|
-
def authentication
|
|
81
|
+
def authentication(scope: self.scope)
|
|
81
82
|
::Booth::Requests::Authentication.new(scope:, warden: request.env['warden'])
|
|
82
83
|
end
|
|
83
84
|
|
|
@@ -54,7 +54,7 @@ module Booth
|
|
|
54
54
|
# Because doing this manually is very hard (would have to play through every scenario).
|
|
55
55
|
def take_screenshot
|
|
56
56
|
screenshot_dir.mkpath
|
|
57
|
-
path = screenshot_dir.join(
|
|
57
|
+
path = screenshot_dir.join(partial.gsub(/[^a-zA-Z0-9._-]/, '_'))
|
|
58
58
|
page.save_screenshot(path.to_s) # rubocop:disable Lint/Debugger
|
|
59
59
|
log { "Screenshot saved to `#{path}`" }
|
|
60
60
|
end
|
data/lib/booth/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: booth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- halo
|
|
@@ -230,6 +230,7 @@ files:
|
|
|
230
230
|
- lib/booth/adminland/authenticators/exist.rb
|
|
231
231
|
- lib/booth/adminland/credentials/create.rb
|
|
232
232
|
- lib/booth/adminland/credentials/index.rb
|
|
233
|
+
- lib/booth/adminland/incognitos/start.rb
|
|
233
234
|
- lib/booth/adminland/onboardings/create.rb
|
|
234
235
|
- lib/booth/adminland/onboardings/destroy.rb
|
|
235
236
|
- lib/booth/adminland/onboardings/find.rb
|