Module: AdminPresence

Defined in:
app/lib/admin_presence.rb

Overview

Cross-subdomain "admin presence" cookie that signals "an @warmlyyours.com
admin is currently signed into CRM in this browser." Set by CRM, read by WWW
(and any other sibling app) to decide whether to render admin-only UI like
the cache-purge bar.

Why a separate cookie (and not the Devise session): CRM and WWW have
isolated session cookies on purpose — a customer XSS on www must not bleed
into crm. This cookie carries no auth power of its own; it only signals
presence so the receiving app can mount an admin UI. The receiving app's
privileged endpoints must still re-verify by calling +decode+ themselves.

Properties:

  • Encrypted with secret_key_base via +cookies.encrypted+ (tamper-proof)
  • HttpOnly + Secure (no JS access; HTTPS only outside test)
  • SameSite=Lax (blocks cross-site POSTs; allows top-level navigations)
  • Apex domain (.warmlyyours.com) so all subdomains see it
  • 1-hour TTL, refreshed on every CRM hit (slides like a session)

SECURITY: Lax cookies are still sent on same-site requests, where
"same-site" = same registrable domain. Any subdomain of warmlyyours.com
can therefore trigger requests with this cookie attached. Endpoints that
act on +decode+ MUST also enforce CSRF protection (do not rely on the
global JSON CSRF bypass), so a sibling-subdomain XSS or takeover cannot
weaponize the cookie. See Www::AdminBarController for the pattern.

Constant Summary collapse

:_hw_admin_presence
:_hw_admin_present
TTL =
1.hour
EMAIL_DOMAIN =
'@warmlyyours.com'
ALLOWED_EMAILS =

Specific @warmlyyours.com employees who get the admin bar even without the
admin role. Keep the list short — for anything broader, give them the role
in CRM. Emails are matched case-insensitively.

%w[
  jbillen@warmlyyours.com
].map(&:downcase).freeze

Class Method Summary collapse

Class Method Details

.account_eligible?(account) ⇒ Boolean

Returns:

  • (Boolean)


89
90
91
92
93
94
95
96
97
# File 'app/lib/admin_presence.rb', line 89

def ()
  return false unless 
  return false unless .respond_to?(:email) && .respond_to?(:is_admin?)

  email = .email.to_s.downcase
  return false unless email.end_with?(EMAIL_DOMAIN)

  .is_admin? || ALLOWED_EMAILS.include?(email)
end

.clear!(cookies) ⇒ Object



68
69
70
71
# File 'app/lib/admin_presence.rb', line 68

def clear!(cookies)
  cookies.delete(COOKIE_NAME, domain: cookie_domain)
  cookies.delete(HINT_COOKIE_NAME, domain: cookie_domain)
end


99
100
101
# File 'app/lib/admin_presence.rb', line 99

def cookie_domain
  ".#{TLD}"
end

.decode(cookies) ⇒ Object

Returns the symbolized payload Hash if the cookie is present, decryptable,
well-formed, and not expired; otherwise nil. Defense-in-depth: we check
the embedded :exp even though the browser also enforces cookie expiry.



76
77
78
79
80
81
82
83
84
85
86
87
# File 'app/lib/admin_presence.rb', line 76

def decode(cookies)
  raw = cookies.encrypted[COOKIE_NAME]
  return nil unless raw.is_a?(Hash)

  payload = raw.symbolize_keys
  return nil unless payload[:exp].is_a?(Integer) && payload[:exp] > Time.current.to_i
  return nil unless payload[:email].is_a?(String) && payload[:email].downcase.end_with?(EMAIL_DOMAIN)

  payload
rescue StandardError
  nil
end

.set!(cookies, account) ⇒ Object



57
58
59
60
61
62
63
64
65
66
# File 'app/lib/admin_presence.rb', line 57

def set!(cookies, )
  expires = TTL.from_now
  common = { expires: expires, domain: cookie_domain, secure: !Rails.env.test?, same_site: :lax }

  cookies.encrypted[COOKIE_NAME] = common.merge(
    value: { aid: .id, email: .email, exp: expires.to_i },
    httponly: true
  )
  cookies[HINT_COOKIE_NAME] = common.merge(value: '1', httponly: false)
end

.sync!(cookies, account) ⇒ Object

Set or clear the cookie based on whether +account+ is an eligible admin.
Safe to call with nil (clears).



49
50
51
52
53
54
55
# File 'app/lib/admin_presence.rb', line 49

def sync!(cookies, )
  if ()
    set!(cookies, )
  else
    clear!(cookies)
  end
end