SsoAuthenticator.java

/*
 ** Module   : SsoAuthenticator.java
 ** Abstract : Base interface for SSO authentication hooks.
 **
 ** Copyright (c) 2023-2025, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 GBB 20230704 Created initial version
 ** 002 GBB 20231121 Javadoc fix. Adding sessionDescription to result.
 ** 003 GBB 20240215 Fingerprint replaced by browser device id.
 ** 004 GBB 20240808 Adding methods initialize and terminate to be called as server hooks.
 ** 005 CA  20250221 Allow the SSO plugin implementation to specify OS user/password.
 */
/*
 ** 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.main.*;

import javax.servlet.http.*;
import java.util.*;

/** Base interface for the SSO authentication hooks */
public interface SsoAuthenticator
{
   /** The form parameter name for the user, when the FWD SDK script is used on the login page. */
   String FWD_SDK_LOGIN_PARAM_USER = "usr";

   /** The form parameter name for the password, when the FWD SDK script is used on the login page. */
   String FWD_SDK_LOGIN_PARAM_PASS = "psw";
   
   /**
    * Provides options configured in directory under config/auth-mode/ssopluginoption. Plain text that can 
    * contain any number of configs to be parsed by the custom implementation of the authenticator. The 
    * method is called right after the current object is instantiated, before any authentication takes place.
    *
    * @param    option
    *           Plain text that can contain any number of configs.
    */
   void setOption(String option);

   /**
    * The method is called on server startup with the server context, after the default database 
    * connections are initialized, and can be used to read configurations from directory.
    */
   default void initialize()
   {
   }

   /**
    * The method is called on server termination.
    */
   default void terminate()
   {
   }

   /**
    * Receives as an argument the html template loaded from the standard location in the custom package or 
    * null if no resource present.
    *
    * @param    loadedTemplate
    *           The html template loaded from the standard location in the custom root package or null.
    *
    * @return   The inflated template ready to be served as login page or null.
    */
   String getLoginPage(String loadedTemplate);

   /**
    * Returns the name of the cookie used for auto-login. Enables the framework to do validation of the 
    * cookies present in the request and determine if auto-login is possible. If <code>null</code> is 
    * returned, that indicates the auto-login function is disabled.
    *
    * @return   The name of the cookie used for auto-login.
    */
   default String getAuthCookieName()
   {
      return null;
   }

   /**
    * Auto-login interface. Searches for a recognizable cookie to validate the session against an 
    * authentication provider (DB, 3rd party directory, custom auth server, etc) and returns a container 
    * with all details needed by the FWD framework to respond to the auth request and eventually spawn a 
    * new client with the specified authorization. On failure,
    * {@link #authenticate(Map, Cookie[], LicensingData)} will be later called by the login page on form 
    * submission.
    *
    * @param    cookies
    *           All cookies coming with the HTTP request for authentication.
    * @param    licensingData
    *           Data that can be used for enforcing licensing policy.
    *
    * @return   A container with all details needed by the FWD framework to spawn a new client.
    */
   default Result authenticate(Cookie[] cookies, LicensingData licensingData)
   {
      return null;
   }

   /**
    * Validates the POST request data against an authentication provider (DB, 3rd party directory, custom 
    * auth server, etc) and returns a container with all details needed by the FWD framework to respond to 
    * the auth request and eventually spawn a new client with the specified authorization. If the FWD SDK
    * script is used on the login page, the parameters map contains {@link #FWD_SDK_LOGIN_PARAM_USER} and
    * {@link #FWD_SDK_LOGIN_PARAM_PASS}.
    *
    * @param    paramMap
    *           A map of all query / form params.
    * @param    cookies
    *           All cookies coming with the HTTP request.
    * @param    licensingData
    *           Data that can be used for enforcing licensing policy.
    *
    * @return   A container with all details needed by the FWD framework to spawn a new client.
    */
   Result authenticate(Map<String, String[]> paramMap, Cookie[] cookies, LicensingData licensingData);

   /**
    * Invalidates the web session associated with the authentication cookie by communicating with the auth 
    * server or updating the DB and returns a cookie with the same name, but invalid value.
    *
    * @param    cookies
    *           The cookie to be returned to the browser and used in the next auth requests.
    * 
    * @return   <code>null</code> if the web session / cookie invalidation failed, otherwise an 
    *           authentication Cookie with invalid value.
    */
   Cookie logout(Cookie[] cookies);

   /** Wrapper class for all properties needed by the FWD framework to respond to the auth / spawn request */
   class Result
   {
      /** Flag to indicate if the authentication is successful. */
      private final boolean isSuccess;

      /** HTTP code for a failed. */
      private WebDriverHandler.SupportedHttpCode failureHttpCode;
      
      /**
       * The name of the FWD account defined in directory that corresponds to the authorization privileges of 
       * the authenticated in-app user.
       */
      private String fwdUser;

      /**
       * The identifier of the persistent client storage used by the authenticated user. This storage 
       * should have a unique identifier to allow multiple end users (or app accounts) authenticated on the 
       * same browser to have separate browser configurations and registry values. The identifier can be the 
       * in-app username or ID, but it should not change. Otherwise the stored data will be lost. All 
       * identifiers will be visible to the users in the browser dev tools.
       */
      private String persistentUserStorageIdentifier;

      /**
       * A blob of data used by the converted application for authorization. Will be available for 4GL 
       * apps under SESSION:AUTH-BLOB .
       */
      private String authBlob;

      /** The cookie to be returned to the browser and used in the next auth requests. */
      private Cookie cookie;

      /** Translated error message. Optional. The FWD default is about invalid credentials. */
      private String translatedErrorMessage;

