masks 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module OpenID
|
4
|
+
# Manages authorizations for OpenID/OAuth2 requests.
|
5
|
+
class Authorization
|
6
|
+
attr_accessor :client, :scopes, :response, :response_type
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def perform(env, **opts)
|
10
|
+
authorization = new(env, **opts)
|
11
|
+
authorization.perform
|
12
|
+
authorization
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(env, **opts)
|
17
|
+
@env = env
|
18
|
+
@app =
|
19
|
+
Rack::OAuth2::Server::Authorize.new do |req, res|
|
20
|
+
@client =
|
21
|
+
session.config.model(:openid_client).find_by(key: req.client_id)
|
22
|
+
|
23
|
+
req.bad_request!(:client_id, "not found") unless @client
|
24
|
+
|
25
|
+
unless req.redirect_uri
|
26
|
+
req.invalid_request!('"redirect_uri" missing')
|
27
|
+
end
|
28
|
+
|
29
|
+
unless @client.redirect_uris.any?
|
30
|
+
@client.redirect_uris = [req.redirect_uri.to_s]
|
31
|
+
@client.valid? || req.invalid_request!('"redirect_uri" invalid')
|
32
|
+
end
|
33
|
+
|
34
|
+
res.redirect_uri = req.verify_redirect_uri!(@client.redirect_uris)
|
35
|
+
|
36
|
+
@scopes = req.scope & @client.scopes
|
37
|
+
|
38
|
+
if res.protocol_params_location == :fragment && req.nonce.blank?
|
39
|
+
req.invalid_request! "nonce required"
|
40
|
+
end
|
41
|
+
|
42
|
+
if @client.response_types.include?(
|
43
|
+
Array(req.response_type).collect(&:to_s).join(" ")
|
44
|
+
)
|
45
|
+
if actor
|
46
|
+
if opts[:approved] || client.auto_consent?
|
47
|
+
@client.save if @client.redirect_uris_changed?
|
48
|
+
|
49
|
+
approved! req, res
|
50
|
+
elsif opts.key?(:approved)
|
51
|
+
req.access_denied!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
req.unsupported_response_type!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def session
|
61
|
+
@session ||= @env[Masks::Middleware::SESSION_KEY]
|
62
|
+
end
|
63
|
+
|
64
|
+
def actor
|
65
|
+
@actor ||= (session.actor if session.passed?)
|
66
|
+
end
|
67
|
+
|
68
|
+
def perform
|
69
|
+
@response = @app.call(@env)
|
70
|
+
end
|
71
|
+
|
72
|
+
def approved!(req, res)
|
73
|
+
response_types = Array(req.response_type)
|
74
|
+
|
75
|
+
if response_types.include? :code
|
76
|
+
authorization =
|
77
|
+
actor.openid_authorizations.create!(
|
78
|
+
openid_client: client,
|
79
|
+
redirect_uri: res.redirect_uri,
|
80
|
+
nonce: req.nonce,
|
81
|
+
scopes: @scopes
|
82
|
+
)
|
83
|
+
|
84
|
+
res.code = authorization.code
|
85
|
+
end
|
86
|
+
|
87
|
+
if response_types.include? :token
|
88
|
+
access_token =
|
89
|
+
actor.openid_access_tokens.create!(
|
90
|
+
openid_client: client,
|
91
|
+
scopes: @scopes
|
92
|
+
)
|
93
|
+
|
94
|
+
res.access_token = access_token.to_bearer_token
|
95
|
+
end
|
96
|
+
|
97
|
+
if response_types.include? :id_token
|
98
|
+
id_token =
|
99
|
+
actor.openid_id_tokens.create!(
|
100
|
+
openid_client: @client,
|
101
|
+
nonce: req.nonce
|
102
|
+
)
|
103
|
+
|
104
|
+
res.id_token =
|
105
|
+
id_token.to_jwt(
|
106
|
+
code: (res.respond_to?(:code) ? res.code : nil),
|
107
|
+
access_token:
|
108
|
+
(res.respond_to?(:access_token) ? res.access_token : nil)
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
res.approve!
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module OpenID
|
4
|
+
# Implementation of the Token Endpoint in OIDC.
|
5
|
+
#
|
6
|
+
# Technically speaking, this conforms to the rack interface
|
7
|
+
# so it can be used directly for managing requests for access
|
8
|
+
# tokens.
|
9
|
+
class Token
|
10
|
+
attr_accessor :app
|
11
|
+
|
12
|
+
delegate :call, to: :app
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@app =
|
16
|
+
Rack::OAuth2::Server::Token.new do |req, res|
|
17
|
+
client =
|
18
|
+
Masks
|
19
|
+
.configuration
|
20
|
+
.model(:openid_client)
|
21
|
+
.find_by(key: req.client_id) || req.invalid_client!
|
22
|
+
client.secret == req.client_secret || req.invalid_client!
|
23
|
+
client.grant_types.include?(req.grant_type.to_s) ||
|
24
|
+
req.unsupported_grant_type!
|
25
|
+
|
26
|
+
case req.grant_type
|
27
|
+
when :client_credentials
|
28
|
+
res.access_token = client.access_tokens.create!.to_bearer_token
|
29
|
+
when :authorization_code
|
30
|
+
authorization =
|
31
|
+
client.authorizations.valid.where(code: req.code).first
|
32
|
+
unless authorization&.valid_redirect_uri?(req.redirect_uri)
|
33
|
+
req.invalid_grant!
|
34
|
+
end
|
35
|
+
access_token = authorization.access_token
|
36
|
+
res.access_token = access_token.to_bearer_token
|
37
|
+
|
38
|
+
if access_token.scope?("openid")
|
39
|
+
res.id_token =
|
40
|
+
access_token
|
41
|
+
.actor
|
42
|
+
.openid_id_tokens
|
43
|
+
.create!(
|
44
|
+
openid_client: access_token.openid_client,
|
45
|
+
nonce: authorization.nonce
|
46
|
+
)
|
47
|
+
.to_jwt
|
48
|
+
end
|
49
|
+
else
|
50
|
+
req.unsupported_grant_type!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -32,6 +32,15 @@ module Masks
|
|
32
32
|
has_many :keys,
|
33
33
|
class_name: Masks.configuration.models[:key],
|
34
34
|
autosave: true
|
35
|
+
has_many :openid_authorizations,
|
36
|
+
class_name: Masks.configuration.models[:openid_authorization],
|
37
|
+
autosave: true
|
38
|
+
has_many :openid_access_tokens,
|
39
|
+
class_name: Masks.configuration.models[:openid_access_token],
|
40
|
+
autosave: true
|
41
|
+
has_many :openid_id_tokens,
|
42
|
+
class_name: Masks.configuration.models[:openid_id_token],
|
43
|
+
autosave: true
|
35
44
|
|
36
45
|
has_secure_password
|
37
46
|
|
@@ -41,7 +50,7 @@ module Masks
|
|
41
50
|
|
42
51
|
before_validation :reset_version, unless: :version
|
43
52
|
|
44
|
-
validates :nickname, uniqueness: { case_sensitive: false }
|
53
|
+
validates :nickname, presence: true, uniqueness: { case_sensitive: false }
|
45
54
|
validates :totp_secret, presence: true, if: :totp_code
|
46
55
|
validates :version, presence: true
|
47
56
|
validate :validates_totp, if: :totp_code
|
@@ -52,6 +61,14 @@ module Masks
|
|
52
61
|
|
53
62
|
serialize :backup_codes, coder: JSON
|
54
63
|
|
64
|
+
def to_param
|
65
|
+
nickname
|
66
|
+
end
|
67
|
+
|
68
|
+
def primary_email
|
69
|
+
emails.where(verified: true).first
|
70
|
+
end
|
71
|
+
|
55
72
|
def email_addresses
|
56
73
|
emails.pluck(:email)
|
57
74
|
end
|
@@ -68,6 +85,11 @@ module Masks
|
|
68
85
|
save!
|
69
86
|
end
|
70
87
|
|
88
|
+
def logout!
|
89
|
+
reset_version
|
90
|
+
save!
|
91
|
+
end
|
92
|
+
|
71
93
|
def totp_uri
|
72
94
|
(totp || random_totp).provisioning_uri(nickname)
|
73
95
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module Rails
|
4
|
+
module OpenID
|
5
|
+
class AccessToken < ApplicationRecord
|
6
|
+
include Masks::Scoped
|
7
|
+
|
8
|
+
self.table_name = "openid_access_tokens"
|
9
|
+
|
10
|
+
scope :valid, -> { where("expires_at >= ?", Time.now.utc) }
|
11
|
+
|
12
|
+
belongs_to :actor,
|
13
|
+
class_name: Masks.configuration.models[:actor],
|
14
|
+
optional: true
|
15
|
+
belongs_to :openid_client,
|
16
|
+
class_name: Masks.configuration.models[:openid_client]
|
17
|
+
|
18
|
+
serialize :scopes, coder: JSON
|
19
|
+
|
20
|
+
before_validation :generate_token
|
21
|
+
|
22
|
+
validates :openid_client, presence: true
|
23
|
+
validates :token, presence: true, uniqueness: true
|
24
|
+
validates :expires_at, presence: true
|
25
|
+
|
26
|
+
def scopes
|
27
|
+
value = self[:scopes]
|
28
|
+
|
29
|
+
return [] unless value
|
30
|
+
|
31
|
+
value & ((actor&.scopes || []) + openid_client.scopes)
|
32
|
+
end
|
33
|
+
|
34
|
+
def roles(*args, **opts)
|
35
|
+
(actor || openid_client).roles(*args, **opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_bearer_token
|
39
|
+
Rack::OAuth2::AccessToken::Bearer.new(
|
40
|
+
access_token: token,
|
41
|
+
expires_in: (expires_at - Time.now.utc).to_i
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def generate_token
|
48
|
+
self.token ||= SecureRandom.uuid
|
49
|
+
self.expires_at ||= openid_client.token_expires_at
|
50
|
+
self.scopes ||= []
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module Rails
|
4
|
+
module OpenID
|
5
|
+
class Authorization < ApplicationRecord
|
6
|
+
self.table_name = "openid_authorizations"
|
7
|
+
|
8
|
+
scope :valid, -> { where("expires_at >= ?", Time.now.utc) }
|
9
|
+
|
10
|
+
belongs_to :actor, class_name: Masks.configuration.models[:actor]
|
11
|
+
belongs_to :openid_client,
|
12
|
+
class_name: Masks.configuration.models[:openid_client]
|
13
|
+
|
14
|
+
serialize :scopes, coder: JSON
|
15
|
+
|
16
|
+
before_validation :generate_code
|
17
|
+
|
18
|
+
validates :actor, presence: true
|
19
|
+
validates :openid_client, presence: true
|
20
|
+
validates :code, presence: true, uniqueness: true
|
21
|
+
validates :expires_at, presence: true
|
22
|
+
|
23
|
+
def valid_redirect_uri?(uri)
|
24
|
+
uri == redirect_uri
|
25
|
+
end
|
26
|
+
|
27
|
+
def access_token
|
28
|
+
@access_token ||=
|
29
|
+
update_attribute!(:expires_at, Time.now) && generate_access_token!
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate_access_token!
|
33
|
+
actor.openid_access_tokens.create!(openid_client:, scopes:)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def generate_code
|
39
|
+
self.code ||= SecureRandom.uuid
|
40
|
+
self.expires_at ||= openid_client.code_expires_at
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module Rails
|
4
|
+
module OpenID
|
5
|
+
class Client < ApplicationRecord
|
6
|
+
include Masks::Scoped
|
7
|
+
|
8
|
+
self.table_name = "openid_clients"
|
9
|
+
|
10
|
+
validates :name, presence: true
|
11
|
+
|
12
|
+
after_initialize :generate_credentials
|
13
|
+
before_validation :generate_key, on: :create
|
14
|
+
|
15
|
+
serialize :scopes, coder: JSON
|
16
|
+
serialize :redirect_uris, coder: JSON
|
17
|
+
serialize :response_types, coder: JSON
|
18
|
+
serialize :grant_types, coder: JSON
|
19
|
+
|
20
|
+
validates :key, :secret, :scopes, presence: true
|
21
|
+
validates :key, uniqueness: true
|
22
|
+
validates :client_type,
|
23
|
+
inclusion: {
|
24
|
+
in: %w[public confidential]
|
25
|
+
},
|
26
|
+
presence: true
|
27
|
+
validates :subject_type,
|
28
|
+
inclusion: {
|
29
|
+
in: Masks.configuration.openid[:subject_types]
|
30
|
+
},
|
31
|
+
presence: true
|
32
|
+
validate :validate_expiries
|
33
|
+
|
34
|
+
has_many :access_tokens,
|
35
|
+
class_name: Masks.configuration.models[:openid_access_token],
|
36
|
+
inverse_of: :openid_client
|
37
|
+
has_many :authorizations,
|
38
|
+
class_name: Masks.configuration.models[:openid_authorization],
|
39
|
+
inverse_of: :openid_client
|
40
|
+
has_many :saved_roles,
|
41
|
+
class_name: Masks.configuration.models[:role],
|
42
|
+
autosave: true
|
43
|
+
|
44
|
+
def to_param
|
45
|
+
key
|
46
|
+
end
|
47
|
+
|
48
|
+
def response_types
|
49
|
+
case client_type
|
50
|
+
when "confidential"
|
51
|
+
["code"]
|
52
|
+
when "public"
|
53
|
+
["token", "id_token", "id_token token"]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def grant_types
|
58
|
+
case client_type
|
59
|
+
when "confidential"
|
60
|
+
%w[refresh_token authorization_code client_credentials]
|
61
|
+
else
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def scopes
|
67
|
+
self[:scopes]
|
68
|
+
end
|
69
|
+
|
70
|
+
def roles(record, **opts)
|
71
|
+
case record
|
72
|
+
when Class, String
|
73
|
+
saved_roles.where(record_type: record.to_s, **opts).includes(
|
74
|
+
:record
|
75
|
+
)
|
76
|
+
else
|
77
|
+
saved_roles.where(record:, **opts)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def issuer
|
82
|
+
Masks::Engine.routes.url_helpers.openid_issuer_url(
|
83
|
+
id: key,
|
84
|
+
host: Masks.configuration.site_url
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def kid
|
89
|
+
:default
|
90
|
+
end
|
91
|
+
|
92
|
+
def private_key
|
93
|
+
OpenSSL::PKey::RSA.new(rsa_private_key)
|
94
|
+
end
|
95
|
+
|
96
|
+
delegate :public_key, to: :private_key
|
97
|
+
|
98
|
+
def subject(actor)
|
99
|
+
case subject_type
|
100
|
+
when "nickname"
|
101
|
+
actor.nickname
|
102
|
+
else
|
103
|
+
Digest::SHA256.hexdigest(
|
104
|
+
[
|
105
|
+
sector_identifier,
|
106
|
+
actor.actor_id,
|
107
|
+
Masks.configuration.openid[:pairwise_salt]
|
108
|
+
].join("/")
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def audience
|
114
|
+
key
|
115
|
+
end
|
116
|
+
|
117
|
+
def auto_consent?
|
118
|
+
!consent
|
119
|
+
end
|
120
|
+
|
121
|
+
def pairwise_subject?
|
122
|
+
sector_identifier && subject_type == "pairwise"
|
123
|
+
end
|
124
|
+
|
125
|
+
def assign_scopes!(*scopes)
|
126
|
+
self.scopes = [*scopes, *self.scopes].uniq.compact
|
127
|
+
save!
|
128
|
+
end
|
129
|
+
|
130
|
+
def remove_scopes!(*scopes)
|
131
|
+
scopes.each { |scope| self.scopes.delete(scope) }
|
132
|
+
|
133
|
+
save!
|
134
|
+
end
|
135
|
+
|
136
|
+
def code_expires_at
|
137
|
+
Time.now + ChronicDuration.parse(code_expires_in)
|
138
|
+
end
|
139
|
+
|
140
|
+
def token_expires_at
|
141
|
+
Time.now + ChronicDuration.parse(token_expires_in)
|
142
|
+
end
|
143
|
+
|
144
|
+
def refresh_expires_at
|
145
|
+
Time.now + ChronicDuration.parse(refresh_expires_in)
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def generate_credentials
|
151
|
+
self.secret ||= SecureRandom.uuid
|
152
|
+
self.client_type ||= "confidential"
|
153
|
+
self.subject_type ||= "nickname"
|
154
|
+
self.scopes ||= Masks.configuration.openid[:scopes] || []
|
155
|
+
self.rsa_private_key ||= OpenSSL::PKey::RSA.generate(2048).to_pem
|
156
|
+
self.sector_identifier ||=
|
157
|
+
URI.parse(Masks.configuration.site_url).host
|
158
|
+
self.code_expires_in ||= "5 minutes"
|
159
|
+
self.token_expires_in ||= "1 day"
|
160
|
+
self.refresh_expires_in ||= "1 week"
|
161
|
+
end
|
162
|
+
|
163
|
+
def generate_key
|
164
|
+
return unless name
|
165
|
+
|
166
|
+
key = name.parameterize
|
167
|
+
count = self.class.where("key LIKE ?", "#{key}%").count
|
168
|
+
|
169
|
+
self.key = count.positive? ? "#{key}-#{count + 1}" : key
|
170
|
+
end
|
171
|
+
|
172
|
+
def validate_expiries
|
173
|
+
%i[
|
174
|
+
code_expires_in
|
175
|
+
token_expires_in
|
176
|
+
refresh_expires_in
|
177
|
+
].each do |param|
|
178
|
+
unless ChronicDuration.parse(send(param))
|
179
|
+
errors.add(param, :invalid)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Masks
|
3
|
+
module Rails
|
4
|
+
module OpenID
|
5
|
+
class IdToken < ApplicationRecord
|
6
|
+
self.table_name = "openid_id_tokens"
|
7
|
+
|
8
|
+
belongs_to :actor, class_name: Masks.configuration.models[:actor]
|
9
|
+
belongs_to :openid_client,
|
10
|
+
class_name: Masks.configuration.models[:openid_client]
|
11
|
+
|
12
|
+
def to_response_object(with = {})
|
13
|
+
subject =
|
14
|
+
if openid_client.pairwise_subject?
|
15
|
+
openid_client.subject_for(actor)
|
16
|
+
else
|
17
|
+
actor.actor_id
|
18
|
+
end
|
19
|
+
|
20
|
+
claims = {
|
21
|
+
sub: subject,
|
22
|
+
iss: openid_client.issuer,
|
23
|
+
aud: openid_client.audience,
|
24
|
+
exp: expires_at.to_i,
|
25
|
+
iat: created_at.to_i,
|
26
|
+
nonce:
|
27
|
+
}
|
28
|
+
|
29
|
+
id_token = OpenIDConnect::ResponseObject::IdToken.new(claims)
|
30
|
+
id_token.code = with[:code] if with[:code]
|
31
|
+
id_token.access_token = with[:access_token] if with[:access_token]
|
32
|
+
id_token
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_jwt(with = {})
|
36
|
+
to_response_object(with).to_jwt(openid_client.private_key) do |jwt|
|
37
|
+
jwt.kid = openid_client.kid
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -19,7 +19,8 @@ module Masks
|
|
19
19
|
def matches_mask?(mask)
|
20
20
|
return false unless mask.access == name.to_s
|
21
21
|
|
22
|
-
original.mask.access&.try(:include?, name.to_s) ||
|
22
|
+
original.mask.access&.try(:include?, name.to_s) ||
|
23
|
+
original.mask.access == mask.access
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
@@ -9,12 +9,29 @@
|
|
9
9
|
<%= javascript_include_tag "masks/application", "data-turbo-track": "reload", defer: true %>
|
10
10
|
</head>
|
11
11
|
<body class="h-full">
|
12
|
-
<div class="navbar
|
13
|
-
<div class="
|
14
|
-
|
12
|
+
<div class="navbar bg-base-100 mb-6">
|
13
|
+
<div class="navbar-start">
|
14
|
+
<div class="dropdown">
|
15
|
+
<div tabindex="0" role="button" class="btn btn-ghost text-lg">
|
16
|
+
masks
|
17
|
+
</div>
|
18
|
+
<ul tabindex="0" class="menu dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52 gap-1">
|
19
|
+
<li><a class="<%= section == :dashboard ? 'active' : '' %>" href="<%= manage_path %>">Dashboard</a></li>
|
20
|
+
<li><a class="<%= section == :clients ? 'active' : '' %>" href="<%= manage_clients_path %>">Clients</a></li>
|
21
|
+
<li><a class="<%= section == :actors ? 'active' : '' %>" href="<%= manage_actors_path %>">Actors</a></li>
|
22
|
+
<li><a class="<%= section == :devices ? 'active' : '' %>" href="<%= manage_devices_path %>">Devices</a></li>
|
23
|
+
</ul>
|
24
|
+
</div>
|
15
25
|
</div>
|
16
|
-
|
17
|
-
|
26
|
+
|
27
|
+
<div class="navbar-end pr-2">
|
28
|
+
<a href="<%= manage_actor_path(current_actor) %>">
|
29
|
+
<div class="avatar placeholder">
|
30
|
+
<div class="bg-neutral text-neutral-content rounded-full w-10">
|
31
|
+
<span class="text-base"><%= current_actor.nickname.slice(0, 1).upcase %></span>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</a>
|
18
35
|
</div>
|
19
36
|
</div>
|
20
37
|
|
@@ -15,9 +15,8 @@
|
|
15
15
|
</div>
|
16
16
|
<a
|
17
17
|
role="button"
|
18
|
-
href="<%= recover_password_url(token: @recovery)%>"
|
19
|
-
class="btn"
|
20
|
-
>
|
18
|
+
href="<%= recover_password_url(token: @recovery) %>"
|
19
|
+
class="btn">
|
21
20
|
<%= lucide_icon("rotate-cw", class: "stroke-warning") %>
|
22
21
|
<%= t(".recover") %>
|
23
22
|
</a>
|
@@ -16,9 +16,8 @@
|
|
16
16
|
</div>
|
17
17
|
<a
|
18
18
|
role="button"
|
19
|
-
href="<%= email_verify_url(email: @email, approve: true)%>"
|
20
|
-
class="btn"
|
21
|
-
>
|
19
|
+
href="<%= email_verify_url(email: @email, approve: true) %>"
|
20
|
+
class="btn">
|
22
21
|
<%= lucide_icon("mail-check", class: "stroke-success") %>
|
23
22
|
<%= t(".verify") %>
|
24
23
|
</a>
|