yiffspace-auth 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dad90f17a6b3bfc0a2a459d13e29709018c1f342190403c8fabba52908ecfb21
4
- data.tar.gz: 7e7f5c43f3bb224d80e496d1b93d7873e89bcd53ba8a383c4fdd6b4e1f2d2f19
3
+ metadata.gz: 797ac14850c6fb1331452f93c0f751e9096ea74aa16d9ed488f1e32acfb17da1
4
+ data.tar.gz: 0c2cbc7304003f8ff54cfe0ccf236e89fb16ff89fa801e7537a931bca70cf2d9
5
5
  SHA512:
6
- metadata.gz: 6c7f74d61e15c0aa03222153dccc65e9d6c9cd61fee05f5bb33b06cddb3eff0e7628e12ff6419b1948767892bd9bf3c0b5c7703f2df7d7f5e0a4441eb9bfb012
7
- data.tar.gz: f8b2aeea3b6f655bf4a3d89ad859edfa62958147a550b8cb1a40d9b83481a8e686780afbc8ef7dfe3975f459c619c5d8a9d07b6cb89b1eaa325cd665882b4aca
6
+ metadata.gz: e81232dbb0d624cf5982b96d80ad454fef794f5fd3b7157bb056807265890e97c27bc9f63ecb2ef2cae784fbb9af1b62c752d6f02c8ad393438340b82acf3aa8
7
+ data.tar.gz: c97f9aca4937066941ddd271041afa13725854cd605b938bb070c748189d360645aa863c7f7ea13d9b4ae059239b117408694f64234efb5cac5e853d5dcfbf04
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## 0.0.5
2
+
3
+ - Add auth spoofing: `YiffSpace::Auth.enable_spoof_auth!`/`disable_spoof_auth!`
4
+ toggle a global switch, and setting `spoof_user_id` (plus optional
5
+ `spoof_permissions`/`spoof_roles`) on a `Client` makes every request through
6
+ that client log in as that user via `Helper#auth`/`#user`, without touching
7
+ the real session. Intended for local development - the gem doesn't gate this
8
+ by environment itself, so only call `enable_spoof_auth!` from somewhere the
9
+ host app already restricts to development (see `enable_debug_action!`).
10
+ Controllers can override this per-request: the `disable_spoof_auth`/
11
+ `override_spoof_auth` class macros (and the underlying `spoof_override=`
12
+ setter, for values only known at request time) let a controller opt out of
13
+ spoofing entirely, or swap in its own `spoof_user_id`/`spoof_permissions`/
14
+ `spoof_roles` independent of the client's configured defaults.
15
+
1
16
  ## 0.0.1
2
17
 
3
18
  - Initial release, extracted from the `yiffspace` gem's `YiffSpace::Auth::*`
@@ -66,7 +66,7 @@ module YiffSpace
66
66
  end
67
67
 
68
68
  def mark_dirty(user_id)
69
- Rails.cache.write(format(Helper::DIRTY_FLAG_KEY, user_id), true, expires_in: DIRTY_FLAG_TTL)
69
+ Rails.cache.write(format(DIRTY_FLAG_KEY, user_id), true, expires_in: DIRTY_FLAG_TTL)
70
70
  end
71
71
  end
72
72
  end
@@ -8,7 +8,8 @@ module YiffSpace
8
8
  class Client
9
9
  attr_accessor(:client_id, :client_secret, :scopes, :resource,
10
10
  :redirect_uri, :server_url, :auth_session_key, :user_session_key,
11
- :update_discord_images, :permissions_separator, :logto_webhook_secret)
11
+ :update_discord_images, :permissions_separator, :logto_webhook_secret,
12
+ :spoof_user_id, :spoof_permissions, :spoof_roles)
12
13
 
13
14
  attr_reader(:name)
14
15
 
@@ -22,6 +23,10 @@ module YiffSpace
22
23
  @update_discord_images = true
23
24
  @permissions_separator = ":"
24
25
  @logto_webhook_secret = nil
26
+ # See YiffSpace::Auth.enable_spoof_auth! - spoofing is off for this client until spoof_user_id is set.
27
+ @spoof_user_id = nil
28
+ @spoof_permissions = []
29
+ @spoof_roles = []
25
30
  end
26
31
 
27
32
  def logto(controller)
@@ -8,17 +8,10 @@ module YiffSpace
8
8
  module Helper
9
9
  extend(ActiveSupport::Concern)
10
10
 
11
- # auth/user session values (raw Discord profile + OIDC token claims) can easily exceed a
12
- # cookie's ~4KB limit, so the session cookie itself only holds an opaque pointer - the real
13
- # payload lives in Rails.cache (already a hard dependency of this module, see
14
- # #sync_auth_if_dirty! below), keyed off that pointer.
15
- SESSION_CACHE_KEY = "yiffspace:auth:session:%s"
16
- SESSION_CACHE_TTL = 30.days
17
-
18
11
  module ClassMethods
