VariablePool.java

/*
** Module   : VariablePool.java
** Abstract : implements variables for access rights expressions
**
** Copyright (c) 2005-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
** 001 NVS 20050323 NEW   @20461 Created the initial version. This class 
**                               implements SecurityManager variables that may
**                               be used in access rights expressions.
** 002 NVS 20050411 CHG   @20677 Fixed few javadoc issues
** 003 NVS 20050414 CHG   @20732 Added handling for the RestrictedUseException
** 004 NVS 20050418 CHG   @20769 New Notifiable required dummy initEIR().
** 005 GES 20060305 CHG   @24896 Logging level changes.
** 006 GES 20070111 CHG   @31802 Removed use of notifiable (it was an over-
**                               complicated approach).
** 007 EVL 20070609 CHG   @34005 Adding explicit import of the class
**                               com.goldencode.p2j.net.Queue to eliminate
**                               conflict with the same class from java.util
**                               package to be able to compile for Java 6.
** 008 ECF 20071106 CHG   @35903 Eliminated references to network Queue class.
**                               Replaced with SessionManager API calls.
** 009 GES 20081027 CHG   @40344 Some cleanups to eliminate direct socket use.
**                               Added isSecureConnection() variable.
** 010 GBB 20230512              Logging methods replaced by CentralLogger/ConversionStatus.
** 011 GBB 20230825              SecurityManager session method call updated.
** 012 GBB 20240313              Session renamed to SecuritySession to resolve ambigious imports.
*/ 
/*
** 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 java.util.*;
import java.text.*;
import java.net.*;
import com.goldencode.p2j.net.*;
import com.goldencode.p2j.directory.*;

/* TODO:
 * 1. there is a great deal of cut and paste duplication throughout this file
 *    (which is unnecessary), most likely there should be some new helpers in 
 *    the security manager for the common stuff, but if this class is really
 *    the ONLY place the helpers would be used, then some private methods can
 *    be added to centralize an implementation
 * 2. the expression engine's data type limits no longer exist so the API can
 *    be simplified a bit (e.g. get rid of use of long where possible and use
 *    an int instead)
 */

/**
 * Implements Security Manager variables that may be used in access rights
 * expressions. 
 */
public class VariablePool
{
   /**
    * Constructor.
    */
   public VariablePool()
   {
   }

   /**
    * Gets the current application ID.
    *
    * @return current application ID
    */
   public String getAppId()
   {
      SecurityCache sc = SecurityManager.getInstance().getCache(); 
      SecurityContextStack ctx = SecurityContextStack.getContext();
      
      if (ctx == null)
         return "";

      int id = ctx.getIdList()[0];

      if (id == -1)
         return "";
      else
         return sc.getAccountByOrd(id).getSubjectId();
   }

   /**
    * Gets the current user ID.
    *
    * @return current user ID
    */
   public String getUserId()
   {
      SecurityCache sc = SecurityManager.getInstance().getCache(); 
      SecurityContextStack ctx = SecurityContextStack.getContext();

      if (ctx == null)
         return "";

      int id = ctx.getIdList()[1];

      if (id == -1)
         return "";
      else
         return sc.getAccountByOrd(id).getSubjectId();
   }

   /**
    * Gets the number of groups.
    *
    * @return number of groups
    */
   public long getGroups()
   {
      SecurityContextStack ctx = getContext();
      
      if (ctx == null)
         return 0;

      int[] idList = ctx.getIdList();
      return idList.length - 2;
   }

   /**
    * Gets the first defined group ID. 
    *
    * @return the first defined group ID
    */
   public String getGroupId()
   {
      SecurityCache sc = SecurityManager.getInstance().getCache(); 
      SecurityContextStack ctx = SecurityContextStack.getContext();
      
      if (ctx == null)
         return "";

      int[] idList = ctx.getIdList();

      if (idList.length == 2)
         return "";
      else
         return sc.getAccountByOrd(idList[2]).getSubjectId();
   }

   /**
    * Gets a group name by index.
    *
    * @param i
    *        index of the group name to get
    * @return indexed group name
    */
   public String getGroup(long i)
   {
      SecurityCache sc = SecurityManager.getInstance().getCache(); 
      SecurityContextStack ctx = SecurityContextStack.getContext();
      
      if (ctx == null)
         return "";

      int[] idList = ctx.getIdList();

      if (2 + i >= idList.length)
         return "";
      else
         return sc.getAccountByOrd(idList[2 + (int)i]).getSubjectId();
   }

