# SecurityContextStack.java

    /*
    ** Module   : SecurityContextStack.java
    ** Abstract : Class that manages security context switches for server threads.
    **
    ** Copyright (c) 2004-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
    ** 001 NVS 20050314   @20318 Created the initial implementation of SecurityContextStack class.
    ** 002 NVS 20050427   @20912 Enhanced to provide unlimited storage for any kind of tokens
    **                           (objects) that can be associated with this context. The need is to
    **                           get rid of all sorts of per thread state information and replace it
    **                           with per context one. This overcomes the problem of arbitrary chosen
    **                           threads from the thread pool to serve elementary requests which have
    **                           share some state for the lifespan of the session.
    ** 003 NVS 20050505   @21106 Added new method getEffectiveContext() which is required to
    **                           implement dropInitialSecurityContext() in SecurityManager. Changed
    **                           the implementation of other methods to benefit from this new one.
    **                           Added a method to access the generation of the security cache this
    **                           context is currently bound to.
    ** 004 ECF 20060120   @24005 Modified {add|remove|has|get}Token methods. Replaced String token
    **                           name with an Object key.
    ** 005 NVS 20070412   @32966 Removed an excessive logging call from push() method.
    ** 006 GES 20111109          Added duplicate().
    ** 007 IAS 20160407          Fixed "system-security-editing" processing, use synthetic key as
    **                           context key.
    ** 008 GES 20200205          Added a mechanism to create a copy that doesn't share mutable state.
    ** 009 GES 20210917          Rework to better encapsulate decision caching.
    ** 010 GBB 20250403          Casting replaced with generics.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.security;

    /**
     * Security context stack is a mechanism of tracking security context switches
     * within a thread.
     */
    class SecurityContextStack
    {
       /** Opaque key for the "system-security-editing" */
       private static final ContextKey SYSTEM_SECURITY_EDITING = new ContextKey();

       /** Per-thread reference to the security context. */
       private static ThreadLocal<SecurityContextStack> contextPointer = new ThreadLocal<>();

       /** The initial context. */
       private SecurityContext initial = null;

       /** The effective context (if not <code>null</code>). */
       private SecurityContext additional = null;

       /**
        * Package private constructor.
        *
        * @param    initial
        *           The initial security context to use.
        */
       SecurityContextStack(SecurityContext initial)
       {
          this.initial = initial;
       }

       /**
        * Gets the current security context stack.
        *
        * @return <code>SecurityContextStack</code> object for the current thread
        *         or <code>null</code> if not initialized
        */
       static SecurityContextStack getContext()
       {
          return contextPointer.get();
       }

       /**
        * Sets the current security context stack.
        *
        * @param stack
        *        <code>SecurityContextStack</code> object to be set for
        *        the current thread
        */
       static void setContext(SecurityContextStack stack)
       {
          contextPointer.set(stack);
       }

       /**
        * Create a copy of this stack with shared initial and effective contexts.
        *
        * @return   The copy which has shared contexts.
        */
       SecurityContextStack duplicate()
       {
          SecurityContextStack dup = new SecurityContextStack(initial);
          dup.additional = additional;

          return dup;
       }

       /**
        * Create a copy of this stack with the initial context set from the current effective
        * context BUT with no shared data and no existing session. The intention is to provide all
        * the state needed to quickly create a new session that is related to the current session
        * but which is independent.  The copied state will include the subjects and ACLs of the
        * existing session but will exclude context-local data.
        *
        * @return   The copy which is related.
        */
       SecurityContextStack related()
       {
          return new SecurityContextStack(new SecurityContext(getEffectiveContext()));
       }

       /**
        * Gets the effective security context.
        *
        * @return <code>SecurityContext</code> object for the current thread
        */
       SecurityContext getEffectiveContext()
       {
          return (additional != null) ? additional : initial;
       }

       /**
        * Gets the current check list for the context.
        *
        * @return an array of indices into accounts that represent this security
        *         context
        */
       int[] getCheckList()
       {
          return getEffectiveContext().getCheckList();
       }

       /**
        * Gets the array of original subject IDs of this context.
        *
        * @return array of indices into accounts
        */
       int[] getIdList()
       {
          return getEffectiveContext().getIdList();
       }

       /**
        * Get the access decision cache for the context.
        *
        * @param    resource
        *           The unique index that identifies the resource type.
        *
        * @return map containing access decisions
        */
       Decision getDecisionCache(int resource)
       {
          return getEffectiveContext().getDecisionCache(resource);
       }

       /**
        * Conditionally pushes an additional security context to the stack.
        *
        * @return <code>true</code> if successful
        */
       boolean push(SecurityContext ctx)
       {
          if (additional != null || !initial.isInitial() || ctx.isInitial())
             return false;

          additional = ctx;

          return true;
       }

       /**
        * Conditionally pops an additional security context off the stack.
        *
        * @return <code>true</code> if successful
        */
       SecurityContext pop()
       {
          SecurityContext ctx = additional;
          additional = null;

          return ctx;
       }

       /**
        * Gets the context that is the session key object.
        *
        * @return security context instance or <code>null</code>
        */
       SecurityContext getSessionKey()
       {
          return additional;
       }

       /**
        * Gets the initial context.
        *
        * @return security context instance or <code>null</code>
        */
       SecurityContext getInitial()
       {
          return initial;
       }

       /**
        * Gets the batch editing status.
        *
        * @return <code>true</code> if an editing batch is open for this context
        */
       boolean isEditing()
       {
          return hasToken(SYSTEM_SECURITY_EDITING);
       }

       /**
        * Sets the batch editing status.
        *
        * @param isEditing
        *        new state of the batch editing for this context
        */
       void setEditing(boolean isEditing)
       {
          SecurityContext ctx = getEffectiveContext();

          if (isEditing)
             ctx.addToken(SYSTEM_SECURITY_EDITING, Boolean.TRUE);
          else
             ctx.removeToken(SYSTEM_SECURITY_EDITING);
       }

       /**
        * Adds a token to the context map.
        *
        * @param key
        *        token key to be used as a key in the map
        *
        * @param token
        *        an arbitrary object to be kept in the entry
        *
        * @return <code>true</code> if the token is added successfully
        */
       boolean addToken(ContextKey key, Object token)
       {
          return getEffectiveContext().addToken(key, token);
       }

       /**
        * Remove a token from the context map.
        *
        * @param key
        *        token key to be used as a key in the map
        *
        * @return <code>true</code> if the token was removed successfully
        */
       boolean removeToken(ContextKey key)
       {
          return getEffectiveContext().removeToken(key);
       }

       /**
        * Checks whether the specified token is in the context map.
        *
        * @param key
        *        token key to be used as a key in the map
        *
        * @return <code>true</code> if the token is found
        */
       boolean hasToken(ContextKey key)
       {
          return getEffectiveContext().hasToken(key);
       }

       /**
        * Gets the token from the context map.
        *
        * @param key
        *        token key to be used as a key in the map
        *
        * @return the value of the token as a generic object or <code>null</code>
        */
       Object getToken(ContextKey key)
       {
          return getEffectiveContext().getToken(key);
       }

       /**
        * Gets the security cache this context stack is bound to.
        *
        * @return <code>SecurityCache</code>
        */
       SecurityCache getCache()
       {
          return getEffectiveContext().getCache();
       }
    }