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();
   }
}