masks 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/LICENSE +21 -0
  4. data/README.md +235 -29
  5. data/app/controllers/masks/rails/avatars_controller.rb +51 -0
  6. data/app/controllers/masks/rails/base_controller.rb +13 -0
  7. data/app/controllers/masks/rails/handshakes_controller.rb +86 -0
  8. data/app/controllers/masks/rails/sessions_controller.rb +118 -0
  9. data/app/views/layouts/masks/rails/plain.html.erb +47 -0
  10. data/app/views/masks/rails/handshakes/refused.html.erb +12 -0
  11. data/app/views/masks/rails/handshakes/show.html.erb +40 -0
  12. data/config/routes.rb +11 -82
  13. data/lib/generators/masks/install/install_generator.rb +35 -22
  14. data/lib/generators/masks/install/templates/masks.rb.tt +38 -0
  15. data/lib/masks/client/claims.rb +144 -0
  16. data/lib/masks/client/errors.rb +60 -0
  17. data/lib/masks/client/handshake.rb +104 -0
  18. data/lib/masks/client/http.rb +96 -0
  19. data/lib/masks/client/introspection.rb +29 -0
  20. data/lib/masks/client/issuer.rb +92 -0
  21. data/lib/masks/client/pkce.rb +27 -0
  22. data/lib/masks/client/rack.rb +62 -0
  23. data/lib/masks/client/registration.rb +115 -0
  24. data/lib/masks/client/registry.rb +25 -0
  25. data/lib/masks/client/resource.rb +92 -0
  26. data/lib/masks/client/session.rb +146 -0
  27. data/lib/masks/client/stores.rb +40 -0
  28. data/lib/masks/client/tokens.rb +70 -0
  29. data/lib/masks/client/tracker.rb +122 -0
  30. data/lib/masks/client/verifier.rb +42 -0
  31. data/lib/masks/client.rb +51 -0
  32. data/lib/masks/rails/authentication.rb +260 -0
  33. data/lib/masks/rails/configurable.rb +11 -0
  34. data/lib/masks/rails/configuration.rb +184 -0
  35. data/lib/masks/rails/credentials.rb +60 -0
  36. data/lib/masks/rails/engine.rb +20 -0
  37. data/lib/masks/rails/protected_resource.rb +51 -0
  38. data/lib/masks/rails.rb +10 -0
  39. data/lib/masks/version.rb +1 -3
  40. data/lib/masks.rb +3 -183
  41. metadata +48 -416
  42. data/MIT-LICENSE +0 -20
  43. data/Rakefile +0 -11
  44. data/app/assets/builds/masks/application.css +0 -1
  45. data/app/assets/builds/masks/application.js +0 -8549
  46. data/app/assets/builds/masks/application.js.map +0 -7
  47. data/app/assets/images/masks.png +0 -0
  48. data/app/assets/javascripts/application.js +0 -2
  49. data/app/assets/javascripts/controllers/application.js +0 -9
  50. data/app/assets/javascripts/controllers/emails_controller.js +0 -28
  51. data/app/assets/javascripts/controllers/index.js +0 -21
  52. data/app/assets/javascripts/controllers/keys_controller.js +0 -20
  53. data/app/assets/javascripts/controllers/recover_controller.js +0 -21
  54. data/app/assets/javascripts/controllers/recover_password_controller.js +0 -21
  55. data/app/assets/javascripts/controllers/session_controller.js +0 -94
  56. data/app/assets/javascripts/controllers/table_controller.js +0 -15
  57. data/app/assets/manifest.js +0 -2
  58. data/app/assets/masks_manifest.js +0 -2
  59. data/app/assets/stylesheets/application.css +0 -34
  60. data/app/controllers/concerns/masks/controller.rb +0 -114
  61. data/app/controllers/masks/actors_controller.rb +0 -15
  62. data/app/controllers/masks/application_controller.rb +0 -35
  63. data/app/controllers/masks/backup_codes_controller.rb +0 -34
  64. data/app/controllers/masks/debug_controller.rb +0 -9
  65. data/app/controllers/masks/devices_controller.rb +0 -20
  66. data/app/controllers/masks/emails_controller.rb +0 -60
  67. data/app/controllers/masks/error_controller.rb +0 -14
  68. data/app/controllers/masks/keys_controller.rb +0 -45
  69. data/app/controllers/masks/manage/actors_controller.rb +0 -83
  70. data/app/controllers/masks/manage/base_controller.rb +0 -20
  71. data/app/controllers/masks/manage/clients_controller.rb +0 -84
  72. data/app/controllers/masks/manage/dashboard_controller.rb +0 -15
  73. data/app/controllers/masks/manage/devices_controller.rb +0 -19
  74. data/app/controllers/masks/one_time_code_controller.rb +0 -49
  75. data/app/controllers/masks/openid/authorizations_controller.rb +0 -45
  76. data/app/controllers/masks/openid/discoveries_controller.rb +0 -55
  77. data/app/controllers/masks/openid/tokens_controller.rb +0 -45
  78. data/app/controllers/masks/openid/userinfo_controller.rb +0 -28
  79. data/app/controllers/masks/passwords_controller.rb +0 -33
  80. data/app/controllers/masks/recoveries_controller.rb +0 -43
  81. data/app/controllers/masks/sessions_controller.rb +0 -53
  82. data/app/helpers/masks/application_helper.rb +0 -49
  83. data/app/jobs/masks/application_job.rb +0 -7
  84. data/app/jobs/masks/expire_actors_job.rb +0 -15
  85. data/app/jobs/masks/expire_recoveries_job.rb +0 -15
  86. data/app/mailers/masks/actor_mailer.rb +0 -22
  87. data/app/mailers/masks/application_mailer.rb +0 -15
  88. data/app/models/concerns/masks/access.rb +0 -162
  89. data/app/models/concerns/masks/actor.rb +0 -132
  90. data/app/models/concerns/masks/adapter.rb +0 -68
  91. data/app/models/concerns/masks/role.rb +0 -9
  92. data/app/models/concerns/masks/scoped.rb +0 -54
  93. data/app/models/masks/access/actor_password.rb +0 -21
  94. data/app/models/masks/access/actor_scopes.rb +0 -18
  95. data/app/models/masks/access/actor_signup.rb +0 -21
  96. data/app/models/masks/actors/anonymous.rb +0 -40
  97. data/app/models/masks/actors/system.rb +0 -24
  98. data/app/models/masks/adapters/active_record.rb +0 -85
  99. data/app/models/masks/application_model.rb +0 -15
  100. data/app/models/masks/application_record.rb +0 -8
  101. data/app/models/masks/check.rb +0 -192
  102. data/app/models/masks/credential.rb +0 -166
  103. data/app/models/masks/credentials/access_token.rb +0 -60
  104. data/app/models/masks/credentials/backup_code.rb +0 -30
  105. data/app/models/masks/credentials/device.rb +0 -59
  106. data/app/models/masks/credentials/email.rb +0 -48
  107. data/app/models/masks/credentials/factor2.rb +0 -71
  108. data/app/models/masks/credentials/key.rb +0 -38
  109. data/app/models/masks/credentials/last_login.rb +0 -12
  110. data/app/models/masks/credentials/masquerade.rb +0 -32
  111. data/app/models/masks/credentials/nickname.rb +0 -63
  112. data/app/models/masks/credentials/one_time_code.rb +0 -34
  113. data/app/models/masks/credentials/password.rb +0 -28
  114. data/app/models/masks/credentials/recovery.rb +0 -71
  115. data/app/models/masks/credentials/return_to.rb +0 -27
  116. data/app/models/masks/credentials/session.rb +0 -67
  117. data/app/models/masks/device.rb +0 -30
  118. data/app/models/masks/error.rb +0 -51
  119. data/app/models/masks/event.rb +0 -14
  120. data/app/models/masks/mask.rb +0 -266
  121. data/app/models/masks/openid/authorization.rb +0 -116
  122. data/app/models/masks/openid/token.rb +0 -56
  123. data/app/models/masks/rails/actor.rb +0 -212
  124. data/app/models/masks/rails/actor_role.rb +0 -12
  125. data/app/models/masks/rails/device.rb +0 -47
  126. data/app/models/masks/rails/email.rb +0 -96
  127. data/app/models/masks/rails/key.rb +0 -61
  128. data/app/models/masks/rails/openid/access_token.rb +0 -55
  129. data/app/models/masks/rails/openid/authorization.rb +0 -45
  130. data/app/models/masks/rails/openid/client.rb +0 -186
  131. data/app/models/masks/rails/openid/id_token.rb +0 -43
  132. data/app/models/masks/rails/recovery.rb +0 -116
  133. data/app/models/masks/rails/role.rb +0 -20
  134. data/app/models/masks/rails/scope.rb +0 -15
  135. data/app/models/masks/session.rb +0 -447
  136. data/app/models/masks/sessions/access.rb +0 -27
  137. data/app/models/masks/sessions/inline.rb +0 -16
  138. data/app/models/masks/sessions/request.rb +0 -42
  139. data/app/resources/masks/actor_resource.rb +0 -9
  140. data/app/resources/masks/session_resource.rb +0 -15
  141. data/app/views/layouts/masks/application.html.erb +0 -17
  142. data/app/views/layouts/masks/mailer.html.erb +0 -17
  143. data/app/views/layouts/masks/mailer.text.erb +0 -1
  144. data/app/views/layouts/masks/manage.html.erb +0 -42
  145. data/app/views/masks/actor_mailer/recover_credentials.html.erb +0 -32
  146. data/app/views/masks/actor_mailer/recover_credentials.text.erb +0 -1
  147. data/app/views/masks/actor_mailer/verify_email.html.erb +0 -33
  148. data/app/views/masks/actor_mailer/verify_email.text.erb +0 -8
  149. data/app/views/masks/actors/current.html.erb +0 -145
  150. data/app/views/masks/application/_header.html.erb +0 -30
  151. data/app/views/masks/backup_codes/new.html.erb +0 -117
  152. data/app/views/masks/emails/new.html.erb +0 -109
  153. data/app/views/masks/emails/verify.html.erb +0 -51
  154. data/app/views/masks/keys/new.html.erb +0 -127
  155. data/app/views/masks/manage/actors/index.html.erb +0 -104
  156. data/app/views/masks/manage/actors/show.html.erb +0 -172
  157. data/app/views/masks/manage/clients/index.html.erb +0 -102
  158. data/app/views/masks/manage/clients/show.html.erb +0 -156
  159. data/app/views/masks/manage/dashboard/index.html.erb +0 -10
  160. data/app/views/masks/manage/devices/index.html.erb +0 -47
  161. data/app/views/masks/one_time_code/new.html.erb +0 -167
  162. data/app/views/masks/openid/authorizations/error.html.erb +0 -23
  163. data/app/views/masks/openid/authorizations/new.html.erb +0 -46
  164. data/app/views/masks/passwords/edit.html.erb +0 -71
  165. data/app/views/masks/recoveries/new.html.erb +0 -69
  166. data/app/views/masks/recoveries/password.html.erb +0 -63
  167. data/app/views/masks/sessions/new.html.erb +0 -152
  168. data/config/brakeman.ignore +0 -28
  169. data/config/initializers/inflections.rb +0 -5
  170. data/config/locales/en.yml +0 -307
  171. data/db/migrate/20231205173845_create_actors.rb +0 -94
  172. data/db/migrate/20240329182422_support_openid.rb +0 -64
  173. data/lib/generators/masks/install/USAGE +0 -8
  174. data/lib/generators/masks/install/templates/initializer.rb +0 -5
  175. data/lib/generators/masks/install/templates/masks.json +0 -9
  176. data/lib/masks/configuration.rb +0 -249
  177. data/lib/masks/engine.rb +0 -25
  178. data/lib/masks/middleware.rb +0 -70
  179. data/lib/tasks/masks_tasks.rake +0 -72
  180. data/masks.json +0 -315
