masks 0.3.1 → 0.4.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/app/assets/builds/masks/application.css +1 -1
- data/app/assets/builds/masks/application.js +2153 -726
- data/app/assets/builds/masks/application.js.map +4 -4
- data/app/assets/javascripts/controllers/application.js +1 -1
- data/app/assets/javascripts/controllers/index.js +9 -0
- data/app/assets/javascripts/controllers/table_controller.js +15 -0
- data/app/assets/stylesheets/application.css +12 -4
- data/app/controllers/concerns/masks/controller.rb +1 -1
- data/app/controllers/masks/manage/actors_controller.rb +72 -1
- data/app/controllers/masks/manage/base_controller.rb +10 -2
- data/app/controllers/masks/manage/clients_controller.rb +84 -0
- data/app/controllers/masks/manage/dashboard_controller.rb +15 -0
- data/app/controllers/masks/manage/devices_controller.rb +19 -0
- data/app/controllers/masks/openid/authorizations_controller.rb +45 -0
- data/app/controllers/masks/openid/discoveries_controller.rb +55 -0
- data/app/controllers/masks/openid/tokens_controller.rb +45 -0
- data/app/controllers/masks/openid/userinfo_controller.rb +28 -0
- data/app/controllers/masks/sessions_controller.rb +1 -1
- data/app/models/concerns/masks/access.rb +2 -2
- data/app/models/masks/access/actor_password.rb +2 -1
- data/app/models/masks/access/actor_signup.rb +1 -2
- data/app/models/masks/credentials/access_token.rb +60 -0
- data/app/models/masks/credentials/key.rb +1 -1
- data/app/models/masks/credentials/return_to.rb +27 -0
- data/app/models/masks/mask.rb +12 -1
- data/app/models/masks/openid/authorization.rb +116 -0
- data/app/models/masks/openid/token.rb +56 -0
- data/app/models/masks/rails/actor.rb +23 -1
- data/app/models/masks/rails/openid/access_token.rb +55 -0
- data/app/models/masks/rails/openid/authorization.rb +45 -0
- data/app/models/masks/rails/openid/client.rb +186 -0
- data/app/models/masks/rails/openid/id_token.rb +43 -0
- data/app/models/masks/sessions/access.rb +2 -1
- data/app/resources/masks/session_resource.rb +1 -1
- data/app/views/layouts/masks/manage.html.erb +22 -5
- data/app/views/masks/actor_mailer/recover_credentials.html.erb +2 -3
- data/app/views/masks/actor_mailer/verify_email.html.erb +2 -3
- data/app/views/masks/actors/current.html.erb +7 -14
- data/app/views/masks/application/_header.html.erb +3 -4
- data/app/views/masks/backup_codes/new.html.erb +34 -20
- data/app/views/masks/emails/new.html.erb +14 -8
- data/app/views/masks/keys/new.html.erb +7 -7
- data/app/views/masks/manage/actors/index.html.erb +101 -37
- data/app/views/masks/manage/{actor → actors}/show.html.erb +63 -17
- data/app/views/masks/manage/clients/index.html.erb +102 -0
- data/app/views/masks/manage/clients/show.html.erb +156 -0
- data/app/views/masks/manage/dashboard/index.html.erb +10 -0
- data/app/views/masks/manage/devices/index.html.erb +47 -0
- data/app/views/masks/one_time_code/new.html.erb +41 -24
- data/app/views/masks/openid/authorizations/error.html.erb +23 -0
- data/app/views/masks/openid/authorizations/new.html.erb +46 -0
- data/app/views/masks/passwords/edit.html.erb +20 -7
- data/app/views/masks/recoveries/new.html.erb +2 -4
- data/app/views/masks/recoveries/password.html.erb +2 -3
- data/app/views/masks/sessions/new.html.erb +22 -23
- data/config/initializers/inflections.rb +5 -0
- data/config/locales/en.yml +23 -2
- data/config/routes.rb +40 -3
- data/db/migrate/20240329182422_support_openid.rb +64 -0
- data/lib/generators/masks/install/templates/masks.json +4 -1
- data/lib/masks/configuration.rb +22 -9
- data/lib/masks/version.rb +1 -1
- data/lib/masks.rb +1 -0
- data/lib/tasks/masks_tasks.rake +3 -2
- data/masks.json +47 -6
- metadata +59 -11
- data/app/assets/builds/application.css +0 -4764
- data/app/assets/builds/application.js +0 -8236
- data/app/assets/builds/application.js.map +0 -7
- data/app/controllers/masks/manage/actor_controller.rb +0 -35
@@ -1,12 +1,21 @@
|
|
1
1
|
import { application } from "./application";
|
2
2
|
|
3
|
+
import PasswordVisibilityController from "@stimulus-components/password-visibility";
|
4
|
+
import RevealController from "@stimulus-components/reveal";
|
5
|
+
import DialogController from "@stimulus-components/dialog";
|
3
6
|
import SessionController from "./session_controller";
|
4
7
|
import RecoverController from "./recover_controller";
|
5
8
|
import RecoverPasswordController from "./recover_password_controller";
|
6
9
|
import EmailsController from "./emails_controller";
|
7
10
|
import KeysController from "./keys_controller";
|
11
|
+
import TableController from "./table_controller";
|
12
|
+
|
8
13
|
application.register("session", SessionController);
|
9
14
|
application.register("recover", RecoverController);
|
10
15
|
application.register("recover-password", RecoverPasswordController);
|
11
16
|
application.register("emails", EmailsController);
|
12
17
|
application.register("keys", KeysController);
|
18
|
+
application.register("table", TableController);
|
19
|
+
application.register("password-visibility", PasswordVisibilityController);
|
20
|
+
application.register("reveal", RevealController);
|
21
|
+
application.register("dialog", DialogController);
|
@@ -17,10 +17,18 @@
|
|
17
17
|
@tailwind components;
|
18
18
|
@tailwind utilities;
|
19
19
|
|
20
|
-
.
|
20
|
+
.pagy {
|
21
21
|
@apply flex gap-2 items-center justify-center p-4;
|
22
|
-
}
|
23
22
|
|
24
|
-
.
|
25
|
-
|
23
|
+
a:not(.gap) {
|
24
|
+
@apply btn btn-sm;
|
25
|
+
|
26
|
+
&:not([href]) {
|
27
|
+
@apply btn-disabled;
|
28
|
+
}
|
29
|
+
|
30
|
+
&.current {
|
31
|
+
@apply btn-neutral;
|
32
|
+
}
|
33
|
+
}
|
26
34
|
}
|
@@ -4,8 +4,79 @@ module Masks
|
|
4
4
|
# @visibility private
|
5
5
|
module Manage
|
6
6
|
class ActorsController < BaseController
|
7
|
+
section :actors
|
8
|
+
|
9
|
+
before_action :find_actor
|
10
|
+
|
7
11
|
def index
|
8
|
-
@pagy, @actors = pagy(
|
12
|
+
@pagy, @actors = pagy(actor_model.all)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
actor =
|
17
|
+
signup_access.signup(
|
18
|
+
nickname: params[:nickname],
|
19
|
+
password: params[:password]
|
20
|
+
)
|
21
|
+
|
22
|
+
if actor.valid?
|
23
|
+
redirect_to manage_actor_path(actor)
|
24
|
+
else
|
25
|
+
flash[:errors] = actor.errors.full_messages
|
26
|
+
redirect_to manage_actors_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
actor_model.transaction do
|
32
|
+
if params[:add_scope]
|
33
|
+
@actor.assign_scopes!(params[:add_scope])
|
34
|
+
flash[:info] = "added scope \"#{params[:add_scope]}\""
|
35
|
+
elsif params[:remove_scope]
|
36
|
+
@actor.remove_scopes!(params[:remove_scope])
|
37
|
+
flash[:info] = "removed scope \"#{params[:remove_scope]}\""
|
38
|
+
elsif params[:remove_factor2]
|
39
|
+
@actor.remove_factor2!
|
40
|
+
flash[:info] = "removed second factor authentication"
|
41
|
+
elsif params[:logout]
|
42
|
+
@actor.logout!
|
43
|
+
flash[:info] = "logged out of all devices"
|
44
|
+
elsif password_param
|
45
|
+
password_access.change_password(password_param, actor: @actor)
|
46
|
+
|
47
|
+
if @actor.valid?
|
48
|
+
flash[:info] = "password changed"
|
49
|
+
else
|
50
|
+
flash[:error] = "invalid password"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
redirect_to manage_actor_path(@actor)
|
55
|
+
end
|
56
|
+
|
57
|
+
@actor
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def find_actor
|
63
|
+
@actor = actor_model.find_by(nickname: params[:actor])
|
64
|
+
end
|
65
|
+
|
66
|
+
def actor_model
|
67
|
+
Masks.configuration.model(:actor)
|
68
|
+
end
|
69
|
+
|
70
|
+
def signup_access
|
71
|
+
masked_session.access("actor.signup")
|
72
|
+
end
|
73
|
+
|
74
|
+
def password_access
|
75
|
+
masked_session.access("actor.password")
|
76
|
+
end
|
77
|
+
|
78
|
+
def password_param
|
79
|
+
params[:change_password]
|
9
80
|
end
|
10
81
|
end
|
11
82
|
end
|
@@ -4,9 +4,17 @@ module Masks
|
|
4
4
|
# @visibility private
|
5
5
|
module Manage
|
6
6
|
class BaseController < ApplicationController
|
7
|
-
# require_mask type: :manage
|
8
|
-
|
9
7
|
layout "masks/manage"
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def section(name)
|
11
|
+
before_action { @section = name }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
helper_method :current_actor, :section
|
16
|
+
|
17
|
+
attr_accessor :section
|
10
18
|
end
|
11
19
|
end
|
12
20
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
module Manage
|
6
|
+
class ClientsController < BaseController
|
7
|
+
section :clients
|
8
|
+
|
9
|
+
before_action :find_client, only: %i[show update destroy]
|
10
|
+
|
11
|
+
rescue_from Pagy::OverflowError do
|
12
|
+
redirect_to manage_clients_path
|
13
|
+
end
|
14
|
+
|
15
|
+
def index
|
16
|
+
@pagy, @clients =
|
17
|
+
pagy(Masks::Rails::OpenID::Client.all.order(created_at: :desc))
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
client =
|
22
|
+
Masks.configuration.model(:openid_client).new(name: params[:name])
|
23
|
+
|
24
|
+
if client.save
|
25
|
+
redirect_to manage_client_path(client)
|
26
|
+
else
|
27
|
+
flash[:errors] = client.errors.full_messages
|
28
|
+
|
29
|
+
redirect_to manage_clients_path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
Masks
|
35
|
+
.configuration
|
36
|
+
.model(:openid_client)
|
37
|
+
.transaction do
|
38
|
+
if params[:add_scope]
|
39
|
+
@client.assign_scopes!(params[:add_scope])
|
40
|
+
flash[:info] = "added scope"
|
41
|
+
elsif params[:remove_scope]
|
42
|
+
@client.remove_scopes!(params[:remove_scope])
|
43
|
+
flash[:info] = "removed scope"
|
44
|
+
else
|
45
|
+
update_client
|
46
|
+
end
|
47
|
+
|
48
|
+
redirect_to manage_client_path(@client)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def destroy
|
53
|
+
@client.destroy
|
54
|
+
|
55
|
+
flash[:destroyed] = @client.name
|
56
|
+
|
57
|
+
redirect_to manage_clients_path
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def update_client
|
63
|
+
@client.name = params[:name]
|
64
|
+
@client.secret = params[:secret]
|
65
|
+
@client.consent = params[:consent]
|
66
|
+
@client.redirect_uris = params[:redirect_uris].split("\n")
|
67
|
+
@client.subject_type = params[:subject_type]
|
68
|
+
@client.code_expires_in = params[:code_expires_in]
|
69
|
+
@client.token_expires_in = params[:token_expires_in]
|
70
|
+
@client.refresh_expires_in = params[:refresh_expires_in]
|
71
|
+
@client.save
|
72
|
+
|
73
|
+
return if @client.valid?
|
74
|
+
|
75
|
+
flash[:errors] = @client.errors.full_messages
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_client
|
79
|
+
@client =
|
80
|
+
Masks.configuration.model(:openid_client).find_by(key: params[:id])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
module Manage
|
6
|
+
class DashboardController < BaseController
|
7
|
+
section :dashboard
|
8
|
+
|
9
|
+
def index
|
10
|
+
@clients = Masks.configuration.model(:openid_client).count
|
11
|
+
@actors = Masks.configuration.model(:actor).count
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
module Manage
|
6
|
+
class DevicesController < BaseController
|
7
|
+
section :devices
|
8
|
+
|
9
|
+
rescue_from Pagy::OverflowError do
|
10
|
+
redirect_to manage_devices_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def index
|
14
|
+
@pagy, @devices =
|
15
|
+
pagy(Masks.configuration.model(:device).all.order(created_at: :desc))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module OpenID
|
4
|
+
class AuthorizationsController < ApplicationController
|
5
|
+
rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e|
|
6
|
+
@error = e
|
7
|
+
|
8
|
+
render :error, status: e.status
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
authorize
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
authorize approved: params[:approve]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def authorize(**opts)
|
22
|
+
# TODO: support incoming id_token request object + max_age parameter
|
23
|
+
@authorization = Authorization.perform(request.env, **opts)
|
24
|
+
|
25
|
+
_status, header, = @authorization.response
|
26
|
+
|
27
|
+
if header["WWW-Authenticate"].present?
|
28
|
+
headers["WWW-Authenticate"] = header["WWW-Authenticate"]
|
29
|
+
end
|
30
|
+
|
31
|
+
if header["Location"]
|
32
|
+
return redirect_to header["Location"], allow_other_host: true
|
33
|
+
end
|
34
|
+
|
35
|
+
unless @authorization.actor
|
36
|
+
session[:return_to] = request.url
|
37
|
+
|
38
|
+
return redirect_to session_path
|
39
|
+
end
|
40
|
+
|
41
|
+
render :new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module OpenID
|
4
|
+
class DiscoveriesController < ApplicationController
|
5
|
+
before_action :find_client
|
6
|
+
|
7
|
+
def jwks
|
8
|
+
jwks =
|
9
|
+
JSON::JWK::Set.new(
|
10
|
+
JSON::JWK.new(client.public_key, use: :sig, kid: client.kid)
|
11
|
+
)
|
12
|
+
|
13
|
+
render json: jwks
|
14
|
+
end
|
15
|
+
|
16
|
+
def new
|
17
|
+
render json:
|
18
|
+
OpenIDConnect::Discovery::Provider::Config::Response.new(
|
19
|
+
issuer: client.issuer,
|
20
|
+
authorization_endpoint: openid_authorization_url,
|
21
|
+
token_endpoint: openid_token_url,
|
22
|
+
userinfo_endpoint: openid_userinfo_url,
|
23
|
+
jwks_uri: openid_jwks_url,
|
24
|
+
# registration_endpoint: site_url, # TODO
|
25
|
+
scopes_supported: client.scopes,
|
26
|
+
response_types_supported: client.response_types,
|
27
|
+
grant_types_supported: client.grant_types,
|
28
|
+
claims_parameter_supported: false,
|
29
|
+
request_parameter_supported: false,
|
30
|
+
request_uri_parameter_supported: false,
|
31
|
+
subject_types_supported: [
|
32
|
+
client.pairwise_subject? ? "pairwise" : "public"
|
33
|
+
],
|
34
|
+
id_token_signing_alg_values_supported: [:RS256],
|
35
|
+
token_endpoint_auth_methods_supported: %w[
|
36
|
+
client_secret_basic
|
37
|
+
client_secret_post
|
38
|
+
],
|
39
|
+
claims_supported: %w[sub iss name email address phone_number]
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def find_client
|
46
|
+
head :not_found unless client
|
47
|
+
end
|
48
|
+
|
49
|
+
def client
|
50
|
+
@client ||=
|
51
|
+
Masks.configuration.model(:openid_client).find_by(key: params[:id])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module OpenID
|
4
|
+
class TokensController < ApplicationController
|
5
|
+
rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e|
|
6
|
+
@error = e
|
7
|
+
|
8
|
+
render :error, status: e.status
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
authorize
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
authorize approved: params[:approve]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def authorize(**opts)
|
22
|
+
# TODO: support incoming id_token request object + max_age parameter
|
23
|
+
@authorization = Authorization.perform(request.env, **opts)
|
24
|
+
|
25
|
+
unless @authorization.actor
|
26
|
+
session[:return_to] = request.url
|
27
|
+
|
28
|
+
return redirect_to session_path
|
29
|
+
end
|
30
|
+
|
31
|
+
_status, header, = @authorization.response
|
32
|
+
|
33
|
+
if header["WWW-Authenticate"].present?
|
34
|
+
headers["WWW-Authenticate"] = header["WWW-Authenticate"]
|
35
|
+
end
|
36
|
+
|
37
|
+
if header["Location"]
|
38
|
+
redirect_to header["Location"]
|
39
|
+
else
|
40
|
+
render :new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module OpenID
|
4
|
+
class UserInfoController < ApplicationController
|
5
|
+
def show
|
6
|
+
claims = { sub: openid_client.subject(current_actor) }
|
7
|
+
|
8
|
+
if access_token.scope?("email")
|
9
|
+
claims[:email] = current_actor.primary_email&.email
|
10
|
+
end
|
11
|
+
|
12
|
+
if access_token.scope?("phone")
|
13
|
+
claims[:phone] = current_actor.phone_number
|
14
|
+
end
|
15
|
+
|
16
|
+
render json: OpenIDConnect::ResponseObject::UserInfo.new(claims)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def access_token
|
22
|
+
@access_token ||= masked_session.extra(:access_token)
|
23
|
+
end
|
24
|
+
|
25
|
+
delegate :openid_client, to: :access_token
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -10,7 +10,8 @@ module Masks
|
|
10
10
|
|
11
11
|
access "actor.password"
|
12
12
|
|
13
|
-
def change_password(password)
|
13
|
+
def change_password(password, **opts)
|
14
|
+
actor = opts[:actor] || self.actor
|
14
15
|
actor.changed_password_at = Time.current
|
15
16
|
actor.password = password
|
16
17
|
actor.save if actor.valid?
|
@@ -11,8 +11,7 @@ module Masks
|
|
11
11
|
access "actor.signup"
|
12
12
|
|
13
13
|
def signup(**opts)
|
14
|
-
actor =
|
15
|
-
configuration.build_actor(session, **opts.slice(:nickname, :email))
|
14
|
+
actor = config.build_actor(session, **opts.slice(:nickname, :email))
|
16
15
|
actor.password = opts[:password]
|
17
16
|
actor.save if actor.valid?
|
18
17
|
actor
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
module Credentials
|
5
|
+
# Checks :key given a valid Authorization header.
|
6
|
+
class AccessToken < Masks::Credential
|
7
|
+
checks :access_token
|
8
|
+
|
9
|
+
def lookup
|
10
|
+
access_token =
|
11
|
+
session.config.model(:openid_access_token).valid.find_by(token:)
|
12
|
+
|
13
|
+
return unless access_token&.actor
|
14
|
+
|
15
|
+
session.extras(access_token:)
|
16
|
+
session.scoped = access_token
|
17
|
+
|
18
|
+
access_token.actor
|
19
|
+
end
|
20
|
+
|
21
|
+
def maskup
|
22
|
+
access_token = session.extra(:access_token)
|
23
|
+
|
24
|
+
if access_token&.actor && access_token&.actor == session&.actor &&
|
25
|
+
session.scoped == access_token
|
26
|
+
approve!
|
27
|
+
else
|
28
|
+
deny!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def token
|
35
|
+
return if [header_token, param_token].uniq.compact.length != 1
|
36
|
+
|
37
|
+
header_token || param_token
|
38
|
+
end
|
39
|
+
|
40
|
+
def header_token
|
41
|
+
unless auth_header.provided? && !auth_header.parts.first.nil? &&
|
42
|
+
auth_header.scheme.to_s == "bearer"
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
auth_header.params
|
47
|
+
end
|
48
|
+
|
49
|
+
def param_token
|
50
|
+
params[:access_token]
|
51
|
+
end
|
52
|
+
|
53
|
+
def auth_header
|
54
|
+
return unless session.try(:request)
|
55
|
+
|
56
|
+
@auth_header = Rack::Auth::AbstractRequest.new(session.request.env)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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
|
data/app/models/masks/mask.rb
CHANGED
@@ -66,6 +66,10 @@ module Masks
|
|
66
66
|
# Whether or not to save results of masks
|
67
67
|
# @return [Boolean]
|
68
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: -> { {} }
|
69
73
|
|
70
74
|
# @visibility private
|
71
75
|
attribute :config
|
@@ -77,7 +81,14 @@ module Masks
|
|
77
81
|
attrs = type.deep_merge(**attrs)
|
78
82
|
end
|
79
83
|
|
80
|
-
|
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)
|
81
92
|
end
|
82
93
|
|
83
94
|
# Returns the class name expected for any actor attached to this session.
|