# 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;
          }
       }
    }