masks 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +50 -0
- data/Rakefile +11 -0
- data/app/assets/builds/application.css +4764 -0
- data/app/assets/builds/application.js +8236 -0
- data/app/assets/builds/application.js.map +7 -0
- data/app/assets/builds/masks/application.css +1 -0
- data/app/assets/builds/masks/application.js +7122 -0
- data/app/assets/builds/masks/application.js.map +7 -0
- data/app/assets/images/masks.png +0 -0
- data/app/assets/javascripts/application.js +2 -0
- data/app/assets/javascripts/controllers/application.js +9 -0
- data/app/assets/javascripts/controllers/emails_controller.js +28 -0
- data/app/assets/javascripts/controllers/index.js +12 -0
- data/app/assets/javascripts/controllers/keys_controller.js +20 -0
- data/app/assets/javascripts/controllers/recover_controller.js +21 -0
- data/app/assets/javascripts/controllers/recover_password_controller.js +21 -0
- data/app/assets/javascripts/controllers/session_controller.js +94 -0
- data/app/assets/manifest.js +2 -0
- data/app/assets/masks_manifest.js +2 -0
- data/app/assets/stylesheets/application.css +26 -0
- data/app/controllers/concerns/masks/controller.rb +114 -0
- data/app/controllers/masks/actors_controller.rb +15 -0
- data/app/controllers/masks/application_controller.rb +35 -0
- data/app/controllers/masks/backup_codes_controller.rb +34 -0
- data/app/controllers/masks/debug_controller.rb +9 -0
- data/app/controllers/masks/devices_controller.rb +20 -0
- data/app/controllers/masks/emails_controller.rb +60 -0
- data/app/controllers/masks/error_controller.rb +14 -0
- data/app/controllers/masks/keys_controller.rb +45 -0
- data/app/controllers/masks/manage/actor_controller.rb +35 -0
- data/app/controllers/masks/manage/actors_controller.rb +12 -0
- data/app/controllers/masks/manage/base_controller.rb +12 -0
- data/app/controllers/masks/one_time_code_controller.rb +49 -0
- data/app/controllers/masks/passwords_controller.rb +33 -0
- data/app/controllers/masks/recoveries_controller.rb +43 -0
- data/app/controllers/masks/sessions_controller.rb +53 -0
- data/app/helpers/masks/application_helper.rb +49 -0
- data/app/jobs/masks/application_job.rb +7 -0
- data/app/jobs/masks/expire_actors_job.rb +15 -0
- data/app/jobs/masks/expire_recoveries_job.rb +15 -0
- data/app/mailers/masks/actor_mailer.rb +22 -0
- data/app/mailers/masks/application_mailer.rb +15 -0
- data/app/models/concerns/masks/access.rb +162 -0
- data/app/models/concerns/masks/actor.rb +132 -0
- data/app/models/concerns/masks/adapter.rb +68 -0
- data/app/models/concerns/masks/role.rb +9 -0
- data/app/models/concerns/masks/scoped.rb +54 -0
- data/app/models/masks/access/actor_password.rb +20 -0
- data/app/models/masks/access/actor_scopes.rb +18 -0
- data/app/models/masks/access/actor_signup.rb +22 -0
- data/app/models/masks/actors/anonymous.rb +40 -0
- data/app/models/masks/actors/system.rb +24 -0
- data/app/models/masks/adapters/active_record.rb +85 -0
- data/app/models/masks/application_model.rb +15 -0
- data/app/models/masks/application_record.rb +8 -0
- data/app/models/masks/check.rb +192 -0
- data/app/models/masks/credential.rb +166 -0
- data/app/models/masks/credentials/backup_code.rb +30 -0
- data/app/models/masks/credentials/device.rb +59 -0
- data/app/models/masks/credentials/email.rb +48 -0
- data/app/models/masks/credentials/factor2.rb +71 -0
- data/app/models/masks/credentials/key.rb +38 -0
- data/app/models/masks/credentials/last_login.rb +12 -0
- data/app/models/masks/credentials/masquerade.rb +32 -0
- data/app/models/masks/credentials/nickname.rb +63 -0
- data/app/models/masks/credentials/one_time_code.rb +34 -0
- data/app/models/masks/credentials/password.rb +28 -0
- data/app/models/masks/credentials/recovery.rb +71 -0
- data/app/models/masks/credentials/session.rb +67 -0
- data/app/models/masks/device.rb +30 -0
- data/app/models/masks/error.rb +51 -0
- data/app/models/masks/event.rb +14 -0
- data/app/models/masks/mask.rb +255 -0
- data/app/models/masks/rails/actor.rb +190 -0
- data/app/models/masks/rails/actor_role.rb +12 -0
- data/app/models/masks/rails/device.rb +47 -0
- data/app/models/masks/rails/email.rb +96 -0
- data/app/models/masks/rails/key.rb +61 -0
- data/app/models/masks/rails/recovery.rb +116 -0
- data/app/models/masks/rails/role.rb +20 -0
- data/app/models/masks/rails/scope.rb +15 -0
- data/app/models/masks/session.rb +447 -0
- data/app/models/masks/sessions/access.rb +26 -0
- data/app/models/masks/sessions/inline.rb +16 -0
- data/app/models/masks/sessions/request.rb +42 -0
- data/app/resources/masks/actor_resource.rb +9 -0
- data/app/resources/masks/session_resource.rb +15 -0
- data/app/views/layouts/masks/application.html.erb +17 -0
- data/app/views/layouts/masks/mailer.html.erb +17 -0
- data/app/views/layouts/masks/mailer.text.erb +1 -0
- data/app/views/layouts/masks/manage.html.erb +25 -0
- data/app/views/masks/actor_mailer/recover_credentials.html.erb +33 -0
- data/app/views/masks/actor_mailer/recover_credentials.text.erb +1 -0
- data/app/views/masks/actor_mailer/verify_email.html.erb +34 -0
- data/app/views/masks/actor_mailer/verify_email.text.erb +8 -0
- data/app/views/masks/actors/current.html.erb +152 -0
- data/app/views/masks/application/_header.html.erb +31 -0
- data/app/views/masks/backup_codes/new.html.erb +103 -0
- data/app/views/masks/emails/new.html.erb +103 -0
- data/app/views/masks/emails/verify.html.erb +51 -0
- data/app/views/masks/keys/new.html.erb +127 -0
- data/app/views/masks/manage/actor/show.html.erb +126 -0
- data/app/views/masks/manage/actors/index.html.erb +40 -0
- data/app/views/masks/one_time_code/new.html.erb +150 -0
- data/app/views/masks/passwords/edit.html.erb +58 -0
- data/app/views/masks/recoveries/new.html.erb +71 -0
- data/app/views/masks/recoveries/password.html.erb +64 -0
- data/app/views/masks/sessions/new.html.erb +153 -0
- data/config/brakeman.ignore +28 -0
- data/config/locales/en.yml +286 -0
- data/config/routes.rb +46 -0
- data/db/migrate/20231205173845_create_actors.rb +94 -0
- data/lib/generators/masks/install/USAGE +8 -0
- data/lib/generators/masks/install/install_generator.rb +33 -0
- data/lib/generators/masks/install/templates/initializer.rb +5 -0
- data/lib/generators/masks/install/templates/masks.json +6 -0
- data/lib/masks/configuration.rb +236 -0
- data/lib/masks/engine.rb +25 -0
- data/lib/masks/middleware.rb +70 -0
- data/lib/masks/version.rb +5 -0
- data/lib/masks.rb +183 -0
- data/lib/tasks/masks_tasks.rake +71 -0
- data/masks.json +274 -0
- metadata +416 -0
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return ["email", "password", "submit"];
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.email = this.emailTarget.value;
|
10
|
+
this.pass = this.passwordTarget.value;
|
11
|
+
|
12
|
+
this.syncState();
|
13
|
+
}
|
14
|
+
|
15
|
+
updateEmail(e) {
|
16
|
+
this.email = e.target.value;
|
17
|
+
this.syncState();
|
18
|
+
}
|
19
|
+
|
20
|
+
updatePassword(e) {
|
21
|
+
this.pass = e.target.value;
|
22
|
+
this.syncState();
|
23
|
+
}
|
24
|
+
|
25
|
+
syncState() {
|
26
|
+
this.submitTarget.disabled = !this.email?.includes("@") || !this.pass;
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { application } from "./application";
|
2
|
+
|
3
|
+
import SessionController from "./session_controller";
|
4
|
+
import RecoverController from "./recover_controller";
|
5
|
+
import RecoverPasswordController from "./recover_password_controller";
|
6
|
+
import EmailsController from "./emails_controller";
|
7
|
+
import KeysController from "./keys_controller";
|
8
|
+
application.register("session", SessionController);
|
9
|
+
application.register("recover", RecoverController);
|
10
|
+
application.register("recover-password", RecoverPasswordController);
|
11
|
+
application.register("emails", EmailsController);
|
12
|
+
application.register("keys", KeysController);
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return ["settings"];
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
// this.email = this.;emailTarget.value;
|
10
|
+
// this.pass = this.passwordTarget.value;
|
11
|
+
// this.syncState();
|
12
|
+
}
|
13
|
+
|
14
|
+
toggleSettings(e) {
|
15
|
+
this.settingsTarget.classList.toggle("hidden");
|
16
|
+
|
17
|
+
e.preventDefault();
|
18
|
+
e.stopPropagation();
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return ["input", "submit"];
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.input = this.inputTarget.value;
|
10
|
+
this.syncState();
|
11
|
+
}
|
12
|
+
|
13
|
+
updateInput(e) {
|
14
|
+
this.input = e.target.value;
|
15
|
+
this.syncState();
|
16
|
+
}
|
17
|
+
|
18
|
+
syncState() {
|
19
|
+
this.submitTarget.disabled = !this.input?.length;
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return ["password", "submit"];
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.password = this.passwordTarget.value;
|
10
|
+
this.syncState();
|
11
|
+
}
|
12
|
+
|
13
|
+
updatePassword(e) {
|
14
|
+
this.password = e.target.value;
|
15
|
+
this.syncState();
|
16
|
+
}
|
17
|
+
|
18
|
+
syncState() {
|
19
|
+
this.submitTarget.disabled = !this.password?.length;
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return [
|
6
|
+
"nickname",
|
7
|
+
"oneTimeCode",
|
8
|
+
"backupCode",
|
9
|
+
"password",
|
10
|
+
"submit",
|
11
|
+
"remember",
|
12
|
+
"flash",
|
13
|
+
];
|
14
|
+
}
|
15
|
+
|
16
|
+
connect() {
|
17
|
+
this.session = this.hasNicknameTarget
|
18
|
+
? {
|
19
|
+
nickname: this.nicknameTarget.value,
|
20
|
+
password: this.passwordTarget.value,
|
21
|
+
}
|
22
|
+
: {};
|
23
|
+
|
24
|
+
if (this.hasOneTimeCodeTarget) {
|
25
|
+
this.session.code = this.oneTimeCodeTarget.value;
|
26
|
+
this.session.factor2 = true;
|
27
|
+
}
|
28
|
+
|
29
|
+
if (this.hasBackupCodeTarget) {
|
30
|
+
this.session.code = this.backupCodeTarget.value;
|
31
|
+
this.session.factor2 = true;
|
32
|
+
}
|
33
|
+
|
34
|
+
this.syncState();
|
35
|
+
}
|
36
|
+
|
37
|
+
updateCode(e) {
|
38
|
+
this.session.code = e.target.value;
|
39
|
+
this.syncState();
|
40
|
+
}
|
41
|
+
|
42
|
+
updateNickname(e) {
|
43
|
+
this.session.nickname = e.target.value;
|
44
|
+
this.syncState();
|
45
|
+
}
|
46
|
+
|
47
|
+
updatePassword(e) {
|
48
|
+
this.session.password = e.target.value;
|
49
|
+
this.syncState();
|
50
|
+
}
|
51
|
+
|
52
|
+
syncState() {
|
53
|
+
this.submitTarget.classList.add("btn-accent");
|
54
|
+
|
55
|
+
let flash;
|
56
|
+
|
57
|
+
if (this.session.nickname && this.session.password) {
|
58
|
+
this.submitTarget.disabled = false;
|
59
|
+
this.rememberTarget.classList.remove("hidden");
|
60
|
+
|
61
|
+
flash = "continue";
|
62
|
+
} else if (this.session.factor2) {
|
63
|
+
const disabled = (this.session.code?.length || 0) < 6;
|
64
|
+
this.submitTarget.disabled = disabled;
|
65
|
+
|
66
|
+
if (disabled) {
|
67
|
+
this.rememberTarget.classList.add("hidden");
|
68
|
+
} else {
|
69
|
+
this.rememberTarget.classList.remove("hidden");
|
70
|
+
}
|
71
|
+
|
72
|
+
flash = "enter-factor2";
|
73
|
+
} else {
|
74
|
+
this.submitTarget.disabled = true;
|
75
|
+
this.rememberTarget.classList.add("hidden");
|
76
|
+
|
77
|
+
if (this.session.nickname) {
|
78
|
+
flash = "enter-password";
|
79
|
+
} else {
|
80
|
+
flash = "enter-credentials";
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
if (flash) {
|
85
|
+
for (let el of this.flashTarget.querySelectorAll("[data-flash]")) {
|
86
|
+
if (el.dataset.flash == flash) {
|
87
|
+
el.classList.remove("hidden");
|
88
|
+
} else {
|
89
|
+
el.classList.add("hidden");
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
16
|
+
@tailwind base;
|
17
|
+
@tailwind components;
|
18
|
+
@tailwind utilities;
|
19
|
+
|
20
|
+
.pagination {
|
21
|
+
@apply flex gap-2 items-center justify-center p-4;
|
22
|
+
}
|
23
|
+
|
24
|
+
.pagination .page {
|
25
|
+
@apply btn btn-sm;
|
26
|
+
}
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# Helpers for interacting with Masks in Rails controllers.
|
5
|
+
#
|
6
|
+
# @see ClassMethods
|
7
|
+
module Controller
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# Require a session masked by the type passed.
|
12
|
+
#
|
13
|
+
# If this fails +unathorized_access+ is called.
|
14
|
+
#
|
15
|
+
# @param [Symbol|String] type
|
16
|
+
# @param [Hash] opts forwarded to +before_action+
|
17
|
+
def require_mask(type: nil, **opts)
|
18
|
+
before_action(
|
19
|
+
:unauthorized_access,
|
20
|
+
unless: -> { masked?(type:) },
|
21
|
+
**opts
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Builds an access object by the passed name, if allowed.
|
26
|
+
#
|
27
|
+
# If this succeeds, the controller instance's +current_access+ will return
|
28
|
+
# the constructed access class. Otherwise, on failure the
|
29
|
+
# +unathorized_access+ method is called.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# class MyController < ApplicationController
|
33
|
+
# include Masks::Controller
|
34
|
+
#
|
35
|
+
# require_access 'my.example', only: :index
|
36
|
+
#
|
37
|
+
# # this action is only called if the session is
|
38
|
+
# # able to build the "my.example" access class.
|
39
|
+
# def index
|
40
|
+
# render json: {
|
41
|
+
# foobar: current_access.foo_bar
|
42
|
+
# }
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @param [Symbol|String] name
|
47
|
+
# @param [Hash] opts forwarded to +before_action+
|
48
|
+
def require_access(name, **opts)
|
49
|
+
before_action(
|
50
|
+
:unauthorized_access,
|
51
|
+
unless: -> { build_access(name) },
|
52
|
+
**opts
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def passed?
|
60
|
+
current_actor && masked_session.passed?
|
61
|
+
end
|
62
|
+
|
63
|
+
def unauthorized_access
|
64
|
+
render plain: "", status: :unauthorized
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the current masks session for the request.
|
68
|
+
#
|
69
|
+
# @return [Masks::Session]
|
70
|
+
# @visibility public
|
71
|
+
def masked_session
|
72
|
+
@masked_session ||= request.env[Masks::Middleware::SESSION_KEY]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the mask for the request.
|
76
|
+
#
|
77
|
+
# @return [Masks::Mask]
|
78
|
+
# @visibility public
|
79
|
+
def current_mask
|
80
|
+
masked_session.mask
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the mask for the request.
|
84
|
+
#
|
85
|
+
# @return [Masks::Actor]
|
86
|
+
# @visibility public
|
87
|
+
def current_actor
|
88
|
+
masked_session.scoped
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the mask for the request.
|
92
|
+
#
|
93
|
+
# @return [Masks::Access]
|
94
|
+
# @visibility public
|
95
|
+
def current_access
|
96
|
+
@current_access
|
97
|
+
end
|
98
|
+
|
99
|
+
def build_access(name)
|
100
|
+
@current_access = Masks.access(name, masked_session)
|
101
|
+
rescue Masks::Error::Unauthorized
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
def masked?(type: nil)
|
106
|
+
if type &&
|
107
|
+
!Array.wrap(type).map(&:to_s).include?(masked_session.mask.type)
|
108
|
+
false
|
109
|
+
else
|
110
|
+
masked_session.passed?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
class ActorsController < ApplicationController
|
6
|
+
require_mask type: %i[session api], only: :current
|
7
|
+
|
8
|
+
def current
|
9
|
+
respond_to do |format|
|
10
|
+
format.json { render json: ActorResource.new(current_actor) }
|
11
|
+
format.html { render(:current) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
class ApplicationController < ActionController::Base
|
6
|
+
include Masks::Controller
|
7
|
+
include Pagy::Backend
|
8
|
+
|
9
|
+
before_action :assign_session
|
10
|
+
|
11
|
+
skip_before_action :verify_authenticity_token, if: :json_request?
|
12
|
+
|
13
|
+
protect_from_forgery with: :exception
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def json_request?
|
18
|
+
request.format.symbol == :json
|
19
|
+
end
|
20
|
+
|
21
|
+
def assign_session
|
22
|
+
@session = masked_session
|
23
|
+
@config = @session.config
|
24
|
+
@actor = @session.actor
|
25
|
+
end
|
26
|
+
|
27
|
+
def require_sudo(redirect)
|
28
|
+
return if current_mask.type == "sudo" && passed?
|
29
|
+
|
30
|
+
flash[:errors] = ["enter a valid password"]
|
31
|
+
|
32
|
+
redirect_to redirect
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
class BackupCodesController < ApplicationController
|
6
|
+
require_mask type: :session, only: :new
|
7
|
+
before_action only: %i[create] do
|
8
|
+
require_sudo(backup_codes_path)
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
respond_to { |format| format.html { render(:new) } }
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
if create_params[:reset]
|
17
|
+
@actor.saved_backup_codes_at = nil
|
18
|
+
@actor.backup_codes = nil
|
19
|
+
@actor.save
|
20
|
+
elsif create_params[:enable]
|
21
|
+
@actor.saved_backup_codes_at = Time.current
|
22
|
+
@actor.save
|
23
|
+
end
|
24
|
+
|
25
|
+
respond_to { |format| format.html { redirect_to backup_codes_path } }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def create_params
|
31
|
+
params.require(:backup_codes).permit(:reset, :enable)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
class DevicesController < ApplicationController
|
6
|
+
require_mask type: :session, only: :update
|
7
|
+
|
8
|
+
def update
|
9
|
+
device =
|
10
|
+
masked_session.config.find_device(masked_session, key: params[:key])
|
11
|
+
|
12
|
+
if device&.persisted? && params[:reset]
|
13
|
+
device.reset_version
|
14
|
+
device.save!
|
15
|
+
end
|
16
|
+
|
17
|
+
redirect_back_or_to "/"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
class EmailsController < ApplicationController
|
6
|
+
require_mask type: :session, only: :new
|
7
|
+
before_action :require_sudo, only: :create
|
8
|
+
|
9
|
+
def new
|
10
|
+
@emails = current_actor.emails
|
11
|
+
|
12
|
+
respond_to { |format| format.html { render(:new) } }
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
@email = current_actor.emails.build(email: email_param.strip)
|
17
|
+
|
18
|
+
if @email.valid?
|
19
|
+
@email.notify!(masked_session)
|
20
|
+
else
|
21
|
+
flash[:errors] = @email.errors.full_messages
|
22
|
+
end
|
23
|
+
|
24
|
+
respond_to { |format| format.html { redirect_to emails_path } }
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify
|
28
|
+
@email = current_actor.emails.find_by(email: email_param.strip)
|
29
|
+
@email.notify!(masked_session) if @email.expired?
|
30
|
+
|
31
|
+
respond_to { |format| format.html { redirect_to emails_path } }
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete
|
35
|
+
@email = current_actor.emails.find_by(email: email_param)
|
36
|
+
@email&.destroy
|
37
|
+
|
38
|
+
respond_to { |format| format.html { redirect_to emails_path } }
|
39
|
+
end
|
40
|
+
|
41
|
+
def verify
|
42
|
+
@email = current_actor.emails.find_by(token: params[:email])
|
43
|
+
@email&.verify! if @email&.valid?
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def email_param
|
49
|
+
params.dig(:email, :value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def require_sudo
|
53
|
+
return if current_mask.type == "sudo" && passed?
|
54
|
+
|
55
|
+
flash[:errors] = ["enter a valid password"]
|
56
|
+
|
57
|
+
redirect_to emails_path
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
class ErrorController < ApplicationController
|
6
|
+
def render_unauthorized
|
7
|
+
render plain: "", status: 401
|
8
|
+
end
|
9
|
+
|
10
|
+
def redirect
|
11
|
+
redirect_to masked_session.mask.fail
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
class KeysController < ApplicationController
|
6
|
+
require_mask type: :session
|
7
|
+
|
8
|
+
def new
|
9
|
+
@keys = current_actor.keys
|
10
|
+
end
|
11
|
+
|
12
|
+
def create
|
13
|
+
key =
|
14
|
+
current_actor.keys.build(
|
15
|
+
name: create_params[:name],
|
16
|
+
secret: create_params[:secret]&.presence,
|
17
|
+
scopes: create_params[:scopes]
|
18
|
+
)
|
19
|
+
key.save
|
20
|
+
|
21
|
+
if key.valid?
|
22
|
+
flash[:key] = { name: key.name, secret: key.secret }
|
23
|
+
else
|
24
|
+
flash[:error] = key.errors.full_messages.first
|
25
|
+
end
|
26
|
+
|
27
|
+
redirect_back_or_to keys_path
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete
|
31
|
+
key = current_actor.keys.find(params[:id])
|
32
|
+
key.destroy
|
33
|
+
|
34
|
+
flash[:notice] = "#{key.name} destroyed" if key.destroyed?
|
35
|
+
|
36
|
+
redirect_back_or_to "/"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_params
|
42
|
+
params.require(:key).permit(:name, :secret, scopes: [])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Masks
|
4
|
+
# @visibility private
|
5
|
+
module Manage
|
6
|
+
class ActorController < BaseController
|
7
|
+
before_action :find_actor
|
8
|
+
|
9
|
+
def update
|
10
|
+
Masks::Rails::Actor.transaction do
|
11
|
+
if params[:add_scope]
|
12
|
+
@actor.assign_scopes!(params[:add_scope])
|
13
|
+
flash[:info] = "added scope"
|
14
|
+
elsif params[:remove_scope]
|
15
|
+
@actor.remove_scopes!(params[:remove_scope])
|
16
|
+
flash[:info] = "removed scope"
|
17
|
+
elsif params[:remove_factor2]
|
18
|
+
@actor.remove_factor2! if params[:remove_factor2]
|
19
|
+
flash[:info] = "removed second factor authentication"
|
20
|
+
end
|
21
|
+
|
22
|
+
redirect_to actor_path(@actor)
|
23
|
+
end
|
24
|
+
|
25
|
+
@actor
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def find_actor
|
31
|
+
@actor = Masks::Rails::Actor.find(params[:actor])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|