19
12
  def set_client_name(name) # rubocop:disable Naming/AccessorMethodName
20
13
  before_action do |controller|
21
- controller.request.env["yiffspace.auth.client_name"] = name
14
+ controller.request.env[CLIENT_NAME_ENV] = name
22
15
  if controller.respond_to?(:yiffspace_client_name=)
23
16
  controller.yiffspace_client_name = name
24
17
  elsif controller.respond_to?(:client_name=)
@@ -32,6 +25,24 @@ module YiffSpace
32
25
  end
33
26
  end
34
27
  end
28
+
29
+ # Disables spoof auth for this controller (or just the actions matched by the given
30
+ # before_action options, e.g. `only:`/`except:`), regardless of the global switch or the
31
+ # client's spoof_user_id. Useful for controllers that must always see the real session
32
+ # (e.g. the auth engine's own login/callback controllers).
33
+ def disable_spoof_auth(**before_action_options)
34
+ before_action(**before_action_options) { |controller| controller.request.env[SPOOF_OVERRIDE_ENV] = false }
35
+ end
36
+
37
+ # Overrides the user/permissions/roles spoofed for this controller (or just the actions
38
+ # matched by the given before_action options), independent of the client's configured
39
+ # spoof_user_id/spoof_permissions/spoof_roles. Still requires the global switch
40
+ # (YiffSpace::Auth.enable_spoof_auth!) to be on - this only changes *who* gets spoofed in,
41
+ # not whether spoofing happens at all.
42
+ def override_spoof_auth(spoof_user_id: nil, spoof_permissions: nil, spoof_roles: nil, **before_action_options)
43
+ overrides = { spoof_user_id: spoof_user_id, spoof_permissions: spoof_permissions, spoof_roles: spoof_roles }.compact
44
+ before_action(**before_action_options) { |controller| controller.request.env[SPOOF_OVERRIDE_ENV] = overrides }
45
+ end
35
46
  end
36
47
 
37
48
  def auth_raw
@@ -39,13 +50,14 @@ module YiffSpace
39
50
  end
40
51
 
41
52
  def auth
53
+ return spoofed_auth if spoofing?
42
54
  return AuthInfo::Anonymous.instance if auth_raw.blank?
43
55
 
44
56
  AuthInfo.from_session(auth_raw)
45
57
  end
46
58
 
47
59
  def auth?
48
- auth_raw.present? && !auth.anonymous?
60
+ spoofing? || (auth_raw.present? && !auth.anonymous?)
49
61
  end
50
62
 
51
63
  def auth=(value)
@@ -62,13 +74,14 @@ module YiffSpace
62
74
  end
63
75
 
64
76
  def user
77
+ return spoofed_user if spoofing?
65
78
  return UserInfo::Anonymous.instance if user_raw.blank?
66
79
 
67
80
  UserInfo.from_session(user_raw)
68
81
  end
69
82
 
70
83
  def user?
71
- user_raw.present? && !user.anonymous?
84
+ spoofing? || (user_raw.present? && !user.anonymous?)
72
85
  end
73
86
 
74
87
  def user=(value)
@@ -99,13 +112,12 @@ module YiffSpace
99
112
  auth.permissions.has?(name)
100
113
  end
101
114
 
102
- DIRTY_FLAG_KEY = "yiffspace:auth:dirty:%s"
103
-
104
115
  # Checks the dirty flag written by the Logto webhook handler. If set, re-fetches
105
116
  # the user's current roles and permissions from the Logto Management API and
106
117
  # rewrites the session — without waiting for the access token to expire.
107
118
  # Call this as a before_action in any controller that needs instant revocation.
108
119
  def sync_auth_if_dirty!
120
+ return if spoofing?
109
121
  return unless auth?
110
122
 
111
123
  flag_key = format(DIRTY_FLAG_KEY, auth.id)
@@ -156,8 +168,62 @@ module YiffSpace
156
168
  request.env[CLIENT_NAME_ENV] = value.to_sym
157
169
  end
158
170
 
171
+ # Per-request override set via the disable_spoof_auth/override_spoof_auth class macros, or
172
+ # by calling this setter directly (e.g. from a custom before_action, for values that can
173
+ # only be computed per-request). `false` disables spoofing; a Hash overrides individual
174
+ # :spoof_user_id/:spoof_permissions/:spoof_roles values; nil (the default) defers entirely
175
+ # to the client's configuration.
176
+ def spoof_override
177
+ respond_to?(:request, true) && request.env[SPOOF_OVERRIDE_ENV]
178
+ end
179
+
180
+ def spoof_override=(value)
181
+ request.env[SPOOF_OVERRIDE_ENV] = value
182
+ end
183
+
159
184
  private
