Module: CamaleonCms::SessionHelper
- Included in:
- SessionRuntimeConcern
- Defined in:
- app/helpers/camaleon_cms/session_helper.rb
Instance Method Summary collapse
-
#cama_authenticate(redirect_uri = nil) ⇒ Object
check if a visitor was logged in if the user was not logged in, then redirect to login url.
-
#cama_current_role ⇒ Object
return the role for the current user if not logged in, then return 'public'.
-
#cama_current_user ⇒ Object
return the current user logged in.
-
#cama_get_session_id ⇒ Object
return the session id.
-
#cama_impersonation_parent_user ⇒ Object
The admin who started the current impersonation, resolved from the stashed parent auth token (same lookup as cama_current_user).
-
#cama_logout_user ⇒ Object
logout current user.
-
#cama_on_heroku? ⇒ Boolean
check if current host is heroku.
-
#cama_register_user(user_data, meta) ⇒ Object
User registration.
-
#cama_sign_in? ⇒ Boolean
(also: #signin?)
Check if the current user is already signed.
- #cookie_auth_token_complete? ⇒ Boolean
- #cookie_split_auth_token ⇒ Object
-
#login_user(user, remember_me = false, redirect_url = nil, rotate_session: true, allow_external: false) ⇒ Object
log in the user in to system user: User model remember_me: true/false (remember session permanently) redirect_url (default nil): after initialized the session, this will be redirected to "redirect_url" if defined it doesn't redirect if redirect_url === false return to previous page if defined the cookie, or login url received extra param: return_to=https://mysite.com allow_external (default false): follow redirect_url even when it points off-site, for a caller that has vetted the destination (e.g. an after_login hook doing SSO/payment).
-
#login_user_with_password(username, password) ⇒ Object
login a user using username and password return boolean: true => authenticated, false => authentication failed.
-
#session_back_to_parent(redirect_url = nil) ⇒ Object
switch current session into parent session called by session_switch_user after returned into parent session, this will be redirected to redirect_url or admin dashboard SECURITY: only call this once the parent admin has been re-authenticated (see Admin::SessionsController#back_to_parent and cama_impersonation_parent_user).
-
#session_switch_user(user, redirect_url = nil) ⇒ Object
switch current session user into other (user) after switched, this will be redirected to redirect_url or admin dashboard.
- #user_auth_token_from_cookie ⇒ Object
Instance Method Details
#cama_authenticate(redirect_uri = nil) ⇒ Object
check if a visitor was logged in if the user was not logged in, then redirect to login url
216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 216 def cama_authenticate(redirect_uri = nil) params[:return_to] = redirect_uri return if cama_sign_in? flash[:error] = t('camaleon_cms.admin.login.please_login') [:return_to] = if params[:return_to].present? params[:return_to] else (request.get? && params[:controller] != 'admin/sessions' ? request.original_url : nil) end redirect_to cama_admin_login_path end |
#cama_current_role ⇒ Object
return the role for the current user if not logged in, then return 'public'
184 185 186 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 184 def cama_current_role current_site.visitor_role end |
#cama_current_user ⇒ Object
return the current user logged in
189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 189 def cama_current_user # Honor an externally-set user, and memoize a nil resolution via user_resolved so a signed-out # request (or a present-but-stale auth cookie) is not re-resolved on every call (regression M16). return CurrentRequest.user if CurrentRequest.user || CurrentRequest.user_resolved CurrentRequest.user_resolved = true user = cama_calc_api_current_user # api current user... if user.nil? && user = current_site.users_include_admins.find_by(auth_token: ).try(:decorate) end CurrentRequest.user = user end |
#cama_get_session_id ⇒ Object
return the session id
230 231 232 233 234 235 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 230 def cama_get_session_id session[:autor] = 'Owen Peredo Diaz' if request.[:id].blank? id = request.[:id] id = id.public_id if id.instance_of?(::Rack::Session::SessionId) id end |
#cama_impersonation_parent_user ⇒ Object
The admin who started the current impersonation, resolved from the stashed parent auth token (same lookup as cama_current_user). Returns nil if there is no active impersonation or the stash no longer resolves to a user (e.g. the admin rotated their token by changing their password). Callers MUST re-authenticate this user before session_back_to_parent (H6 residual): the impersonated session an admin holds is indistinguishable from one they abandon, so the admin's password is the only proof that the person returning is the admin and not a later occupant.
134 135 136 137 138 139 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 134 def cama_impersonation_parent_user token = session[:parent_auth_token].to_s.split('&').first return if token.blank? current_site.users_include_admins.find_by(auth_token: token) end |
#cama_logout_user ⇒ Object
logout current user
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 154 def cama_logout_user # Security (audit 2026-08-11 M3): rotate the server-side token so a cookie copied before logout # cannot be replayed afterwards. The token is per-user, so this also ends the user's other sessions. # Audit M14: skip the rotation during impersonation -- cama_current_user is then the impersonated # user, not the admin ending the session, and rotating their token would log the innocent user # out of all their own devices. session_back_to_parent is the intended exit from impersonation. cama_current_user&.cama_reset_auth_token! if session[:parent_auth_token].blank? .delete(:auth_token, domain: :all) .delete(:auth_token, domain: nil) c_data = { value: nil, expires: 24.hours.ago } c_data[:domain] = :all if PluginRoutes.system_info['users_share_sites'].present? && CamaleonCms::Site.count > 1 [:auth_token] = c_data CurrentRequest.user = nil CurrentRequest.user_resolved = true # the cookie is gone; don't re-resolve to a stale user # Drop all server-side session state on logout (H6): a lingering impersonation parent_auth_token # must not survive to be restored into a later session on a shared browser. reset_session cama_safe_redirect(params[:return_to], cama_admin_login_path, notice: t('camaleon_cms.admin.logout.message.closed')) end |
#cama_on_heroku? ⇒ Boolean
check if current host is heroku
110 111 112 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 110 def cama_on_heroku? ENV.keys.any? { |var_name| var_name.match(/(heroku|dyno)/i) } end |
#cama_register_user(user_data, meta) ⇒ Object
User registration.
user_data must contain:
- first_name
- username
- password
- password_confirmation
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 70 def cama_register_user(user_data, ) user = current_site.users.new(user_data) # Gate on the register captcha before running any hook, so user_before_register does not fire for # attempts that fail the captcha. This diverges from user_before_login, which runs its hook even on a # failed captcha and passes the result as r[:captcha_validate]; user_before_register does neither, so # the "parity with user_before_login" below is limited to the stop_process veto. Verifying here also # consumes the single-use captcha before the hook, so a later veto burns a solved challenge — # harmless, since the re-rendered form issues a fresh one. if current_site.security_user_register_captcha_enabled? && !cama_captcha_verified? return { result: false, type: :captcha_error, message: t('camaleon_cms.admin.users.message.error_captcha'), user: user } end r = { user: user, params: params, stop_process: false } # Broadcast (hooks_run), not hook_run: hook_run(target, name, …) takes the app as its first arg, so # hook_run('user_before_register', r) treated the name as a plugin and silently no-op'd — the hook # never fired. Match the sibling user_before_login/user_after_register broadcasts. hooks_run('user_before_register', r) # A handler may veto the registration by setting r[:stop_process] (parity with user_before_login): # stop here without saving. The handler may surface its own reason (add to r[:user].errors, or # render/redirect); otherwise the controller shows a generic error. return { result: false, type: :stopped, user: user } if r[:stop_process] if user.save user.() = if current_site.need_validate_email? t('camaleon_cms.admin.users.message.created_pending_validate_email') else t('camaleon_cms.admin.users.message.created') end r = { user: user, message: , redirect_url: cama_admin_login_path } hooks_run('user_after_register', r) { result: true, message: r[:message], redirect_url: r[:redirect_url], user: user } else { result: false, type: :no_saved, user: user } end end |
#cama_sign_in? ⇒ Boolean Also known as: signin?
Check if the current user is already signed
176 177 178 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 176 def cama_sign_in? !cama_current_user.nil? end |
#cookie_auth_token_complete? ⇒ Boolean
202 203 204 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 202 def &.size == 3 end |
#cookie_split_auth_token ⇒ Object
206 207 208 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 206 def [:auth_token]&.split('&') end |
#login_user(user, remember_me = false, redirect_url = nil, rotate_session: true, allow_external: false) ⇒ Object
log in the user in to system user: User model remember_me: true/false (remember session permanently) redirect_url (default nil): after initialized the session, this will be redirected to "redirect_url" if defined it doesn't redirect if redirect_url === false return to previous page if defined the cookie, or login url received extra param: return_to=https://mysite.com allow_external (default false): follow redirect_url even when it points off-site, for a caller that has vetted the destination (e.g. an after_login hook doing SSO/payment). http(s) only; a configured allowlist (redirect_allowed_hosts option / safe_redirect_hosts hook) is honored regardless of it.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 14 def login_user(user, remember_me = false, redirect_url = nil, rotate_session: true, allow_external: false) # Rotate the session on a genuine sign-in (H6): a fresh session drops any state left behind by a # previous occupant of a shared browser — notably an impersonation parent_auth_token, which # session_back_to_parent would otherwise restore on logout, escalating the new user to admin. # Impersonation manages that token itself and passes rotate_session: false so this does not wipe it. reset_session if rotate_session # Security (audit 2026-08-11 M3): the auth cookie is HttpOnly (JavaScript/XSS cannot read the # bearer token) and Secure over an SSL request (never sent in the clear). The three components are # joined into one string so the split-based reader and the impersonation stash keep their shape. c = ([user.auth_token, request.user_agent, request.ip].join('&')) c[:expires] = 1.month.from_now if remember_me # fix to overwrite a cookie .delete(:auth_token, domain: :all) .delete(:auth_token) user.update({ last_login_at: Time.zone.now }) [:auth_token] = c # user redirection flash[:notice] = t('camaleon_cms.admin.login.message.success', locale: current_site.get_admin_language) return if redirect_url == false if redirect_url.present? # Host-check the explicit redirect_url too: after_login hooks and downstream plugins pass # caller-controlled destinations here (e.g. a return_to cookie), so it gets the same open-redirect # guard as the return_to cookie branch below. allow_external lets such a hook opt a vetted off-site # destination (SSO/payment) past the same-host rule; it stays http(s)-only either way. cama_safe_redirect(redirect_url, cama_admin_dashboard_path, allow_external: allow_external) elsif (return_to = .delete(:return_to)).present? cama_safe_redirect(return_to, cama_admin_dashboard_path) else redirect_to cama_admin_dashboard_path end end |
#login_user_with_password(username, password) ⇒ Object
login a user using username and password return boolean: true => authenticated, false => authentication failed
53 54 55 56 57 58 59 60 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 53 def login_user_with_password(username, password) # Custom class finder: usernames are stored downcased, so the lookup must # lower-case both sides regardless of DB collation. user = current_site.users.find_by_username(username) # rubocop:disable Rails/DynamicFindBy r = { user: user, params: params, password: password, captcha_validate: true } hooks_run('user_before_login', r) user&.authenticate(password) end |
#session_back_to_parent(redirect_url = nil) ⇒ Object
switch current session into parent session called by session_switch_user after returned into parent session, this will be redirected to redirect_url or admin dashboard SECURITY: only call this once the parent admin has been re-authenticated (see Admin::SessionsController#back_to_parent and cama_impersonation_parent_user).
145 146 147 148 149 150 151 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 145 def session_back_to_parent(redirect_url = nil) return unless cama_sign_in? && session[:parent_auth_token].present? [:auth_token] = (session[:parent_auth_token]) session.delete(:parent_auth_token) redirect_to (redirect_url || cama_admin_dashboard_path), notice: 'Welcome back!' end |
#session_switch_user(user, redirect_url = nil) ⇒ Object
switch current session user into other (user) after switched, this will be redirected to redirect_url or admin dashboard
116 117 118 119 120 121 122 123 124 125 126 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 116 def session_switch_user(user, redirect_url = nil) return unless cama_sign_in? # Start impersonation from a clean session (see login_user), then stash the admin's auth cookie. # The stash happens AFTER the reset and login_user is told not to rotate again, so the token # session_back_to_parent restores later survives this rotation (H6). parent_auth_token = [:auth_token] reset_session session[:parent_auth_token] = parent_auth_token login_user(user, false, redirect_url, rotate_session: false) end |
#user_auth_token_from_cookie ⇒ Object
210 211 212 |
# File 'app/helpers/camaleon_cms/session_helper.rb', line 210 def .first end |