masks 0.4.0 → 0.5.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.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/LICENSE +21 -0
  4. data/README.md +215 -29
  5. data/app/controllers/masks/rails/base_controller.rb +13 -0
  6. data/app/controllers/masks/rails/handshakes_controller.rb +87 -0
  7. data/app/controllers/masks/rails/sessions_controller.rb +113 -0
  8. data/app/views/layouts/masks/rails/plain.html.erb +47 -0
  9. data/app/views/masks/rails/handshakes/refused.html.erb +12 -0
  10. data/app/views/masks/rails/handshakes/show.html.erb +40 -0
  11. data/config/routes.rb +10 -82
  12. data/lib/generators/masks/install/install_generator.rb +35 -22
  13. data/lib/generators/masks/install/templates/masks.rb.tt +38 -0
  14. data/lib/masks/client/claims.rb +103 -0
  15. data/lib/masks/client/errors.rb +52 -0
  16. data/lib/masks/client/handshake.rb +104 -0
  17. data/lib/masks/client/http.rb +81 -0
  18. data/lib/masks/client/introspection.rb +29 -0
  19. data/lib/masks/client/issuer.rb +76 -0
  20. data/lib/masks/client/pkce.rb +27 -0
  21. data/lib/masks/client/rack.rb +62 -0
  22. data/lib/masks/client/registration.rb +91 -0
  23. data/lib/masks/client/registry.rb +25 -0
  24. data/lib/masks/client/resource.rb +92 -0
  25. data/lib/masks/client/session.rb +146 -0
  26. data/lib/masks/client/stores.rb +40 -0
  27. data/lib/masks/client/tokens.rb +70 -0
  28. data/lib/masks/client/tracker.rb +122 -0
  29. data/lib/masks/client/verifier.rb +42 -0
  30. data/lib/masks/client.rb +51 -0
  31. data/lib/masks/rails/authentication.rb +217 -0
  32. data/lib/masks/rails/configurable.rb +11 -0
  33. data/lib/masks/rails/configuration.rb +172 -0
  34. data/lib/masks/rails/credentials.rb +60 -0
  35. data/lib/masks/rails/engine.rb +20 -0
  36. data/lib/masks/rails/protected_resource.rb +51 -0
  37. data/lib/masks/rails.rb +10 -0
  38. data/lib/masks/version.rb +1 -3
  39. data/lib/masks.rb +3 -183
  40. metadata +47 -416
  41. data/MIT-LICENSE +0 -20
  42. data/Rakefile +0 -11
  43. data/app/assets/builds/masks/application.css +0 -1
  44. data/app/assets/builds/masks/application.js +0 -8549
  45. data/app/assets/builds/masks/application.js.map +0 -7
  46. data/app/assets/images/masks.png +0 -0
  47. data/app/assets/javascripts/application.js +0 -2
  48. data/app/assets/javascripts/controllers/application.js +0 -9
  49. data/app/assets/javascripts/controllers/emails_controller.js +0 -28
  50. data/app/assets/javascripts/controllers/index.js +0 -21
  51. data/app/assets/javascripts/controllers/keys_controller.js +0 -20
  52. data/app/assets/javascripts/controllers/recover_controller.js +0 -21
  53. data/app/assets/javascripts/controllers/recover_password_controller.js +0 -21
  54. data/app/assets/javascripts/controllers/session_controller.js +0 -94
  55. data/app/assets/javascripts/controllers/table_controller.js +0 -15
  56. data/app/assets/manifest.js +0 -2
  57. data/app/assets/masks_manifest.js +0 -2
  58. data/app/assets/stylesheets/application.css +0 -34
  59. data/app/controllers/concerns/masks/controller.rb +0 -114
  60. data/app/controllers/masks/actors_controller.rb +0 -15
  61. data/app/controllers/masks/application_controller.rb +0 -35
  62. data/app/controllers/masks/backup_codes_controller.rb +0 -34
  63. data/app/controllers/masks/debug_controller.rb +0 -9
  64. data/app/controllers/masks/devices_controller.rb +0 -20
  65. data/app/controllers/masks/emails_controller.rb +0 -60
  66. data/app/controllers/masks/error_controller.rb +0 -14
  67. data/app/controllers/masks/keys_controller.rb +0 -45
  68. data/app/controllers/masks/manage/actors_controller.rb +0 -83
  69. data/app/controllers/masks/manage/base_controller.rb +0 -20
  70. data/app/controllers/masks/manage/clients_controller.rb +0 -84
  71. data/app/controllers/masks/manage/dashboard_controller.rb +0 -15
  72. data/app/controllers/masks/manage/devices_controller.rb +0 -19
  73. data/app/controllers/masks/one_time_code_controller.rb +0 -49
  74. data/app/controllers/masks/openid/authorizations_controller.rb +0 -45
  75. data/app/controllers/masks/openid/discoveries_controller.rb +0 -55
  76. data/app/controllers/masks/openid/tokens_controller.rb +0 -45
  77. data/app/controllers/masks/openid/userinfo_controller.rb +0 -28
  78. data/app/controllers/masks/passwords_controller.rb +0 -33
  79. data/app/controllers/masks/recoveries_controller.rb +0 -43
  80. data/app/controllers/masks/sessions_controller.rb +0 -53
  81. data/app/helpers/masks/application_helper.rb +0 -49
  82. data/app/jobs/masks/application_job.rb +0 -7
  83. data/app/jobs/masks/expire_actors_job.rb +0 -15
  84. data/app/jobs/masks/expire_recoveries_job.rb +0 -15
  85. data/app/mailers/masks/actor_mailer.rb +0 -22
  86. data/app/mailers/masks/application_mailer.rb +0 -15
  87. data/app/models/concerns/masks/access.rb +0 -162
  88. data/app/models/concerns/masks/actor.rb +0 -132
  89. data/app/models/concerns/masks/adapter.rb +0 -68
  90. data/app/models/concerns/masks/role.rb +0 -9
  91. data/app/models/concerns/masks/scoped.rb +0 -54
  92. data/app/models/masks/access/actor_password.rb +0 -21
  93. data/app/models/masks/access/actor_scopes.rb +0 -18
  94. data/app/models/masks/access/actor_signup.rb +0 -21
  95. data/app/models/masks/actors/anonymous.rb +0 -40
  96. data/app/models/masks/actors/system.rb +0 -24
  97. data/app/models/masks/adapters/active_record.rb +0 -85
  98. data/app/models/masks/application_model.rb +0 -15
  99. data/app/models/masks/application_record.rb +0 -8
  100. data/app/models/masks/check.rb +0 -192
  101. data/app/models/masks/credential.rb +0 -166
  102. data/app/models/masks/credentials/access_token.rb +0 -60
  103. data/app/models/masks/credentials/backup_code.rb +0 -30
  104. data/app/models/masks/credentials/device.rb +0 -59
  105. data/app/models/masks/credentials/email.rb +0 -48
  106. data/app/models/masks/credentials/factor2.rb +0 -71
  107. data/app/models/masks/credentials/key.rb +0 -38
  108. data/app/models/masks/credentials/last_login.rb +0 -12
  109. data/app/models/masks/credentials/masquerade.rb +0 -32
  110. data/app/models/masks/credentials/nickname.rb +0 -63
  111. data/app/models/masks/credentials/one_time_code.rb +0 -34
  112. data/app/models/masks/credentials/password.rb +0 -28
  113. data/app/models/masks/credentials/recovery.rb +0 -71
  114. data/app/models/masks/credentials/return_to.rb +0 -27
  115. data/app/models/masks/credentials/session.rb +0 -67
  116. data/app/models/masks/device.rb +0 -30
  117. data/app/models/masks/error.rb +0 -51
  118. data/app/models/masks/event.rb +0 -14
  119. data/app/models/masks/mask.rb +0 -266
  120. data/app/models/masks/openid/authorization.rb +0 -116
  121. data/app/models/masks/openid/token.rb +0 -56
  122. data/app/models/masks/rails/actor.rb +0 -212
  123. data/app/models/masks/rails/actor_role.rb +0 -12
  124. data/app/models/masks/rails/device.rb +0 -47
  125. data/app/models/masks/rails/email.rb +0 -96
  126. data/app/models/masks/rails/key.rb +0 -61
  127. data/app/models/masks/rails/openid/access_token.rb +0 -55
  128. data/app/models/masks/rails/openid/authorization.rb +0 -45
  129. data/app/models/masks/rails/openid/client.rb +0 -186
  130. data/app/models/masks/rails/openid/id_token.rb +0 -43
  131. data/app/models/masks/rails/recovery.rb +0 -116
  132. data/app/models/masks/rails/role.rb +0 -20
  133. data/app/models/masks/rails/scope.rb +0 -15
  134. data/app/models/masks/session.rb +0 -447
  135. data/app/models/masks/sessions/access.rb +0 -27
  136. data/app/models/masks/sessions/inline.rb +0 -16
  137. data/app/models/masks/sessions/request.rb +0 -42
  138. data/app/resources/masks/actor_resource.rb +0 -9
  139. data/app/resources/masks/session_resource.rb +0 -15
  140. data/app/views/layouts/masks/application.html.erb +0 -17
  141. data/app/views/layouts/masks/mailer.html.erb +0 -17
  142. data/app/views/layouts/masks/mailer.text.erb +0 -1
  143. data/app/views/layouts/masks/manage.html.erb +0 -42
  144. data/app/views/masks/actor_mailer/recover_credentials.html.erb +0 -32
  145. data/app/views/masks/actor_mailer/recover_credentials.text.erb +0 -1
  146. data/app/views/masks/actor_mailer/verify_email.html.erb +0 -33
  147. data/app/views/masks/actor_mailer/verify_email.text.erb +0 -8
  148. data/app/views/masks/actors/current.html.erb +0 -145
  149. data/app/views/masks/application/_header.html.erb +0 -30
  150. data/app/views/masks/backup_codes/new.html.erb +0 -117
  151. data/app/views/masks/emails/new.html.erb +0 -109
  152. data/app/views/masks/emails/verify.html.erb +0 -51
  153. data/app/views/masks/keys/new.html.erb +0 -127
  154. data/app/views/masks/manage/actors/index.html.erb +0 -104
  155. data/app/views/masks/manage/actors/show.html.erb +0 -172
  156. data/app/views/masks/manage/clients/index.html.erb +0 -102
  157. data/app/views/masks/manage/clients/show.html.erb +0 -156
  158. data/app/views/masks/manage/dashboard/index.html.erb +0 -10
  159. data/app/views/masks/manage/devices/index.html.erb +0 -47
  160. data/app/views/masks/one_time_code/new.html.erb +0 -167
  161. data/app/views/masks/openid/authorizations/error.html.erb +0 -23
  162. data/app/views/masks/openid/authorizations/new.html.erb +0 -46
  163. data/app/views/masks/passwords/edit.html.erb +0 -71
  164. data/app/views/masks/recoveries/new.html.erb +0 -69
  165. data/app/views/masks/recoveries/password.html.erb +0 -63
  166. data/app/views/masks/sessions/new.html.erb +0 -152
  167. data/config/brakeman.ignore +0 -28
  168. data/config/initializers/inflections.rb +0 -5
  169. data/config/locales/en.yml +0 -307
  170. data/db/migrate/20231205173845_create_actors.rb +0 -94
  171. data/db/migrate/20240329182422_support_openid.rb +0 -64
  172. data/lib/generators/masks/install/USAGE +0 -8
  173. data/lib/generators/masks/install/templates/initializer.rb +0 -5
  174. data/lib/generators/masks/install/templates/masks.json +0 -9
  175. data/lib/masks/configuration.rb +0 -249
  176. data/lib/masks/engine.rb +0 -25
  177. data/lib/masks/middleware.rb +0 -70
  178. data/lib/tasks/masks_tasks.rake +0 -72
  179. data/masks.json +0 -315