160
185
 
186
+ # True when YiffSpace::Auth.enable_spoof_auth! has been called and a spoof user id resolves
187
+ # (from the client's spoof_user_id or a controller override). Clients without one keep
188
+ # behaving normally even with the global switch on, so spoofing is always opt-in.
189
+ def spoofing?
190
+ YiffSpace::Auth.spoof_auth? && resolved_spoof_user_id.present?
191
+ end
192
+
193
+ def resolved_spoof_user_id
194
+ return nil if spoof_override == false
195
+
196
+ (spoof_override.is_a?(Hash) && spoof_override[:spoof_user_id]) || auth_client_config.spoof_user_id
197
+ end
198
+
199
+ def resolved_spoof_permissions
200
+ (spoof_override.is_a?(Hash) && spoof_override[:spoof_permissions]) || auth_client_config.spoof_permissions
201
+ end
202
+
203
+ def resolved_spoof_roles
204
+ (spoof_override.is_a?(Hash) && spoof_override[:spoof_roles]) || auth_client_config.spoof_roles
205
+ end
206
+
207
+ # Memoized per-request so repeated calls (controller + views) don't refetch the
208
+ # spoofed user from the Logto Management API on every access.
209
+ def spoofed_user
210
+ @spoofed_user ||= begin
211
+ config = auth_client_config
212
+ api_user = config.logto_management.get_or_create_user(resolved_spoof_user_id)
213
+ UserInfo.new(id: api_user.discord_id, user: api_user, discord: api_user.discord.serializable_hash)
214
+ end
215
+ end
216
+
217
+ def spoofed_auth
218
+ @spoofed_auth ||= AuthInfo.new(
219
+ id: spoofed_user.id,
220
+ token: "spoofed",
221
+ roles: resolved_spoof_roles,
222
+ permissions: resolved_spoof_permissions,
223
+ client_id: auth_client_config.client_id,
224
+ )
225
+ end
226
+
161
227
  def read_session_cache(session_key)
162
228
  token = session[session_key]
163
229
  return nil if token.blank?
@@ -2,6 +2,6 @@
2
2
 
3
3
  module YiffSpace
4
4
  module Auth
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.5"
6
6
  end
7
7
  end
@@ -19,8 +19,23 @@ module YiffSpace
19
19
  CLIENT_NAME_ENV = "yiffspace.auth.client_name"
20
20
  DEFAULT_CLIENT_NAME = :default
21
21
 
22
+ # Per-request override of spoof auth, keyed off the request env like CLIENT_NAME_ENV is - see
23
+ # Helper#spoof_override/#spoof_override= below. `false` disables spoofing outright; a Hash
24
+ # overrides individual :spoof_user_id/:spoof_permissions/:spoof_roles values.
25
+ SPOOF_OVERRIDE_ENV = "yiffspace.auth.spoof_override"
26
+
27
+ # auth/user session values (raw Discord profile + OIDC token claims) can easily exceed a
28
+ # cookie's ~4KB limit, so the session cookie itself only holds an opaque pointer - the real
29
+ # payload lives in Rails.cache (already a hard dependency of Helper, see
30
+ # Helper#sync_auth_if_dirty! below), keyed off that pointer.
31
+ SESSION_CACHE_KEY = "yiffspace:auth:session:%s"
32
+ SESSION_CACHE_TTL = 30.days
33
+
34
+ DIRTY_FLAG_KEY = "yiffspace:auth:dirty:%s"
35
+
22
36
  @clients = {}
23
37
  @enable_debug_action = false
38
+ @spoof_auth = false
24
39
 
25
40
  module_function
26
41
 
@@ -54,6 +69,23 @@ module YiffSpace
54
69
  def disable_debug_action!
55
70
  @enable_debug_action = false
56
71
  end
72
+
73
+ # Global kill switch for auth spoofing. When enabled, any client with a
74
+ # `spoof_user_id` configured logs every request in as that user instead of
75
+ # reading the real session - see Helper#auth/#user. There's no environment
76
+ # check here (mirrors enable_debug_action!) - it's on the host app to only
77
+ # ever call this from somewhere gated to development.
78
+ def spoof_auth?
79
+ @spoof_auth
80
+ end
81
+
82
+ def enable_spoof_auth!
83
+ @spoof_auth = true
84
+ end
85
+
86
+ def disable_spoof_auth!
87
+ @spoof_auth = false
88
+ end
57
89
  end
58
90
 
59
91
  class Configuration
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yiffspace-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Donovan_DMC
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-08-14 00:00:00.000000000 Z
10
+ date: 2026-08-23 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: httparty