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