@@ -1,192 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- # Checks track attempts to verify one attribute of a session, like the actor
5
- # or their password.
6
- #
7
- # Every session contains a list of checks that can be manipulated while it is
8
- # masked. Credentials associated with the session are typical consumers of
9
- # checks, but direct manipulation is possible as well.
10
- #
11
- # Once a check's consumers have reported their status (approved, denied, or
12
- # skipped) it will report an overall status based on the results, either:
13
- #
14
- # - +passed?+ - true if attempts were made and all were approved, not skipped, or the check is optional
15
- # - +failed?+ - true if attempts were made and any were denied
16
- #
17
- # **Note**: Checks can exist in a middle state, neither passed or failed, in
18
- # the case that no attempts were made.
19
- #
20
- # @see Masks::Credential Masks::Credential
21
- class Check < ApplicationModel
22
- attribute :key
23
- attribute :lifetime
24
- attribute :optional, default: false
25
- attribute :attempted, default: -> { {} }
26
- attribute :approved
27
- attribute :skipped
28
- attribute :denied
29
-
30
- # Returns a hash of attempts for the check.
31
- #
32
- # Each key in the hash is the name of a specific attemptee, like a class or
33
- # credential. The value is a hash of data about the attempt (like when it was
34
- # attempted, approved, denied, and/or skipped).
35
- #
36
- # @return [Hash]
37
- def attempts
38
- attempted.deep_merge(@attempts || {}).deep_stringify_keys
39
- end
40
-
41
- # Whether or not the check is optional.
42
- #
43
- # Optional checks always return +true+ for +passed?+.
44
- #
45
- # @return Boolean
46
- def optional?
47
- optional
48
- end
49
-
50
- # Whether or not the check passed.
51
- #
52
- # +true+ if attempts were made and all were approved, not skipped, or the check is optional
53
- #
54
- # @return Boolean
55
- def passed?
56
- return true if optional? && !failed?
57
- return false if attempts.keys.empty?
58
-
59
- attempts.all? do |id, _opts|
60
- attempt_approved?(id) || attempt_skipped?(id)
61
- end
62
- end
63
-
64
- # Returns true if a specific attempt was approved.
65
- # @param [String] id
66
- # @return Boolean
67
- def attempt_approved?(id)
68
- opts = attempts.fetch(id.to_s, {})
69
-
70
- return approved unless lifetime
71
- return false if opts["skipped_at"]
72
-
73
- time =
74
- case opts["approved_at"]
75
- when nil
76
- return false
77
- when String
78
- Time.try(:parse, opts["approved_at"])
79
- else
80
- time
81
- end
82
-
83
- if time
84
- time + ActiveSupport::Duration.parse(lifetime) > Time.current
85
- else
86
- false
87
- end
88
- end
89
-
90
- # Returns true if a specific attempt was skipped.
91
- # @param [String] id
92
- # @return Boolean
93
- def attempt_skipped?(id)
94
- opts = attempts.fetch(id.to_s, {})
95
- opts["skipped_at"] && optional
96
- end
97
-
98
- # Approves an attempt.
99
- #
100
- # Additional metadata can be passed as keyword arguments, and it will be
101
- # saved alongside the attempt data.
102
- #
103
- # @param [String] id
104
- # @param [Hash] opts
105
- # @return Boolean
106
- def approve!(id, **opts)
107
- self.approved = true
108
-
109
- merge_attempt(
110
- id,
111
- opts.merge(approved_at: Time.current.iso8601, skipped_at: nil)
112
- )
113
- end
114
-
115
- # Skips an attempt. Skips count as approvals.
116
- #
117
- # Additional metadata can be passed as keyword arguments, and it will be
118
- # saved alongside the attempt data.
119
- #
120
- # @param [String] id
121
- # @param [Hash] opts
122
- # @return Boolean
123
- def skip!(id, **opts)
124
- self.skipped = true
125
-
126
- merge_attempt(
127
- id,
128
- opts.merge(approved_at: nil, skipped_at: Time.current.iso8601)
129
- )
130
- end
131
-
132
- # Denies an attempt.
133
- #
134
- # Additional metadata can be passed as keyword arguments, and it will be
135
- # saved alongside the attempt data.
136
- #
137
- # @param [String] id
138
- # @param [Hash] opts
139
- # @return Boolean
140
- def deny!(id, **opts)
141
- self.denied = true
142
-
143
- merge_attempt(id, opts.merge(approved_at: nil, skipped_at: nil))
144
- end
145
-
146
- # Returns the time the check passed, if it did.
147
- # @return [Datetime]
148
- def passed_at
149
- return unless passed?
150
-
151
- attempts
152
- .map do |_id, opts|
153
- time = opts["approved_at"] || opts["skipped_at"]
154
- Time.try(:parse, time) if time
155
- end
156
- .compact
157
- .max
158
- end
159
-
160
- # Clears all data for attempts by the given +id+.
161
- # @param [String] id
162
- # @return [Datetime]
163
- def clear!(id)
164
- @attempts&.except!(id)
165
- attempted.except!(id)
166
- end
167
-
168
- # Returns a version of the check intended for the rails session.
169
- # @return [Hash]
170
- def to_session
171
- return { optional:, attempted: } unless lifetime
172
-
173
- { optional:, attempted: attempts }
174
- end
175
-
176
- private
177
-
178
- def failed?
179
- return false if attempts.keys.empty?
180
-
181
- attempts.any? do |id, _opts|
182
- !attempt_approved?(id) && !attempt_skipped?(id)
183
- end
184
- end
185
-
186
- def merge_attempt(id, data)
187
- @attempts ||= {}
188
- @attempts[id] ||= {}
189
- @attempts[id].deep_merge!(data.deep_stringify_keys)
190
- end
191
- end
192
- end
@@ -1,166 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- # A base class for credentials, which identify actors and check their access.
5
- #
6
- # When a session is masked, a set of credentials are given the chance to
7
- # inspect the session parameters, propose an actor, and approve or deny
8
- # their access.
9
- #
10
- # There are a few lifecycle methods available to credentials:
11
- #
12
- # - +lookup+ - should return an identified actor if found
13
- # - +maskup+ - validates the session, actor, and any other data
14
- # - +backup+ - records the status of the credential's check(s), if necessary
15
- # - +cleanup+ - deletes any recorded data for the credential
16
- #
17
- # Sessions expect credentials to use checks to record their results, so there
18
- # are helper methods to approve, deny, or skip associated checks—+approve!+,
19
- # +deny!+, and +skip!+ respectively.
20
- #
21
- # @see Masks::Check Masks::Check
22
- # @see Masks::Credentials Masks::Credentials
23
- class Credential < ApplicationModel
24
- class << self
25
- def checks(value = nil)
26
- @checks ||= {}
27
- @checks[self.class.name] = value.to_s if value
28
- @checks[self.class.name]
29
- end
30
- end
31
-
32
- attribute :session
33
- attribute :masked_at
34
- attribute :passed_at
35
-
36
- delegate :config,
37
- :actor,
38
- :session_params,
39
- :account_params,
40
- :params,
41
- :writable?,
42
- to: :session
43
-
44
- # return an actor if it's found and valid
45
- def lookup
46
- nil
47
- end
48
-
49
- def mask!
50
- before_mask
51
-
52
- # existing checks (found from the session) can be
53
- # skipped when already present and not expired
54
- return if check&.passed? && check.attempts[slug] && valid?
55
-
56
- self.masked_at = Time.current
57
-
58
- maskup
59
- end
60
-
61
- # verify the session and actor
62
- def maskup
63
- nil
64
- end
65
-
66
- def backup!
67
- self.passed_at = Time.current if check&.passed? &&
68
- check&.attempt_approved?(slug)
69
-
70
- backup
71
- end
72
-
73
- # write any data after all credentials/checks have run
74
- def backup
75
- nil # but overridable
76
- end
77
-
78
- # cleanup data re: the mask
79
- def cleanup
80
- nil # but overridable
81
- end
82
-
83
- def cleanup!
84
- cleanup
85
- reset!
86
- end
87
-
88
- delegate :optional?,
89
- :passed?,
90
- :skipped?,
91
- :invalidated?,
92
- to: :check,
93
- allow_nil: true
94
-
95
- def slug
96
- self.class.name.split("::").join("_").underscore
97
- end
98
-
99
- def name
100
- I18n.t("auth.credentials.#{slug}.name")
101
- end
102
-
103
- def check
104
- session&.find_check(self.class.checks)
105
- end
106
-
107
- def patch_params
108
- session&.account_params&.fetch(slug, {})
109
- end
110
-
111
- private
112
-
113
- def before_mask
114
- nil
115
- end
116
-
117
- def approve!(**opts)
118
- check&.approve!(slug, **opts)
119
- end
120
-
121
- def deny!(**opts)
122
- check&.deny!(slug, **opts)
123
- end
124
-
125
- def skip!(**opts)
126
- check&.skip!(slug, **opts)
127
- end
128
-
129
- def reset!
130
- check&.clear!(slug)
131
- end
132
-
133
- def nickname_config
134
- session.config.dat.nickname
135
- end
136
-
137
- def nickname_format
138
- return unless nickname_config.format
139
-
140
- Regexp.new(nickname_config.format)
141
- end
142
-
143
- def prefix_nickname(value, default: nil)
144
- prefix = nickname_config&.prefix
145
-
146
- return default unless value.present?
147
-
148
- prefixed = value
149
- prefixed = "#{prefix}#{value}" if prefix && !value.start_with?(prefix)
150
-
151
- return default if nickname_format && !nickname_format.match?(prefixed)
152
-
153
- prefixed
154
- end
155
-
156
- def validates_length(key, opts)
157
- return unless opts
158
-
159
- if opts[:min] && send(key).length < opts[:min]
160
- errors.add(key, :too_short, count: opts[:min])
161
- elsif opts[:max] && send(key).length > opts[:max]
162
- errors.add(key, :too_long, count: opts[:max])
163
- end
164
- end
165
- end
166
- end
@@ -1,60 +0,0 @@
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
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Credentials
5
- # Checks :factor2 for a valid backup code.
6
- class BackupCode < Masks::Credential
7
- include Factor2
8
-
9
- private
10
-
11
- def param
12
- :backup_code
13
- end
14
-
15
- def secret
16
- actor&.backup_codes if actor&.saved_backup_codes?
17
- end
18
-
19
- def verify(code)
20
- code if secret&.fetch(code, false)
21
- end
22
-
23
- def backup
24
- return unless verified?
25
-
26
- actor.update_attribute("backup_codes", secret.merge(code => false))
27
- end
28
- end
29
- end
30
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Credentials
5
- # Checks for a known, valid :device.
6
- #
7
- # If the device is not associated with the session's actor, it will be.
8
- # Identification is based on the +user_agent+ and a few other facets.
9
- class Device < Masks::Credential
10
- checks :device
11
-
12
- def lookup
13
- return unless actor
14
-
15
- device = config.find_device(session, actor:)
16
-
17
- session.extras(device:)
18
-
19
- nil
20
- end
21
-
22
- def maskup
23
- device = session.extra(:device)
24
-
25
- return deny! unless device
26
-
27
- session_key = session.data[:device_key]
28
-
29
- # ensure devices match across sessions, which would only happen if a
30
- # session cookie happened is shared across machines. this destroys
31
- # the entire session and cleans up everything involved.
32
- if session_key && device.session_key != session_key
33
- raise "invalid device"
34
- end
35
-
36
- # store devices that are found in a database of known devices
37
- if device.known?
38
- session.data[:device_key] = device.session_key
39
- actor.devices << device
40
- approve!
41
- else
42
- cleanup
43
- deny!
44
- end
45
- end
46
-
47
- def backup
48
- session.extra(:device)&.touch(:accessed_at) if session&.passed?
49
- end
50
-
51
- def cleanup
52
- device = session.extra(:device)
53
- device&.reset_version
54
-
55
- session.data[:device_key] = nil
56
- end
57
- end
58
- end
59
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Credentials
5
- # Checks for an :actor given a matching email.
6
- class Email < Masks::Credential
7
- checks :actor
8
-
9
- def lookup
10
- return if actor || !email
11
-
12
- actor = config.find_actor(session, email:)
13
- actor ||=
14
- config.build_actor(
15
- session,
16
- email:,
17
- nickname: generate_nickname(email)
18
- )
19
- actor.signup = true
20
- actor
21
- end
22
-
23
- def maskup
24
- approve! if actor && email && valid?
25
- end
26
-
27
- private
28
-
29
- def email
30
- @email ||=
31
- [session_params[:email], session_params[:nickname]].find do |param|
32
- ValidateEmail.valid?(param)
33
- end
34
- end
35
-
36
- def generate_nickname(email)
37
- return email if !nickname_format || nickname_format.match?(email)
38
-
39
- parts = email.split("@")
40
- name = parts[0].gsub(/[^\w\d]/, "")
41
-
42
- prefix_nickname(
43
- "#{name.downcase.slice(0, 16)}#{SecureRandom.hex.slice(0, 8)}"
44
- )
45
- end
46
- end
47
- end
48
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Credentials
5
- # Base class for factor2 credentials.
6
- module Factor2
7
- extend ActiveSupport::Concern
8
-
9
- included do
10
- validates :secret, presence: true, if: :enabled?
11
- validates :code, presence: { allow_nil: true }
12
-
13
- attribute :code
14
-
15
- checks :factor2
16
- end
17
-
18
- def lookup
19
- # nothing to do here
20
- end
21
-
22
- def maskup
23
- check.optional = false if actor&.factor2?
24
-
25
- return unless enabled?
26
-
27
- code_param = session_params&.fetch(param, nil)&.presence
28
-
29
- self.code = verify(code_param) if code_param
30
-
31
- if code
32
- approve!
33
- elsif code_param
34
- deny!
35
- end
36
- end
37
-
38
- def verified?
39
- code
40
- end
41
-
42
- def enabled?
43
- secret.present?
44
- end
45
-
46
- def param
47
- raise NotImplementedError
48
- end
49
-
50
- def secret
51
- raise NotImplementedError
52
- end
53
-
54
- def enable(code, secret:)
55
- raise NotImplementedError
56
- end
57
-
58
- def verify(code)
59
- raise NotImplementedError
60
- end
61
-
62
- def verify_on_enable?
63
- false
64
- end
65
-
66
- def generate_secret
67
- nil
68
- end
69
- end
70
- end
71
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Credentials
5
- # Checks :key given a valid Authorization header.
6
- class Key < Masks::Credential
7
- checks :key
8
-
9
- attribute :accessed
10
-
11
- def lookup
12
- secret = session.request.authorization&.split&.last
13
- key = session.config.find_key(session, secret:)
14
-
15
- return unless key
16
-
17
- session.extras(key:)
18
- session.scoped = key
19
- self.accessed = true
20
- key.actor
21
- end
22
-
23
- def maskup
24
- key = session.extra(:key)
25
-
26
- if key&.actor == session&.actor && session.scoped == key
27
- approve!
28
- else
29
- deny!
30
- end
31
- end
32
-
33
- def backup
34
- session.extra(:key).touch(:accessed_at) if session&.passed? && accessed
35
- end
36
- end
37
- end
38
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Credentials
5
- # As of now, simply keeps track of +last_login+ times on the actor.
6
- class LastLogin < Masks::Credential
7
- def backup
8
- actor&.touch(:last_login_at) if session&.passed?
9
- end
10
- end
11
- end
12
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Masks
4
- module Credentials
5
- # Checks for an :actor to masquerade as.
6
- class Masquerade < Masks::Credential
7
- checks :actor
8
-
9
- def lookup
10
- return if session.actor
11
-
12
- value = session.data[:as]
13
-
14
- @loaded =
15
- case value
16
- when Masks::ANON
17
- Actors::Anonymous.new(session:) if session.mask&.allow_anonymous?
18
- when session.mask.actor_scope
19
- value
20
- when ValidateEmail.valid?(value)
21
- config.find_actor(session, email: value)
22
- when String
23
- config.find_actor(session, nickname: prefix_nickname(value))
24
- end
25
- end
26
-
27
- def maskup
28
- approve! if @loaded
29
- end
30
- end
31
- end
32
- end