   /**
    * Gets the TLS peer name. 
    *
    * @return TLS peer name
    */
   public String getPeerName()
   {
      SecurityContextStack ctx = getContext();
      
      if (ctx == null)
         return "";

      NetSocket   nsock = getSocket(ctx);
      InetAddress peer  = nsock.getRemoteAddr();
      
      return peer.getHostName();
   }

   /**
    * Gets the TLS server name. 
    *
    * @return TLS server name
    */
   public String getServerName()
   {
      SecurityContextStack ctx = getContext();
      
      if (ctx == null)
         return "";

      NetSocket   nsock = getSocket(ctx);
      InetAddress serv  = nsock.getLocalAddr();
      
      return serv.getHostName();
   }

   /**
    * Gets the TLS peer IP address. 
    *
    * @return TLS peer IP address
    */
   public String getPeerIp()
   {
      SecurityContextStack ctx = getContext();
      
      if (ctx == null)
         return "";

      NetSocket   nsock = getSocket(ctx);
      InetAddress peer  = nsock.getRemoteAddr();
      
      return peer.getHostAddress();
   }

   /**
    * Gets the TLS server IP address. 
    *
    * @return TLS server IP address
    */
   public String getServerIp()
   {
      SecurityContextStack ctx = getContext();
      
      if (ctx == null)
         return "";

      NetSocket   nsock = getSocket(ctx);
      InetAddress serv  = nsock.getLocalAddr();
      
      return serv.getHostAddress();
   }

   /**
    * Gets the secure status of the socket in use for this session. 
    *
    * @return   <code>true</code> if the socket is based on SSL/TLS and
    *           <code>false</code> if the socket is insecure.
    */
   public boolean isSecureConnection()
   {
      SecurityContextStack ctx = getContext();
      
      if (ctx == null)
         return false;

      NetSocket nsock = getSocket(ctx);
      
      return (nsock.getSession() != null);
   }

   /**
    * Gets the TLS P2J peer node address. 
    *
    * @return  TLS P2J peer node address, or 0 if there is no network session
    *          for the current context.
    */
   public long getPeerNode()
   {
      return SessionManager.get().getNodeAddress();
   }

   /**
    * Gets the TLS P2J server node address. 
    *
    * @return  TLS P2J server node address, or 0 if there is no network
    *          session for the current context.
    */
   public long getServerNode()
   {
      return SessionManager.get().getRemoteAddress();
   }

   /**
    * Gets the current date as "YYYY/MM/DD".
    *
    * @return current date
    */
   public String getDate()
   {
      Date date = new Date();
      DateFormat df = new SimpleDateFormat("yyyy/MM/dd");
      StringBuffer b = new StringBuffer();

      df.format(date, b, new FieldPosition(DateFormat.YEAR_FIELD));
      return new String(b);
   }

