masks 0.4.0 → 0.6.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 +4 -4
- data/CHANGELOG.md +29 -0
- data/LICENSE +21 -0
- data/README.md +235 -29
- data/app/controllers/masks/rails/avatars_controller.rb +51 -0
- data/app/controllers/masks/rails/base_controller.rb +13 -0
- data/app/controllers/masks/rails/handshakes_controller.rb +86 -0
- data/app/controllers/masks/rails/sessions_controller.rb +118 -0
- data/app/views/layouts/masks/rails/plain.html.erb +47 -0
- data/app/views/masks/rails/handshakes/refused.html.erb +12 -0
- data/app/views/masks/rails/handshakes/show.html.erb +40 -0
- data/config/routes.rb +11 -82
- data/lib/generators/masks/install/install_generator.rb +35 -22
- data/lib/generators/masks/install/templates/masks.rb.tt +38 -0
- data/lib/masks/client/claims.rb +144 -0
- data/lib/masks/client/errors.rb +60 -0
- data/lib/masks/client/handshake.rb +104 -0
- data/lib/masks/client/http.rb +96 -0
- data/lib/masks/client/introspection.rb +29 -0
- data/lib/masks/client/issuer.rb +92 -0
- data/lib/masks/client/pkce.rb +27 -0
- data/lib/masks/client/rack.rb +62 -0
- data/lib/masks/client/registration.rb +115 -0
- data/lib/masks/client/registry.rb +25 -0
- data/lib/masks/client/resource.rb +92 -0
- data/lib/masks/client/session.rb +146 -0
- data/lib/masks/client/stores.rb +40 -0
- data/lib/masks/client/tokens.rb +70 -0
- data/lib/masks/client/tracker.rb +122 -0
- data/lib/masks/client/verifier.rb +42 -0
- data/lib/masks/client.rb +51 -0
- data/lib/masks/rails/authentication.rb +260 -0
- data/lib/masks/rails/configurable.rb +11 -0
- data/lib/masks/rails/configuration.rb +184 -0
- data/lib/masks/rails/credentials.rb +60 -0
- data/lib/masks/rails/engine.rb +20 -0
- data/lib/masks/rails/protected_resource.rb +51 -0
- data/lib/masks/rails.rb +10 -0
- data/lib/masks/version.rb +1 -3
- data/lib/masks.rb +3 -183
- metadata +48 -416
- data/MIT-LICENSE +0 -20
- data/Rakefile +0 -11
- data/app/assets/builds/masks/application.css +0 -1
- data/app/assets/builds/masks/application.js +0 -8549
- data/app/assets/builds/masks/application.js.map +0 -7
- data/app/assets/images/masks.png +0 -0
- data/app/assets/javascripts/application.js +0 -2
- data/app/assets/javascripts/controllers/application.js +0 -9
- data/app/assets/javascripts/controllers/emails_controller.js +0 -28
- data/app/assets/javascripts/controllers/index.js +0 -21
- data/app/assets/javascripts/controllers/keys_controller.js +0 -20
- data/app/assets/javascripts/controllers/recover_controller.js +0 -21
- data/app/assets/javascripts/controllers/recover_password_controller.js +0 -21
- data/app/assets/javascripts/controllers/session_controller.js +0 -94
- data/app/assets/javascripts/controllers/table_controller.js +0 -15
- data/app/assets/manifest.js +0 -2
- data/app/assets/masks_manifest.js +0 -2
- data/app/assets/stylesheets/application.css +0 -34
- data/app/controllers/concerns/masks/controller.rb +0 -114
- data/app/controllers/masks/actors_controller.rb +0 -15
- data/app/controllers/masks/application_controller.rb +0 -35
- data/app/controllers/masks/backup_codes_controller.rb +0 -34
- data/app/controllers/masks/debug_controller.rb +0 -9
- data/app/controllers/masks/devices_controller.rb +0 -20
- data/app/controllers/masks/emails_controller.rb +0 -60
- data/app/controllers/masks/error_controller.rb +0 -14
- data/app/controllers/masks/keys_controller.rb +0 -45
- data/app/controllers/masks/manage/actors_controller.rb +0 -83
- data/app/controllers/masks/manage/base_controller.rb +0 -20
- data/app/controllers/masks/manage/clients_controller.rb +0 -84
- data/app/controllers/masks/manage/dashboard_controller.rb +0 -15
- data/app/controllers/masks/manage/devices_controller.rb +0 -19
- data/app/controllers/masks/one_time_code_controller.rb +0 -49
- data/app/controllers/masks/openid/authorizations_controller.rb +0 -45
- data/app/controllers/masks/openid/discoveries_controller.rb +0 -55
- data/app/controllers/masks/openid/tokens_controller.rb +0 -45
- data/app/controllers/masks/openid/userinfo_controller.rb +0 -28
- data/app/controllers/masks/passwords_controller.rb +0 -33
- data/app/controllers/masks/recoveries_controller.rb +0 -43
- data/app/controllers/masks/sessions_controller.rb +0 -53
- data/app/helpers/masks/application_helper.rb +0 -49
- data/app/jobs/masks/application_job.rb +0 -7
- data/app/jobs/masks/expire_actors_job.rb +0 -15
- data/app/jobs/masks/expire_recoveries_job.rb +0 -15
- data/app/mailers/masks/actor_mailer.rb +0 -22
- data/app/mailers/masks/application_mailer.rb +0 -15
- data/app/models/concerns/masks/access.rb +0 -162
- data/app/models/concerns/masks/actor.rb +0 -132
- data/app/models/concerns/masks/adapter.rb +0 -68
- data/app/models/concerns/masks/role.rb +0 -9
- data/app/models/concerns/masks/scoped.rb +0 -54
- data/app/models/masks/access/actor_password.rb +0 -21
- data/app/models/masks/access/actor_scopes.rb +0 -18
- data/app/models/masks/access/actor_signup.rb +0 -21
- data/app/models/masks/actors/anonymous.rb +0 -40
- data/app/models/masks/actors/system.rb +0 -24
- data/app/models/masks/adapters/active_record.rb +0 -85
- data/app/models/masks/application_model.rb +0 -15
- data/app/models/masks/application_record.rb +0 -8
- data/app/models/masks/check.rb +0 -192
- data/app/models/masks/credential.rb +0 -166
- data/app/models/masks/credentials/access_token.rb +0 -60
- data/app/models/masks/credentials/backup_code.rb +0 -30
- data/app/models/masks/credentials/device.rb +0 -59
- data/app/models/masks/credentials/email.rb +0 -48
- data/app/models/masks/credentials/factor2.rb +0 -71
- data/app/models/masks/credentials/key.rb +0 -38
- data/app/models/masks/credentials/last_login.rb +0 -12
- data/app/models/masks/credentials/masquerade.rb +0 -32
- data/app/models/masks/credentials/nickname.rb +0 -63
- data/app/models/masks/credentials/one_time_code.rb +0 -34
- data/app/models/masks/credentials/password.rb +0 -28
- data/app/models/masks/credentials/recovery.rb +0 -71
- data/app/models/masks/credentials/return_to.rb +0 -27
- data/app/models/masks/credentials/session.rb +0 -67
- data/app/models/masks/device.rb +0 -30
- data/app/models/masks/error.rb +0 -51
- data/app/models/masks/event.rb +0 -14
- data/app/models/masks/mask.rb +0 -266
- data/app/models/masks/openid/authorization.rb +0 -116
- data/app/models/masks/openid/token.rb +0 -56
- data/app/models/masks/rails/actor.rb +0 -212
- data/app/models/masks/rails/actor_role.rb +0 -12
- data/app/models/masks/rails/device.rb +0 -47
- data/app/models/masks/rails/email.rb +0 -96
- data/app/models/masks/rails/key.rb +0 -61
- data/app/models/masks/rails/openid/access_token.rb +0 -55
- data/app/models/masks/rails/openid/authorization.rb +0 -45
- data/app/models/masks/rails/openid/client.rb +0 -186
- data/app/models/masks/rails/openid/id_token.rb +0 -43
- data/app/models/masks/rails/recovery.rb +0 -116
- data/app/models/masks/rails/role.rb +0 -20
- data/app/models/masks/rails/scope.rb +0 -15
- data/app/models/masks/session.rb +0 -447
- data/app/models/masks/sessions/access.rb +0 -27
- data/app/models/masks/sessions/inline.rb +0 -16
- data/app/models/masks/sessions/request.rb +0 -42
- data/app/resources/masks/actor_resource.rb +0 -9
- data/app/resources/masks/session_resource.rb +0 -15
- data/app/views/layouts/masks/application.html.erb +0 -17
- data/app/views/layouts/masks/mailer.html.erb +0 -17
- data/app/views/layouts/masks/mailer.text.erb +0 -1
- data/app/views/layouts/masks/manage.html.erb +0 -42
- data/app/views/masks/actor_mailer/recover_credentials.html.erb +0 -32
- data/app/views/masks/actor_mailer/recover_credentials.text.erb +0 -1
- data/app/views/masks/actor_mailer/verify_email.html.erb +0 -33
- data/app/views/masks/actor_mailer/verify_email.text.erb +0 -8
- data/app/views/masks/actors/current.html.erb +0 -145
- data/app/views/masks/application/_header.html.erb +0 -30
- data/app/views/masks/backup_codes/new.html.erb +0 -117
- data/app/views/masks/emails/new.html.erb +0 -109
- data/app/views/masks/emails/verify.html.erb +0 -51
- data/app/views/masks/keys/new.html.erb +0 -127
- data/app/views/masks/manage/actors/index.html.erb +0 -104
- data/app/views/masks/manage/actors/show.html.erb +0 -172
- data/app/views/masks/manage/clients/index.html.erb +0 -102
- data/app/views/masks/manage/clients/show.html.erb +0 -156
- data/app/views/masks/manage/dashboard/index.html.erb +0 -10
- data/app/views/masks/manage/devices/index.html.erb +0 -47
- data/app/views/masks/one_time_code/new.html.erb +0 -167
- data/app/views/masks/openid/authorizations/error.html.erb +0 -23
- data/app/views/masks/openid/authorizations/new.html.erb +0 -46
- data/app/views/masks/passwords/edit.html.erb +0 -71
- data/app/views/masks/recoveries/new.html.erb +0 -69
- data/app/views/masks/recoveries/password.html.erb +0 -63
- data/app/views/masks/sessions/new.html.erb +0 -152
- data/config/brakeman.ignore +0 -28
- data/config/initializers/inflections.rb +0 -5
- data/config/locales/en.yml +0 -307
- data/db/migrate/20231205173845_create_actors.rb +0 -94
- data/db/migrate/20240329182422_support_openid.rb +0 -64
- data/lib/generators/masks/install/USAGE +0 -8
- data/lib/generators/masks/install/templates/initializer.rb +0 -5
- data/lib/generators/masks/install/templates/masks.json +0 -9
- data/lib/masks/configuration.rb +0 -249
- data/lib/masks/engine.rb +0 -25
- data/lib/masks/middleware.rb +0 -70
- data/lib/tasks/masks_tasks.rake +0 -72
- data/masks.json +0 -315
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
module Credentials
|
|
5
|
-
# Checks for an :actor given a matching nickname.
|
|
6
|
-
class Nickname < Masks::Credential
|
|
7
|
-
checks :actor
|
|
8
|
-
|
|
9
|
-
delegate :actor_id, :nickname, to: :actor, allow_nil: true
|
|
10
|
-
|
|
11
|
-
validates :nickname, :actor, presence: true
|
|
12
|
-
validate :validates_custom, if: :nickname
|
|
13
|
-
|
|
14
|
-
def lookup
|
|
15
|
-
return if actor
|
|
16
|
-
|
|
17
|
-
actor =
|
|
18
|
-
(
|
|
19
|
-
if nickname_param
|
|
20
|
-
config.find_actor(session, nickname: nickname_param)
|
|
21
|
-
end
|
|
22
|
-
)
|
|
23
|
-
actor ||=
|
|
24
|
-
if nickname_param
|
|
25
|
-
config
|
|
26
|
-
.build_actor(session, nickname: nickname_param)
|
|
27
|
-
.tap { |actor| actor.signup = true }
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
if actor&.new_record?
|
|
31
|
-
actor.nickname =
|
|
32
|
-
prefix_nickname(actor.nickname, default: actor.nickname)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
actor
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def maskup
|
|
39
|
-
approve! if valid?
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def nickname_param
|
|
45
|
-
@nickname_param ||=
|
|
46
|
-
prefix_nickname(
|
|
47
|
-
session_params[:nickname],
|
|
48
|
-
default: session_params[:nickname]
|
|
49
|
-
)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def validates_custom
|
|
53
|
-
return unless nickname
|
|
54
|
-
|
|
55
|
-
validates_length :nickname, nickname_config&.length
|
|
56
|
-
|
|
57
|
-
return unless nickname_format
|
|
58
|
-
|
|
59
|
-
errors.add(:nickname, :format) unless nickname_format.match?(nickname)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
module Credentials
|
|
5
|
-
# Checks :factor2 for a valid one-time code.
|
|
6
|
-
class OneTimeCode < Masks::Credential
|
|
7
|
-
include Factor2
|
|
8
|
-
|
|
9
|
-
private
|
|
10
|
-
|
|
11
|
-
def param
|
|
12
|
-
:one_time_code
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def secret
|
|
16
|
-
actor&.totp_secret
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def verify(code)
|
|
20
|
-
valid_code?(code, secret)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def valid_code?(code, secret)
|
|
24
|
-
if code && secret
|
|
25
|
-
actor.totp.verify(code)
|
|
26
|
-
else
|
|
27
|
-
false
|
|
28
|
-
end
|
|
29
|
-
rescue OpenSSL::HMACError
|
|
30
|
-
false
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
module Credentials
|
|
5
|
-
# Checks :password for a match.
|
|
6
|
-
class Password < Masks::Credential
|
|
7
|
-
checks :password
|
|
8
|
-
|
|
9
|
-
def lookup
|
|
10
|
-
actor.password = password if actor&.new_record? && password
|
|
11
|
-
|
|
12
|
-
nil
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def maskup
|
|
16
|
-
return unless password
|
|
17
|
-
|
|
18
|
-
actor&.authenticate(password) && actor&.valid? ? approve! : deny!
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def password
|
|
24
|
-
session_params[:password]
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
module Credentials
|
|
5
|
-
# Checks for a :recovery on the session.
|
|
6
|
-
class Recovery < Masks::Credential
|
|
7
|
-
checks :recovery
|
|
8
|
-
|
|
9
|
-
def lookup
|
|
10
|
-
return if actor
|
|
11
|
-
|
|
12
|
-
@recovery =
|
|
13
|
-
if recovery_key
|
|
14
|
-
config.find_recovery(session, token: recovery_key)
|
|
15
|
-
else
|
|
16
|
-
config.build_recovery(
|
|
17
|
-
session,
|
|
18
|
-
nickname: nickname_param,
|
|
19
|
-
email: email_param,
|
|
20
|
-
phone: phone_param,
|
|
21
|
-
value: recovery_value
|
|
22
|
-
)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
session.extras(recovery: @recovery)
|
|
26
|
-
|
|
27
|
-
@recovery&.actor
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def maskup
|
|
31
|
-
return unless valid? && actor
|
|
32
|
-
|
|
33
|
-
if recovery_key
|
|
34
|
-
if recovery_password && @recovery&.valid?
|
|
35
|
-
@recovery.reset_password!(recovery_password)
|
|
36
|
-
approve!
|
|
37
|
-
end
|
|
38
|
-
elsif recovery_value && @recovery&.valid?
|
|
39
|
-
@recovery.notify!
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def recovery_value
|
|
46
|
-
params.dig(:recover, :value) if writable?
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def recovery_key
|
|
50
|
-
params[:token]
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def recovery_password
|
|
54
|
-
params.dig(:recover, :password) if writable?
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def phone_param
|
|
58
|
-
@phone_param ||= Phonelib.valid?(recovery_value) ? recovery_value : nil
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def email_param
|
|
62
|
-
@email_param ||=
|
|
63
|
-
ValidateEmail.valid?(recovery_value) ? recovery_value : nil
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def nickname_param
|
|
67
|
-
@nickname_param ||= prefix_nickname(recovery_value, default: nil)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
module Credentials
|
|
5
|
-
# Assigns the request URL to session data, under the key +return_to+.
|
|
6
|
-
#
|
|
7
|
-
# This credential is intended to keep track of an actor's attempts to
|
|
8
|
-
# access protected resources, so it only assigns the value if the session
|
|
9
|
-
# has not passed.
|
|
10
|
-
#
|
|
11
|
-
# In some cases, you may want to selectively disable the functionality
|
|
12
|
-
# of this credential. To do so, set +return_to+ to +false+ in the
|
|
13
|
-
# mask configuration.
|
|
14
|
-
class ReturnTo < Masks::Credential
|
|
15
|
-
def backup
|
|
16
|
-
return if session&.passed?
|
|
17
|
-
return unless session.try(:request) && session.request.get?
|
|
18
|
-
if session.mask.extras.key?(:return_to) &&
|
|
19
|
-
!session.mask.extras[:return_to]
|
|
20
|
-
return
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
session.data[:return_to] = session.request.url
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
module Credentials
|
|
5
|
-
# Checks for past :actor(s) on a session.
|
|
6
|
-
#
|
|
7
|
-
# This can be used in lieu of supplying identifying details like an
|
|
8
|
-
# email/nickname (usually provided they were supplied in the past).
|
|
9
|
-
class Session < Masks::Credential
|
|
10
|
-
checks :actor
|
|
11
|
-
|
|
12
|
-
def lookup
|
|
13
|
-
return if session_params[:actor_id]
|
|
14
|
-
|
|
15
|
-
actor_ids = session.data[:actors]&.keys || []
|
|
16
|
-
actor_id = session.data[:actor]
|
|
17
|
-
actors = (actor_ids.any? ? config.find_actors(session, actor_ids) : [])
|
|
18
|
-
|
|
19
|
-
# only lookup and return the current actor if
|
|
20
|
-
# it's not provided via a param (e.g. someone
|
|
21
|
-
# is trying to login)
|
|
22
|
-
actor =
|
|
23
|
-
if actor_id
|
|
24
|
-
actors.find do |a|
|
|
25
|
-
a.actor_id == actor_id &&
|
|
26
|
-
a.session_key == session.data[:actors][a.actor_id]
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
actor = Actors::Anonymous.new(session:) if optional? && !actors.present?
|
|
31
|
-
|
|
32
|
-
actor
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def maskup
|
|
36
|
-
return approve! if optional? && actor&.anonymous?
|
|
37
|
-
|
|
38
|
-
actor_id = actor&.actor_id
|
|
39
|
-
|
|
40
|
-
return unless actor_id && session.data[:actors]&.fetch(actor_id, nil)
|
|
41
|
-
return unless session.data[:actor] == actor_id
|
|
42
|
-
|
|
43
|
-
if session.data.dig(:actors, actor_id) == actor.session_key
|
|
44
|
-
approve!
|
|
45
|
-
else
|
|
46
|
-
cleanup
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def backup
|
|
51
|
-
return unless actor && passed?
|
|
52
|
-
|
|
53
|
-
session.data[:actors] ||= {}
|
|
54
|
-
session.data[:actors][actor.actor_id] = actor.session_key
|
|
55
|
-
session.data[:actor] = actor.actor_id
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def cleanup
|
|
59
|
-
actor_id = actor&.actor_id || session.data[:actor]
|
|
60
|
-
|
|
61
|
-
session.data[:actor] = nil
|
|
62
|
-
session.data[:actors] ||= {}
|
|
63
|
-
session.data[:actors].delete(actor_id)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
data/app/models/masks/device.rb
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
# Represents a device, used for interacting with a session.
|
|
5
|
-
#
|
|
6
|
-
# Device detection is optionally added with the +Device+ credential,
|
|
7
|
-
# using the +device-detector+ gem.
|
|
8
|
-
#
|
|
9
|
-
# @see Masks::Credentials::Device Masks::Credentials::Device
|
|
10
|
-
class Device < ApplicationModel
|
|
11
|
-
attribute :session
|
|
12
|
-
|
|
13
|
-
def fingerprint
|
|
14
|
-
return unless detector.known?
|
|
15
|
-
|
|
16
|
-
input = [
|
|
17
|
-
detector.name,
|
|
18
|
-
detector.os_name,
|
|
19
|
-
detector.device_name,
|
|
20
|
-
detector.device_type
|
|
21
|
-
].compact.join("-")
|
|
22
|
-
|
|
23
|
-
Digest::SHA512.hexdigest(input)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def detector
|
|
27
|
-
@detector ||= DeviceDetector.new(session.user_agent)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
data/app/models/masks/error.rb
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
module Error
|
|
5
|
-
# Base class for Masks errors
|
|
6
|
-
class Base < RuntimeError
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# Thrown when a session is required but not provided
|
|
10
|
-
class InvalidSession < Base
|
|
11
|
-
def initialize
|
|
12
|
-
super("a session was expected, but none was provided")
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Thrown when invalid configuration is detected
|
|
17
|
-
class InvalidConfiguration < Base
|
|
18
|
-
def initialize(value)
|
|
19
|
-
super("cannot use '#{value}' for masks.json")
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Thrown when configuration is not found
|
|
24
|
-
class ConfigurationNotFound < Base
|
|
25
|
-
def initialize
|
|
26
|
-
super("cannot find masks.json")
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Thrown when Masks encounters an unauthorized session
|
|
31
|
-
class Unauthorized < Base
|
|
32
|
-
def initialize
|
|
33
|
-
super("unauthorized")
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Thrown when Masks cannot find a mask for a session
|
|
38
|
-
class UnknownMask < Base
|
|
39
|
-
def initialize(session)
|
|
40
|
-
super("could not determine mask for #{session}")
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Thrown when Masks cannot find an access class for the given name
|
|
45
|
-
class UnknownAccess < Base
|
|
46
|
-
def initialize(name)
|
|
47
|
-
super("could not determine access class for #{name}")
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
data/app/models/masks/event.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
# Publishes events using +ActiveSupport::Notifications+
|
|
5
|
-
class Event
|
|
6
|
-
class << self
|
|
7
|
-
def emit(name, **opts, &block)
|
|
8
|
-
ActiveSupport::Notifications.instrument("masks.#{name}", **opts) do
|
|
9
|
-
block.call
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
data/app/models/masks/mask.rb
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Masks
|
|
4
|
-
# Represents an individual mask, its properties, and methods for interpreting them.
|
|
5
|
-
#
|
|
6
|
-
# When a session is created it finds the first matching mask to use for
|
|
7
|
-
# dictating how to control access. A Mask contains rules that allow sessions
|
|
8
|
-
# to match them.
|
|
9
|
-
#
|
|
10
|
-
# @see Masks::Check Masks::Check
|
|
11
|
-
# @see Masks::Credentials Masks::Credentials
|
|
12
|
-
class Mask < ApplicationModel
|
|
13
|
-
# @!attribute [rw] name
|
|
14
|
-
# a unique name for the mask
|
|
15
|
-
# @return [String]
|
|
16
|
-
attribute :name
|
|
17
|
-
# @!attribute [rw] skip
|
|
18
|
-
# Whether or not to skip processing by masks
|
|
19
|
-
# @return [Boolean]
|
|
20
|
-
attribute :skip, default: false
|
|
21
|
-
# @!attribute [rw] type
|
|
22
|
-
# A type name to inherit configuration from
|
|
23
|
-
# @return [String]
|
|
24
|
-
attribute :type
|
|
25
|
-
# @!attribute [rw] types
|
|
26
|
-
# A list of required type names
|
|
27
|
-
# @return [String]
|
|
28
|
-
attribute :types
|
|
29
|
-
# @!attribute [rw] checks
|
|
30
|
-
# A hash of checks required to pass the session
|
|
31
|
-
# @return [Hash]
|
|
32
|
-
attribute :checks
|
|
33
|
-
# @!attribute [rw] credentials
|
|
34
|
-
# An array of credentials that will check the session
|
|
35
|
-
# @return [Array<Class>]
|
|
36
|
-
attribute :credentials
|
|
37
|
-
# @!attribute [rw] scopes
|
|
38
|
-
# An array of scopes required to access the session
|
|
39
|
-
# @return [Array<String>]
|
|
40
|
-
attribute :scopes
|
|
41
|
-
# @!attribute [rw] request
|
|
42
|
-
# A hash of properties an HTTP request must match to use this mask
|
|
43
|
-
# @return [Hash]
|
|
44
|
-
attribute :request
|
|
45
|
-
# @!attribute [rw] access
|
|
46
|
-
# A list of access classes allowed during this session
|
|
47
|
-
# @return [Array<String>]
|
|
48
|
-
attribute :access
|
|
49
|
-
# @!attribute [rw] actor
|
|
50
|
-
# The expected class name of the actor using the sesssion, as a string
|
|
51
|
-
# @return [String]
|
|
52
|
-
attribute :actor
|
|
53
|
-
# @!attribute [rw] anon
|
|
54
|
-
# Whether or not to allow "anonymous" actors
|
|
55
|
-
# @return [Boolean]
|
|
56
|
-
attribute :anon
|
|
57
|
-
# @!attribute [rw] pass
|
|
58
|
-
# What to do when the session passes, typically a redirect uri
|
|
59
|
-
# @return [String]
|
|
60
|
-
attribute :pass, default: "/"
|
|
61
|
-
# @!attribute [rw] fail
|
|
62
|
-
# What to do when the session is failed by checks, credentials, or another error
|
|
63
|
-
# @return [String|Boolean]
|
|
64
|
-
attribute :fail, default: true
|
|
65
|
-
# @!attribute [rw] backup
|
|
66
|
-
# Whether or not to save results of masks
|
|
67
|
-
# @return [Boolean]
|
|
68
|
-
attribute :backup, default: true
|
|
69
|
-
# @!attribute [rw] extras
|
|
70
|
-
# Extra attributes and configuration accessible on the mask
|
|
71
|
-
# @return [Hash]
|
|
72
|
-
attribute :extras, default: -> { {} }
|
|
73
|
-
|
|
74
|
-
# @visibility private
|
|
75
|
-
attribute :config
|
|
76
|
-
|
|
77
|
-
# @visibility private
|
|
78
|
-
def initialize(attrs = {})
|
|
79
|
-
if attrs[:type]
|
|
80
|
-
type = attrs[:config].mask(attrs[:type])
|
|
81
|
-
attrs = type.deep_merge(**attrs)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# required for `attribute_names` to work
|
|
85
|
-
super({})
|
|
86
|
-
|
|
87
|
-
extras = attrs.except(*attribute_names.map(&:to_sym))
|
|
88
|
-
attrs = attrs.slice(*attribute_names.map(&:to_sym))
|
|
89
|
-
attrs[:extras] = extras.deep_symbolize_keys
|
|
90
|
-
|
|
91
|
-
assign_attributes(**attrs)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Returns the class name expected for any actor attached to this session.
|
|
95
|
-
#
|
|
96
|
-
# @return [String]
|
|
97
|
-
def actor
|
|
98
|
-
super || config.models[:actor]
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Returns the constantized version of +#actor+.
|
|
102
|
-
#
|
|
103
|
-
# @return [String]
|
|
104
|
-
def actor_scope
|
|
105
|
-
(actor.constantize unless skip?)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Whether or not sessions matching this mask should skip all work.
|
|
109
|
-
#
|
|
110
|
-
# @return [Boolean]
|
|
111
|
-
def skip?
|
|
112
|
-
!!skip
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Whether or not anonymous actors are allowed in the session.
|
|
116
|
-
#
|
|
117
|
-
# @return [Boolean]
|
|
118
|
-
def allow_anonymous?
|
|
119
|
-
anon
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Whether or not sessions using this mask should be saved.
|
|
123
|
-
#
|
|
124
|
-
# Some masks definitely want this enabled, as it is what stores the results
|
|
125
|
-
# of masks, credentials, and checks in the rails session. In other cases, it
|
|
126
|
-
# is not necessary, for example when verifying an API key.
|
|
127
|
-
def backup?
|
|
128
|
-
return false if skip?
|
|
129
|
-
|
|
130
|
-
!!backup
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# A hash of check configuration, keyed by their name.
|
|
134
|
-
#
|
|
135
|
-
# @return [Hash]
|
|
136
|
-
def checks
|
|
137
|
-
return {} if skip?
|
|
138
|
-
|
|
139
|
-
(type_config&.fetch(:checks, {}) || {}).merge(super || {})
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Converts credentials to classes before returning the list.
|
|
143
|
-
#
|
|
144
|
-
# @return [Array<Class>]
|
|
145
|
-
# @raise Masks::Error::InvalidConfiguration
|
|
146
|
-
def credentials
|
|
147
|
-
return [] if skip?
|
|
148
|
-
|
|
149
|
-
merged = [
|
|
150
|
-
*(type_config&.fetch(:credentials, []) || []),
|
|
151
|
-
*(super || [])
|
|
152
|
-
].uniq
|
|
153
|
-
merged.map do |cls|
|
|
154
|
-
case cls
|
|
155
|
-
when Class
|
|
156
|
-
cls
|
|
157
|
-
when /:+/
|
|
158
|
-
cls.constantize
|
|
159
|
-
when String
|
|
160
|
-
"Masks::Credentials::#{cls}".constantize
|
|
161
|
-
else
|
|
162
|
-
raise Masks::Error::InvalidConfiguration, cls
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# Returns whether or not the mask matches the passed session.
|
|
168
|
-
#
|
|
169
|
-
# The behaviour of this method depends on the mask's configuration. For
|
|
170
|
-
# example, a session with an anonymous actor will return true only if the
|
|
171
|
-
# mask's +#allow_anonymous?+ method returns true.
|
|
172
|
-
#
|
|
173
|
-
# @param [Masks::Session] session
|
|
174
|
-
# @return [Boolean]
|
|
175
|
-
def matches_session?(session)
|
|
176
|
-
actor = session.actor
|
|
177
|
-
|
|
178
|
-
return false unless actor
|
|
179
|
-
return true if actor.anonymous? && allow_anonymous?
|
|
180
|
-
|
|
181
|
-
case self.actor
|
|
182
|
-
when String
|
|
183
|
-
return false unless actor.is_a?(self.actor.constantize)
|
|
184
|
-
when Class
|
|
185
|
-
return false unless actor.is_a?(self.actor)
|
|
186
|
-
else
|
|
187
|
-
return false unless actor.is_a?(config.model(:actor))
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
matches_scopes?(session.scopes)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
# Returns whether or not the mask's request confiig matches the request passed.
|
|
194
|
-
#
|
|
195
|
-
# The following parameters are supported in the +request+ hash:
|
|
196
|
-
#
|
|
197
|
-
# - +path+ - if specified, the request path must be in this list.
|
|
198
|
-
# - +method+ - if specified, the request method must be in this list.
|
|
199
|
-
# - +param+ - if specified, the key must exist in the session params.
|
|
200
|
-
# - +header+ - if specified, the header must be present in the request.
|
|
201
|
-
#
|
|
202
|
-
# @return [Boolean]
|
|
203
|
-
def matches_request?(request)
|
|
204
|
-
return false unless self.request
|
|
205
|
-
return false unless matches_path?(request)
|
|
206
|
-
return false unless matches_method?(request)
|
|
207
|
-
|
|
208
|
-
param = self.request.fetch(:param, nil)
|
|
209
|
-
|
|
210
|
-
if param && param != "*" && !request.params&.fetch(param.to_sym, nil)
|
|
211
|
-
return false
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
header = self.request.fetch(:header, nil)
|
|
215
|
-
|
|
216
|
-
return false if header && !request.headers[header]
|
|
217
|
-
|
|
218
|
-
true
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
private
|
|
222
|
-
|
|
223
|
-
# Returns whether or not the mask's allowed scopes includes one of the scopes passed.
|
|
224
|
-
#
|
|
225
|
-
# @return [Boolean]
|
|
226
|
-
def matches_scopes?(scopes)
|
|
227
|
-
return true if self.scopes.blank?
|
|
228
|
-
|
|
229
|
-
# the mask scopes and scopes passed should have at least one match
|
|
230
|
-
# otherwise this will return false because the intersection is blank
|
|
231
|
-
(Array.wrap(self.scopes) & (scopes || [])).present?
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def matches_path?(request)
|
|
235
|
-
return true unless (paths = self.request.fetch(:path, nil))
|
|
236
|
-
|
|
237
|
-
Array
|
|
238
|
-
.wrap(paths)
|
|
239
|
-
.any? do |path|
|
|
240
|
-
case path
|
|
241
|
-
when /^r.+/
|
|
242
|
-
Regexp.new(path.slice(1..)).match?(request.url)
|
|
243
|
-
else
|
|
244
|
-
Fuzzyurl.matches?(Fuzzyurl.mask(path:), request.url)
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
def matches_method?(request)
|
|
250
|
-
return true unless (methods = self.request&.fetch(:method, nil))
|
|
251
|
-
return true if !methods || methods == "*"
|
|
252
|
-
|
|
253
|
-
Array
|
|
254
|
-
.wrap(methods)
|
|
255
|
-
.map(&:upcase)
|
|
256
|
-
.any? do |m|
|
|
257
|
-
m == request.method ||
|
|
258
|
-
(request.method == "POST" && request.params[:_method]&.upcase == m)
|
|
259
|
-
end
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def type_config
|
|
263
|
-
@type_config ||= config.mask(type) if type
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
end
|