Skip to content

Instantly share code, notes, and snippets.

@bjeanes
Created November 6, 2020 06:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bjeanes/ea2701563436f3374bfc1afe3dd691d7 to your computer and use it in GitHub Desktop.
Save bjeanes/ea2701563436f3374bfc1afe3dd691d7 to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
require 'rodauth'
# Rodauth calls `clear_session` every time it updates the session. This includes when logging in. Unfortunately, this
# means that logging in as an admin will log you out as a user, and vice versa.
#
# This feature introduces a notion of a "scoped" session (inside Rodauth only) that has the following behaviours:
#
# * Adding a key to the session adds it as a sub-hash, scoped by the configuration name.
# * For an extant key, look up will return the corresponding scoped value
# * For a missing key, look up will migrate it from a prefix-scoped top-level key if there is one.
# * For other missing keys, we'll just return it but not migrate it into the scoped hash.
# * For clearing the session, we will clear our scope, as well as all top-level keys which are not owned by another
# scope session.
module Rodauth
Feature.define(:scoped_session, :ScopedSession) do
auth_value_methods :config_name
def session
SessionScoper.new(super(), config_name)
end
def clear_session
session.clear
end
class SessionScoper
def initialize(full_session, config_name)
@full_session = full_session
@legacy_prefix = "#{config_name}_"
@scope = "rodauth.#{config_name}"
end
def [](key)
scoped.fetch(key.to_s) do
# If we have a top-level value in session corresponding to the old way
# of namespacing session values, migrate and return it
if (previous = delete_legacy(key))
self[key] = previous
else
# return but don't migrate un-prefixed top-level values
@full_session[key]
end
end
end
def []=(key, value)
scoped![key.to_s] = value
end
def delete(key)
delete_legacy(key)
scoped.delete(key.to_s)
end
def has_key?(key)
scoped.fetch(key.to_s) { @full_session.has_key?(legacy_key(key)) }
end
def to_hash
scoped.to_hash
end
alias to_h to_hash
# Clears all top-level keys EXCEPT `rodauth.*` other than the current one.
def clear
scoped_session_keys = @full_session.keys.grep(/\Arodauth\./).grep_v(@scope)
preserve = @full_session.to_h.slice(*scoped_session_keys)
@full_session.clear
@full_session.merge!(preserve)
end
private
def scoped
@full_session.fetch(@scope, {})
end
def scoped!
@full_session[@scope] ||= {}
end
def delete_legacy(key)
@full_session.delete(legacy_key(key))
end
def legacy_key(key)
"#{@legacy_prefix}#{key}"
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment