Created
November 6, 2020 06:07
-
-
Save bjeanes/ea2701563436f3374bfc1afe3dd691d7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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