   /**
    * Gets the current year. 
    *
    * @return current year
    */
   public long getYear()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.YEAR);
   }

   /**
    * Gets the current month. 
    *
    * @return current month
    */
   public long getMonth()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.MONTH) + 1;
   }

   /**
    * Gets the current day of the month. 
    *
    * @return current day of the month
    */
   public long getDayOfMonth()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.DATE);
   }

   /**
    * Gets the current day of the year.
    *
    * @return current day of the year
    */
   public long getDayOfYear()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.DAY_OF_YEAR);
   }

   /**
    * Gets the current day of the epoch. 
    *
    * @return current day of the epoch
    */
   public long getDayOfEpoch()
   {
      Date date = new Date();
      return date.getTime() / 1000 / 86400;
   }

   /**
    * Gets the current day of the week. 
    *
    * @return current day of the week
    */
   public long getWeekDay()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.DAY_OF_WEEK);
   }

   /**
    * Gets the current week number. 
    *
    * @return current week number
    */
   public long getWeekNum()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.WEEK_OF_YEAR);
   }

   /**
    * Checks whether today is a holiday.  
    *
    * @return <code>true</code> if today is a public holiday
    */
   public boolean isHoliday()
   {
      // Safely query cached data
      SecurityCache sc = SecurityManager.getInstance().getCache(); 

      DateValue today = new DateValue();

      return sc.getHolidays().contains(today.toString());
   }

   /**
    * Gets the current time of day. 
    *
    * @return current time of day
    */
   public String getTime()
   {
      Date date = new Date();
      DateFormat df = new SimpleDateFormat("HH/mm/ss");
      StringBuffer b = new StringBuffer();

      df.format(date, b, new FieldPosition(DateFormat.HOUR_OF_DAY0_FIELD));
      return new String(b);
   }

   /**
    * Gets the current hour for 12 hour clock. 
    *
    * @return current hour for 12 hour clock
    */
   public long getHourAmPm()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.HOUR);
   }

   /**
    * Gets the current hour for 24 hour clock. 
    *
    * @return current hour for 24 hour clock
    */
   public long getHour()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.HOUR_OF_DAY);
   }

   /**
    * Gets the current minute. 
    *
    * @return current minute
    */
   public long getMinute()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.MINUTE);
   }

   /**
    * Gets the current second.
    *
    * @return current second
    */
   public long getSecond()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.SECOND);
   }

   /**
    * Gets the current minute since midnight.
    *
    * @return current minute since midnight
    */
   public long getMinSinceMidnight()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.MINUTE) + cal.get(Calendar.HOUR_OF_DAY) * 60;
   }

   /**
    * Gets the current second since midnight.
    *
    * @return current second since midnight
    */
   public long getSecSinceMidnight()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.SECOND) +
         (cal.get(Calendar.MINUTE) + cal.get(Calendar.HOUR_OF_DAY) * 60) * 60;
   }

   /**
    * Checks whether it is AM time. 
    *
    * @return <code>true</code> if it is AM time.
    */
   public boolean isAm()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.AM_PM) == Calendar.AM;
   }

   /**
    * Checks whether it is PM time. 
    *
    * @return <code>true</code> if it is PM time.
    */
   public boolean isPm()
   {
      Calendar cal = Calendar.getInstance();
      return cal.get(Calendar.AM_PM) == Calendar.PM;
   }

   /**
    * Reads a single (the first) string value from a P2J directory attribute.
    *
    * @param oid
    *        directory object id to query
    * @param attrName
    *        attribute name to read
    * @return attribute value
    */
   public String dirS(String oid, String attrName)
   {
      return dirS(oid, attrName, 0);
   }

   /**
    * Reads a single indexed string value from a P2J directory attribute.
    *
    * @param oid
    *        directory object id to query
    * @param attrName
    *        attribute name to read
    * @param i
    *        index of the particular value to read
    * @return attribute value
    */
   public String dirS(String oid, String attrName, long i)
   {
      // accessing directory
      DirectoryService ds = DirectoryService.getInstance();

      ds.bind();
      String value = ds.getNodeString(oid, attrName, (int)i);
      ds.unbind();

      return value;
   }

   /**
    * Reads a single (the first) integer value from a P2J directory attribute.
    *
    * @param oid
    *        directory object id to query
    * @param attrName
    *        attribute name to read
    * @return attribute value
    */
   public long dirL(String oid, String attrName)
   {
      return dirL(oid, attrName, 0);
   }

   /**
    * Reads a single indexed integer value from a P2J directory attribute.
    *
    * @param oid
    *        directory object id to query
    * @param attrName
    *        attribute name to read
    * @param i
    *        index of the particular value to read
    * @return attribute value
    */
   public long dirL(String oid, String attrName, long i)
   {
      // accessing directory
      DirectoryService ds = DirectoryService.getInstance();

      ds.bind();
      Integer value = ds.getNodeInteger(oid, attrName, (int)i);
      ds.unbind();

      return value == null ? 0 : value.intValue();
   }

   /**
    * Reads a single (the first) boolean value from a P2J directory attribute.
    *
    * @param oid
    *        directory object id to query
    * @param attrName
    *        attribute name to read
    * @return attribute value
    */
   public boolean dirB(String oid, String attrName)
   {
      return dirB(oid, attrName, 0);
   }

   /**
    * Reads a single indexed boolean value from a P2J directory attribute.
    *
    * @param oid
    *        directory object id to query
    * @param attrName
    *        attribute name to read
    * @param i
    *        index of the particular value to read
    * @return attribute value
    */
   public boolean dirB(String oid, String attrName, long i)
   {
      // accessing directory
      DirectoryService ds = DirectoryService.getInstance();

      ds.bind();
      Boolean value = ds.getNodeBoolean(oid, attrName, (int)i);
      ds.unbind();

      return value == null ? false : value.booleanValue();
   }
   
   /**
    * Safely query the cached security context.
    *
    * @return   The security context or <code>null</code> if there is no
    *           context available.
    */
   private SecurityContextStack getContext()
   {
      SecurityCache sc = SecurityManager.getInstance().getCache(); 

      return SecurityContextStack.getContext();
   }
   
   /**
    * Access the network state associated with the current security context.
    *
    * @param    ctx
    *           The current session's security context.
    *
    * @return   The socket associated with the remote connection.
    */
   private NetSocket getSocket(SecurityContextStack ctx)
   {
      SecurityContext key = ctx.getSessionKey();
      SecuritySession sess = SecurityManager.getInstance().sessionSm.locateSession(key);
      Object sessid = sess.getSessionId();
      return (NetSocket) sessid;
   }
}