# SystemResource.java

    /*
    ** Module   : SystemResource.java
    ** Abstract : plugin to manage access to the "system" abstract resource
    **
    ** Copyright (c) 2005-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------Description----------------------
    ** 001 NVS 20050318   @20425 Created the initial version. This class is
    **                           responsible for the "system" abstract
    **                           resource which offers controllable access to
    **                           the "logon" instance of the resource.
    ** 002 NVS 20050411   @20678 Fixed few javadoc issues
    ** 003 NVS 20050412   @20688 Using new expressions interface that takes
    **                           the Rights instance.
    ** 004 NVS 20050414   @20708 Added rights search tracing for debugging.
    ** 005 NVS 20050524   @21274 Some methods and members are moved to base
    **                           abstract class.
    ** 006 NVS 20060412   @25496 Added new resource instances, "accounts" and
    **                           "extensions" and new check methods for them.
    ** 007 GES 20081105   @40342 Minor cleanup.
    ** 008 NVS 20090318   @41628 Added new resource instance, "admin" and
    **                           a new check method for it. This instance will
    **                           protect the admin APIs for now.
    ** 009 NVS 20090707   @43079 Added getRightsEditorName() method.
    ** 010 HC  20170612          Changes related to implementation of new GWT-based Admin client.
    ** 011 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
    */
    /*
    ** 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;

    import com.goldencode.p2j.directory.*;
    import com.goldencode.expr.*;
    import com.goldencode.p2j.util.logging.*;

    import java.util.logging.*;

    /**
     * Implements the "system" abstract resource. Instances of this resource
     * control access to various features of the P2J application. Currently
     *defined instances are:
     * <ul>
     *   <li>logon - allows logging on to the P2J server
     *   <li>context - names security context related events for audit
     *   <li>change - controls access to the batch editing feature
     *   <li>shutdown - controls access to the server shutdown
     *   <li>debug - controls access to the debug level change
     * </ul>
     */
    class SystemResource
    extends AbstractResource
    {
       /** the only access mode for "system" resources */
       static final int SYSTEM_ACCESS = 0;

       /** context related event constant */
       static final int SYSTEM_CONTEXT_CREATE = 1;

       /** context related event constant */
       static final int SYSTEM_CONTEXT_DELETE = 2;

       /** context related event constant */
       static final int SYSTEM_CONTEXT_SWITCH = 4;

       /** array of valid instance names */
       private static final String[] validInstances = new String[]
       {
          "logon",
          "context",
          "change",
          "shutdown",
          "debug",
          "accounts",
          "extensions",
          "admin",
       };

       /** Logger. */
       private static final CentralLogger LOG = CentralLogger.get(SystemResource.class);

       /**
        * Package private constructor.
        */
       SystemResource()
       {
       }

       /**
        * Returns the plugin resource type name as a string.
        *
        * @return plugin resource type name
        */
       public String getTypeName()
       {
          return "system";
       }

       /**
        * Returns an array of descriptions, one object per the plugin's access
        * rights item.
        *
        * @return array of <code>Description</code>s, one per each plugin's access
        *         rights item.
        */
       public Description[] describeRights()
       {
          Description[] items = new Description[1];
          items[0] = new Description(AttributeType.ATTR_STRING,    // string
                                     false,                        // mandatory
                                     true,                         // var.size
                                     0,                            // size
                                     "check",                      // text label
                                     "enables access if evaluates to true"
                                     );

          return items;
       }

       /**
        * Instantiates a plugin's class that implements the Rights interface,
        * using the array of objects representing a set of access rights fields.
        *
        * @param  rights
        *         an array of objects of proper types representing items in a set
        *         of access rights
        *
        * @return the reference to the interface.
        */
       public Rights getRightsInstance(Object[] rights)
       {
          return new SystemRights((String)rights[0]);
       }

       /**
        * Checks whether a given string is a valid resource name
        * for this resource type.
        *
        * @param  resource
        *         string naming a resource
        *
        * @return <code>true</code> if the name is syntactically correct
        */
       public boolean isInstanceNameValid(String resource)
       {
          for (int i = 0; i < validInstances.length; i ++)
          {
             if (resource.equals(validInstances[i]))
                return true;
          }

          return false;
       }

       /**
        * Checks whether a given array of objects representing a set of access
        * rights fields is acceptable.
        *
        * @param  rights
        *         an array of objects of proper types representing items in a set
        *         of access rights
        *
        * @return <code>true</code> if the array is acceptable for an instance
        *         of access rights
        */
       public boolean isRightsSetValid(Object[] rights)
       {
          if (rights.length != 1)
             return false;

          if (!rights[0].getClass().isInstance(""))
             return false;

          return true;
       }

       /**
        * Checks access rights of the current subject with regards to the "logon"
        * instance of the "system" abstract resource.
        *
        * @return <code>true</code> if access is allowed.
        */
       boolean checkLogonAccess()
       {
          return systemAccessWorker("logon", SYSTEM_ACCESS);
       }

       /**
        * Checks access rights of the current subject with regards to the "change"
        * instance of the "system" abstract resource.
        *
        * @return <code>true</code> if access is allowed.
        */
       boolean checkChangeAccess()
       {
          return systemAccessWorker("change", SYSTEM_ACCESS);
       }

       /**
        * Checks access rights of the current subject with regards to the
        * "shutdown" instance of the "system" abstract resource.
        *
        * @return <code>true</code> if access is allowed.
        */
       boolean checkShutdownAccess()
       {
          return systemAccessWorker("shutdown", SYSTEM_ACCESS);
       }

       /**
        * Checks access rights of the current subject with regards to the
        * "debug" instance of the "system" abstract resource.
        *
        * @return <code>true</code> if access is allowed.
        */
       boolean checkDebugAccess()
       {
          return systemAccessWorker("debug", SYSTEM_ACCESS);
       }

       /**
        * Checks access rights of the current subject with regards to the
        * "accounts" instance of the "system" abstract resource.
        *
        * @return <code>true</code> if access is allowed.
        */
       boolean checkAccountsAccess()
       {
          return systemAccessWorker("accounts", SYSTEM_ACCESS);
       }

       /**
        * Checks access rights of the current subject with regards to the
        * "extensions" instance of the "system" abstract resource.
        *
        * @return <code>true</code> if access is allowed.
        */
       boolean checkExtensionsAccess()
       {
          return systemAccessWorker("extensions", SYSTEM_ACCESS);
       }

       /**
        * Checks access rights of the current subject with regards to the
        * "admin" instance of the "system" abstract resource.
        *
        * @return <code>true</code> if access is allowed.
        */
       boolean checkAdminAccess()
       {
          return systemAccessWorker("admin", SYSTEM_ACCESS);
       }

       /**
        * Implements generalized access rights check worker.
        *
        * @param instanceName
        *        name of the particular instance of the abstract resource about
        *        to be accessed
        *
        * @param mode
        *        an integer value encoding the access mode specific to this
        *        resource
        *
        * @return <code>true</code> if access is allowed.
        */
       private boolean systemAccessWorker(String instanceName, int mode)
       {
          Boolean cached   = null;
          boolean decision = false;

          // check to see if there is a cached decision
          cached = sm.getCachedDecision(resourceIndex, instanceName, mode);

          if (cached != null)
          {
             return cached.booleanValue();
          }

          // initiate the ACL search
          int handle = sm.openRightsSearch(resourceIndex, instanceName, mode);

          boolean isFinerLoggable = LOG.isLoggable(Level.FINER);
          if (isFinerLoggable)
          {
             LOG.finer("Search open: resId " + resourceIndex + ", instance " +
                          instanceName + ", mode " + mode + ", handle " + handle);
          }

          // rights check loop
          SystemRights rights = (SystemRights) sm.getNextRights(handle);

          while (rights != null)
          {
             if (isFinerLoggable)
             {
                LOG.finer("Search next: handle " + handle + ", rights " + rights);
             }
             decision = checkSingle(rights);

             if (decision)
                break;

             rights = (SystemRights) sm.getNextRights(handle);
          }

          if (isFinerLoggable)
          {
             LOG.finer("Search done: handle " + handle + ", decision " +
                          decision + ", cache false");
          }

          // close the ACL search
          sm.closeRightsSearch(handle, decision, false);

          return decision;
       }

       /**
        * Implements a worker that performs access rights check on a single
        * instance of the SystemRights object.
        *
        * @param    rights
        *           The instance of SystemRights to check, must not be
        *           <code>null</code>.
        *
        * @return   <code>true</code> if access is allowed.
        */
       private boolean checkSingle(SystemRights rights)
       {
          Object obj = sm.evaluate(resourceIndex, rights, rights.getCondition());

          if (obj == null || !Boolean.class.isInstance(obj))
             return false;

          Boolean result = (Boolean) obj;

          return result.booleanValue();
       }
    }