      /**
       * Free form description of the session. Shows up in the Admin Console in 'Current Network Sessions' 
       * table. Can be used as a way to correlate sessions to in-app users.
       */
      private String sessionDescription;
      
      /** The OS user set by the implementation. */
      private String osUser = null;
      
      /** The OS password set by the implementation. */
      private String osPassword = null;
      
      /**
       * Public constructor to set all fields of this immutable wrapper.
       *
       * @param    isSuccess
       *           Flag to indicate if the authentication is successful.
       * @param    failureHttpCode
       *           Authentication failure HTTP code.
       * @param    translatedErrorMessage
       *           The message to be shown on the client UI. If none, the default one associated with the 
       *           http status is used.
       */
      public Result(boolean isSuccess,
                    WebDriverHandler.SupportedHttpCode failureHttpCode,
                    String translatedErrorMessage)
      {
         this.isSuccess = isSuccess;
         this.failureHttpCode = failureHttpCode;
         this.translatedErrorMessage = translatedErrorMessage;
      }
      
      /**
       * Public constructor to set all fields of this immutable wrapper.
       * 
       * @param    isSuccess
       *           Flag to indicate if the authentication is successful.
       * @param    fwdUser
       *           The name of the FWD account defined in directory.
       * @param    persistentUserStorageIdentifier
       *           The identifier of the persistent client storage used by the authenticated user.
       * @param    authBlob
       *           All cookies coming with the HTTP request for authentication.
       * @param    cookie
       *           A blob of data used by the converted application for authorization.
       */
      public Result(boolean isSuccess,
                    String fwdUser,
                    String persistentUserStorageIdentifier, 
                    String authBlob,
                    Cookie cookie)
      {
         this.isSuccess = isSuccess;
         this.fwdUser = fwdUser;
         this.persistentUserStorageIdentifier = persistentUserStorageIdentifier;
         this.authBlob = authBlob;
         this.cookie = cookie;
      }
      
      /**
       * Getter for {@link #isSuccess}.
       *
       * @return   See above.
       */
      public boolean isSuccess()
      {
         return isSuccess;
      }

      /**
       * Getter for {@link #failureHttpCode}.
       *
       * @return   See above.
       */
      public WebDriverHandler.SupportedHttpCode getFailureHttpCode()
      {
         return failureHttpCode;
      }

      /**
       * Getter for {@link #fwdUser}.
       *
       * @return   See above.
       */
      public String getFwdUser()
      {
         return fwdUser;
      }
      
      /**
       * Getter for {@link #persistentUserStorageIdentifier}.
       *
       * @return   See above.
       */
      public String getPersistentUserStorageIdentifier()
      {
         return persistentUserStorageIdentifier;
      }

      /**
       * Getter for {@link #cookie}.
       *
       * @return   See above.
       */
      public Cookie getCookie()
      {
         return cookie;
      }

      /**
       * Getter for {@link #authBlob}.
       *
       * @return   See above.
       */
      public String getAuthBlob()
      {
         return authBlob;
      }
      
      /**
       * Getter for {@link #translatedErrorMessage}.
       *
       * @return   See above.
       */
      public String getTranslatedErrorMessage()
      {
         return translatedErrorMessage;
      }

      /**
       * Getter for {@link #sessionDescription}.
       *
       * @return   See above.
       */
      public String getSessionDescription()
      {
         return sessionDescription;
      }

      /**
       * Setter for {@link #sessionDescription}.
       *
       * @param    sessionDescription
       *           See {@link #sessionDescription}.
       */
      public void setSessionDescription(String sessionDescription)
      {
         this.sessionDescription = sessionDescription;
      }
      
      /**
       * Get the {@link #osPassword} set by the SSO plugin.
       * 
       * @return   See above.
       */
      public String getOsPassword()
      {
         return osPassword;
      }

      /**
       * Set the {@link #osPassword}.
       * 
       * @param    osPassword
       *           The OS password.
       */
      public void setOsPassword(String osPassword)
      {
         this.osPassword = osPassword;
      }
      
      /**
       * Get the {@link #osUser} set by the SSO plugin.
       * 
       * @return   See above.
       */
      public String getOsUser()
      {
         return osUser;
      }

      /**
       * Set the {@link #osUSer}.
       * 
       * @param    osUser
       *           The OS user.
       */
      public void setOsUser(String osUser)
      {
         this.osUser = osUser;
      }
   }
   
   /** Wrapper class for all properties that can be used for enforcing licensing policy. */
   class LicensingData
   {
      /** The external IP of the user */
      private String externalUserIp;
      
      /** The internal IP of the user */
      private String internalUserIp;

      /** The browser device ID */
      private String browserDeviceId;

      /**
       * Public constructor to set all fields of this immutable wrapper.
       *
       * @param    externalUserIp
       *           The external IP of the user
       * @param    internalUserIp
       *           The internal IP of the user
       * @param    browserDeviceId
       *           The browser device ID
       */
      public LicensingData(String externalUserIp, String internalUserIp, String browserDeviceId)
      {
         this.externalUserIp = externalUserIp;
         this.internalUserIp = internalUserIp;
         this.browserDeviceId = browserDeviceId;
      }
      
      /**
       * Getter for {@link #externalUserIp}.
       *
       * @return   See above.
       */
      public String getExternalUserIp()
      {
         return externalUserIp;
      }
      
      /**
       * Getter for {@link #internalUserIp}.
       *
       * @return   See above.
       */
      public String getInternalUserIp()
      {
         return internalUserIp;
      }

      /**
       * Getter for {@link #browserDeviceId}.
       *
       * @return   See above.
       */
      public String getBrowserDeviceId()
      {
         return browserDeviceId;
      }
   }
}