@@ -1,116 +0,0 @@
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
@@ -1,56 +0,0 @@
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
@@ -1,212 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Rails
5
- class Actor < ApplicationRecord
6
- include Masks::Actor
7
-
8
- self.table_name = "actors"
9
-
10
- scope :expired,
11
- lambda {
12
- where(
13
- "last_login_at < ?",
14
- Masks.configuration.lifetimes[:expired_actor]&.ago ||
15
- 6.months.ago
16
- )
17
- }
18
-
19
- has_many :saved_scopes,
20
- class_name: Masks.configuration.models[:scope],
21
- autosave: true
22
- has_many :saved_roles,
23
- class_name: Masks.configuration.models[:role],
24
- autosave: true
25
- has_many :recoveries,
26
- class_name: Masks.configuration.models[:recovery],
27
- autosave: true
28
- has_many :emails, class_name: Masks.configuration.models[:email]
29
- has_many :devices,
30
- class_name: Masks.configuration.models[:device],
31
- autosave: true
32
- has_many :keys,
33
- class_name: Masks.configuration.models[:key],
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
44
-
45
- has_secure_password
46
-
47
- attribute :signup
48
- attribute :session
49
- attribute :totp_code
50
-
51
- before_validation :reset_version, unless: :version
52
-
53
- validates :nickname, presence: true, uniqueness: { case_sensitive: false }
54
- validates :totp_secret, presence: true, if: :totp_code
55
- validates :version, presence: true
56
- validate :validates_totp, if: :totp_code
57
- validate :validates_password, if: :password
58
- validate :validates_signup
59
-
60
- before_save :regenerate_backup_codes
61
-
62
- serialize :backup_codes, coder: JSON
63
-
64
- def to_param
65
- nickname
66
- end
67
-
68
- def primary_email
69
- emails.where(verified: true).first
70
- end
71
-
72
- def email_addresses
73
- emails.pluck(:email)
74
- end
75
-
76
- def factor2?
77
- phone_number || totp_secret
78
- end
79
-
80
- def remove_factor2!
81
- self.added_totp_secret_at = nil
82
- saved_backup_codes_at
83
- self.totp_secret = nil
84
- self.backup_codes = nil
85
- save!
86
- end
87
-
88
- def logout!
89
- reset_version
90
- save!
91
- end
92
-
93
- def totp_uri
94
- (totp || random_totp).provisioning_uri(nickname)
95
- end
96
-
97
- def totp_svg(**opts)
98
- qrcode = RQRCode::QRCode.new(totp_uri)
99
- qrcode.as_svg(**opts)
100
- end
101
-
102
- def totp
103
- return unless totp_secret
104
-
105
- ROTP::TOTP.new(totp_secret, issuer: Masks.configuration.issuer)
106
- end
107
-
108
- def random_totp
109
- ROTP::TOTP.new(random_totp_secret, issuer: Masks.configuration.issuer)
110
- end
111
-
112
- def random_totp_secret
113
- @random_totp_secret ||= ROTP::Base32.random
114
- end
115
-
116
- def assign_scopes(*list)
117
- list.map { |scope| saved_scopes.find_or_initialize_by(name: scope) }
118
- end
119
-
120
- def assign_scopes!(*list)
121
- list.map { |scope| saved_scopes.find_or_create_by(name: scope) }
122
- end
123
-
124
- def remove_scopes!(*list)
125
- saved_scopes.where(name: list).destroy_all
126
- end
127
-
128
- def assign_role(record, **opts)
129
- saved_roles
130
- .find_by!(record:)
131
- .tap { |role| role.assign_attributes(**opts) }
132
- rescue ActiveRecord::RecordNotFound
133
- saved_roles.build(record:, **opts)
134
- end
135
-
136
- def assign_role!(record, **opts)
137
- assign_role(record, **opts).save!
138
- end
139
-
140
- def remove_role!(record)
141
- saved_roles.where(record:).destroy_all
142
- end
143
-
144
- def scopes
145
- @scopes ||= saved_scopes.order(created_at: :desc).pluck(:name)
146
- end
147
-
148
- def roles(record, **opts)
149
- case record
150
- when Class, String
151
- saved_roles.where(record_type: record.to_s, **opts).includes(:record)
152
- else
153
- saved_roles.where(record:, **opts)
154
- end
155
- end
156
-
157
- def mask!
158
- save # sub-classes are encouraged to override
159
- end
160
-
161
- def should_save_backup_codes?
162
- factor2? && saved_backup_codes_at.blank?
163
- end
164
-
165
- def saved_backup_codes?
166
- factor2? && saved_backup_codes_at.present?
167
- end
168
-
169
- def changed_during_mask?
170
- changed? && changes.keys != ["session"]
171
- end
172
-
173
- private
174
-
175
- def validates_totp
176
- errors.add(:totp_code, :invalid) unless totp.verify(totp_code)
177
- end
178
-
179
- def validates_password
180
- opts = Masks.configuration.dat.password&.length&.to_h
181
-
182
- return unless password && opts
183
-
184
- if opts[:min] && password.length < opts[:min]
185
- errors.add(:password, :too_short, count: opts[:min])
186
- elsif opts[:max] && password.length > opts[:max]
187
- errors.add(key, :too_long, count: opts[:max])
188
- end
189
- end
190
-
191
- def validates_signup
192
- return unless !persisted? && !Masks.configuration.signups? && signup
193
-
194
- errors.add(:signup, :disabled)
195
- end
196
-
197
- def reset_version
198
- self.version = SecureRandom.hex
199
- end
200
-
201
- def regenerate_backup_codes
202
- if factor2?
203
- self.backup_codes ||=
204
- (1..12).to_h { |_i| [SecureRandom.base58(10), true] }
205
- else
206
- self.backup_codes = nil
207
- self.saved_backup_codes_at = nil
208
- end
209
- end
210
- end
211
- end
212
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Rails
5
- class ActorRole < ApplicationRecord
6
- self.table_name = "actor_roles"
7
-
8
- belongs_to :actor, class_name: Masks.configuration.models[:actor]
9
- belongs_to :role, class_name: Masks.configuration.models[:role]
10
- end
11
- end
12
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Rails
5
- class Device < ApplicationRecord
6
- self.table_name = "devices"
7
-
8
- attribute :session
9
-
10
- belongs_to :actor, class_name: Masks.configuration.models[:actor]
11
-
12
- validates :key, presence: true, uniqueness: { scope: :actor_id }
13
- validates :known?, presence: true
14
-
15
- after_initialize :reset_version, unless: :version
16
- before_validation :copy_session, if: :session
17
-
18
- def known?
19
- session.device&.known?
20
- end
21
-
22
- def session_key
23
- Digest::SHA512.hexdigest([key, version].join("-"))
24
- end
25
-
26
- def reset_version
27
- self.version = SecureRandom.hex
28
- end
29
-
30
- delegate :name, :device_type, :device_name, :os_name, to: :detected
31
-
32
- private
33
-
34
- def detected
35
- @detected ||= DeviceDetector.new(user_agent || session&.user_agent)
36
- end
37
-
38
- def copy_session
39
- return unless known?
40
-
41
- self.user_agent ||= session.user_agent
42
- self.ip_address ||= session.ip_address
43
- self.fingerprint ||= session.fingerprint
44
- end
45
- end
46
- end
47
- end
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Rails
5
- class Email < ApplicationRecord
6
- self.table_name = "emails"
7
-
8
- belongs_to :actor, class_name: Masks.configuration.models[:actor]
9
-
10
- validates :token, presence: true, uniqueness: true
11
- validates :email, presence: true, email: true
12
- validates :expired?, absence: true
13
-
14
- validate :validates_verified_email
15
-
16
- after_initialize :generate_token
17
- before_validation :downcase_email
18
-
19
- def notify!(session)
20
- return if verified?
21
-
22
- if expired? && !verified?
23
- self.notified_at = nil
24
- self.token = nil
25
-
26
- generate_token
27
- end
28
-
29
- return if notified?
30
-
31
- self.notified_at = Time.current
32
-
33
- return unless valid?
34
-
35
- save
36
- ActorMailer.with(session:, email: self).verify_email.deliver_now
37
- end
38
-
39
- def verify!
40
- return if verified?
41
-
42
- self.verified_at = Time.current
43
- self.verified = true
44
- save!
45
- end
46
-
47
- def to_param
48
- token
49
- end
50
-
51
- def notified?
52
- notified_at.present?
53
- end
54
-
55
- def expired?
56
- return false unless notified?
57
-
58
- notified_at <=
59
- (Masks.configuration.lifetimes[:verification_email] || 1.hour).ago
60
- end
61
-
62
- def taken?
63
- self.class.where(email:, verified: true).any?
64
- end
65
-
66
- private
67
-
68
- def validates_verified_email
69
- # not sure how to check this with uniqueness conditions,
70
- # so using a custom validation
71
- query =
72
- self.class.where(
73
- [
74
- persisted? ? "id != :id AND (" : "(",
75
- "(email = :email AND verified) OR",
76
- "(actor_id = :actor_id AND email = :email)",
77
- ")"
78
- ].compact.join(" "),
79
- actor_id: actor.id,
80
- email: email.downcase,
81
- id:
82
- )
83
-
84
- errors.add(:email, :taken) if query.any?
85
- end
86
-
87
- def generate_token
88
- self.token ||= SecureRandom.hex
89
- end
90
-
91
- def downcase_email
92
- self.email = email&.downcase
93
- end
94
- end
95
- end
96
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Rails
5
- class Key < ApplicationRecord
6
- include Masks::Scoped
7
-
8
- self.table_name = "keys"
9
-
10
- class << self
11
- def sha(secret)
12
- Digest::SHA512.hexdigest(secret)
13
- end
14
- end
15
-
16
- scope :latest, -> { order(created_at: :desc) }
17
-
18
- attribute :session
19
- attribute :secret
20
-
21
- serialize :scopes, coder: JSON
22
-
23
- belongs_to :actor, class_name: Masks.configuration.models[:actor]
24
-
25
- after_initialize :generate_hash
26
-
27
- validates :name, presence: true, length: { maximum: 32 }
28
- validates :sha, presence: true, uniqueness: true
29
-
30
- def nickname
31
- [name.parameterize, sha.slice(0...32)].join("-")
32
- end
33
-
34
- alias slug nickname
35
-
36
- def scopes
37
- value = self[:scopes]
38
-
39
- return [] unless value
40
-
41
- value & (actor&.scopes || [])
42
- end
43
-
44
- def roles_for(_record, **_opts)
45
- []
46
- end
47
-
48
- private
49
-
50
- def generate_secret
51
- self.secret ||= SecureRandom.uuid
52
- end
53
-
54
- def generate_hash
55
- self.secret ||= SecureRandom.uuid
56
-
57
- self.sha = self.class.sha(secret) if self.secret
58
- end
59
- end
60
- end
61
- end
@@ -1,55 +0,0 @@
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
@@ -1,45 +0,0 @@
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