CE-937 Add concept of customizers to authentication modules; basic implementation within auth0 for customizing session/security keys

This commit is contained in:
2024-02-26 14:29:45 -06:00
parent 29a54f5293
commit 878f374cb5
6 changed files with 349 additions and 42 deletions

View File

@ -153,6 +153,7 @@ public class QInstanceValidator
try try
{ {
validateBackends(qInstance); validateBackends(qInstance);
validateAuthentication(qInstance);
validateAutomationProviders(qInstance); validateAutomationProviders(qInstance);
validateTables(qInstance, joinGraph); validateTables(qInstance, joinGraph);
validateProcesses(qInstance); validateProcesses(qInstance);
@ -383,6 +384,23 @@ public class QInstanceValidator
/*******************************************************************************
**
*******************************************************************************/
private void validateAuthentication(QInstance qInstance)
{
QAuthenticationMetaData authentication = qInstance.getAuthentication();
if(authentication != null)
{
if(authentication.getCustomizer() != null)
{
validateSimpleCodeReference("Instance Authentication meta data customizer ", authentication.getCustomizer(), QAuthenticationModuleCustomizerInterface.class);
}
}
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -28,6 +28,7 @@ import com.fasterxml.jackson.annotation.JsonFilter;
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType; import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface; import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
/******************************************************************************* /*******************************************************************************
@ -43,6 +44,7 @@ public class QAuthenticationMetaData implements TopLevelMetaDataInterface
@JsonFilter("secretsFilter") @JsonFilter("secretsFilter")
private Map<String, String> values; private Map<String, String> values;
private QCodeReference customizer;
/******************************************************************************* /*******************************************************************************
@ -192,4 +194,35 @@ public class QAuthenticationMetaData implements TopLevelMetaDataInterface
qInstance.setAuthentication(this); qInstance.setAuthentication(this);
} }
/*******************************************************************************
** Getter for customizer
*******************************************************************************/
public QCodeReference getCustomizer()
{
return (this.customizer);
}
/*******************************************************************************
** Setter for customizer
*******************************************************************************/
public void setCustomizer(QCodeReference customizer)
{
this.customizer = customizer;
}
/*******************************************************************************
** Fluent setter for customizer
*******************************************************************************/
public QAuthenticationMetaData withCustomizer(QCodeReference customizer)
{
this.customizer = customizer;
return (this);
}
} }

View File

@ -0,0 +1,55 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* 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 should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.modules.authentication;
import java.io.Serializable;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.session.QSession;
/*******************************************************************************
** Interface for customizing behavior of an Authentication module.
*******************************************************************************/
public interface QAuthenticationModuleCustomizerInterface
{
/*******************************************************************************
**
*******************************************************************************/
default void addSecurityKeyValueToSession(QSession session, String keyName, Serializable value)
{
session.withSecurityKeyValue(keyName, value);
}
/*******************************************************************************
**
*******************************************************************************/
default void customizeSession(QInstance qInstance, QSession qSession, Map<String, Object> context)
{
//////////
// noop //
//////////
}
}

View File

@ -47,6 +47,7 @@ import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.net.Response; import com.auth0.net.Response;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction; import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
@ -71,6 +72,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0Authent
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType; import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.model.session.QUser; import com.kingsrook.qqq.backend.core.model.session.QUser;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface; import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.model.UserSession; import com.kingsrook.qqq.backend.core.modules.authentication.implementations.model.UserSession;
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider; import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
@ -80,6 +82,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils; import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils;
import com.kingsrook.qqq.backend.core.utils.memoization.Memoization;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
@ -129,6 +132,19 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
static final String EXPIRED_TOKEN_ERROR = "Token has expired"; static final String EXPIRED_TOKEN_ERROR = "Token has expired";
static final String INVALID_TOKEN_ERROR = "An invalid token was provided"; static final String INVALID_TOKEN_ERROR = "An invalid token was provided";
private Auth0AuthenticationMetaData metaData;
//////////////////////////////////////////////////////////////////////////////////
// do not use this var directly - rather - always call the getCustomizer method //
//////////////////////////////////////////////////////////////////////////////////
private QAuthenticationModuleCustomizerInterface _customizer = null;
private boolean customizerHasBeenRequested = false;
private static boolean mayMemoize = true;
private static final Memoization<String, String> getAccessTokenFromSessionUUIDMemoization = new Memoization<String, String>()
.withTimeout(Duration.of(1, ChronoUnit.MINUTES))
.withMaxSize(1000);
//////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
// this is how we allow the actions within this class to work without themselves having a logged-in user. // // this is how we allow the actions within this class to work without themselves having a logged-in user. //
@ -165,9 +181,12 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
@Override @Override
public QSession createSession(QInstance qInstance, Map<String, String> context) throws QAuthenticationException public QSession createSession(QInstance qInstance, Map<String, String> context) throws QAuthenticationException
{ {
QInstance contextInstanceBefore = QContext.getQInstance();
try try
{ {
Auth0AuthenticationMetaData metaData = (Auth0AuthenticationMetaData) qInstance.getAuthentication(); QContext.setQInstance(qInstance);
this.metaData = (Auth0AuthenticationMetaData) qInstance.getAuthentication();
String accessToken = null; String accessToken = null;
if(CollectionUtils.containsKeyWithNonNullValue(context, SESSION_UUID_KEY)) if(CollectionUtils.containsKeyWithNonNullValue(context, SESSION_UUID_KEY))
@ -252,16 +271,6 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
} }
} }
/* todo confirm this is deprecated
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// check to see if the session id is a UUID, if so, that means we need to look up the 'actual' token in the access_token table //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(accessToken != null && StringUtils.isUUID(accessToken))
{
accessToken = lookupActualAccessToken(metaData, accessToken);
}
*/
/////////////////////////////////////////// ///////////////////////////////////////////
// if token wasn't found by now, give up // // if token wasn't found by now, give up //
/////////////////////////////////////////// ///////////////////////////////////////////
@ -311,6 +320,10 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
LOG.error(message, e); LOG.error(message, e);
throw (new QAuthenticationException(message)); throw (new QAuthenticationException(message));
} }
finally
{
QContext.setQInstance(contextInstanceBefore);
}
} }
@ -346,6 +359,11 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
*******************************************************************************/ *******************************************************************************/
private QSession buildAndValidateSession(QInstance qInstance, String accessToken) throws JwkException private QSession buildAndValidateSession(QInstance qInstance, String accessToken) throws JwkException
{ {
QSession beforeSession = QContext.getQSession();
try
{
QContext.setQSession(getChickenAndEggSession());
QSession qSession = buildQSessionFromToken(accessToken, qInstance); QSession qSession = buildQSessionFromToken(accessToken, qInstance);
if(isSessionValid(qInstance, qSession)) if(isSessionValid(qInstance, qSession))
{ {
@ -367,6 +385,11 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
return (qSession); return (qSession);
} }
finally
{
QContext.setQSession(beforeSession);
}
}
@ -509,7 +532,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
*******************************************************************************/ *******************************************************************************/
private void validateToken(QInstance qInstance, String tokenString) throws JwkException private void validateToken(QInstance qInstance, String tokenString) throws JwkException
{ {
Auth0AuthenticationMetaData metaData = (Auth0AuthenticationMetaData) qInstance.getAuthentication(); this.metaData = (Auth0AuthenticationMetaData) qInstance.getAuthentication();
DecodedJWT idToken = JWT.decode(tokenString); DecodedJWT idToken = JWT.decode(tokenString);
JwkProvider provider = new UrlJwkProvider(metaData.getBaseUrl()); JwkProvider provider = new UrlJwkProvider(metaData.getBaseUrl());
@ -567,10 +590,10 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
qSession.setIdReference(accessToken); qSession.setIdReference(accessToken);
qSession.setUser(qUser); qSession.setUser(qUser);
///////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// put the user id reference in security key value for usierId key // // put the user id reference in security key value for userId key //
///////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
qSession.withSecurityKeyValue("userId", qUser.getIdReference()); addSecurityKeyValueToSession(qSession, "userId", qUser.getIdReference());
///////////////////////////////////////////////// /////////////////////////////////////////////////
// set permissions in the session from the JWT // // set permissions in the session from the JWT //
@ -582,11 +605,42 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession); setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
//////////////////////////////////////////////////////////////
// allow customizer to do custom things here, if so desired //
//////////////////////////////////////////////////////////////
if(getCustomizer() != null)
{
getCustomizer().customizeSession(qInstance, qSession, Map.of("jwtPayloadJsonObject", payload));
}
return (qSession); return (qSession);
} }
/*******************************************************************************
**
*******************************************************************************/
private void addSecurityKeyValueToSession(QSession qSession, String key, String value)
{
if(getCustomizer() == null)
{
///////////////////////////////////////////////////
// if there's no customizer, do the direct thing //
///////////////////////////////////////////////////
qSession.withSecurityKeyValue(key, value);
}
else
{
///////////////////////////////////////////////////////////
// else have the customizer add the value to the session //
///////////////////////////////////////////////////////////
getCustomizer().addSecurityKeyValueToSession(qSession, key, value);
}
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -616,7 +670,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
static void setSecurityKeysInSessionFromJwtPayload(QInstance qInstance, JSONObject payload, QSession qSession) void setSecurityKeysInSessionFromJwtPayload(QInstance qInstance, JSONObject payload, QSession qSession)
{ {
for(String payloadKey : List.of("com.kingsrook.qqq.app_metadata", "com.kingsrook.qqq.client_metadata")) for(String payloadKey : List.of("com.kingsrook.qqq.app_metadata", "com.kingsrook.qqq.client_metadata"))
{ {
@ -668,7 +722,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private static void setSecurityKeyValuesFromToken(Set<String> allowedSecurityKeyNames, QSession qSession, String securityKeyName, JSONObject securityKeyValues, String jsonKey) private void setSecurityKeyValuesFromToken(Set<String> allowedSecurityKeyNames, QSession qSession, String securityKeyName, JSONObject securityKeyValues, String jsonKey)
{ {
if(!allowedSecurityKeyNames.contains(securityKeyName)) if(!allowedSecurityKeyNames.contains(securityKeyName))
{ {
@ -686,7 +740,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
Object optValue = valueArray.opt(i); Object optValue = valueArray.opt(i);
if(optValue != null) if(optValue != null)
{ {
qSession.withSecurityKeyValue(securityKeyName, ValueUtils.getValueAsString(optValue)); addSecurityKeyValueToSession(qSession, securityKeyName, ValueUtils.getValueAsString(optValue));
} }
} }
} }
@ -697,7 +751,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
{ {
for(String v : values.split(",")) for(String v : values.split(","))
{ {
qSession.withSecurityKeyValue(securityKeyName, v); addSecurityKeyValueToSession(qSession, securityKeyName, v);
} }
} }
} }
@ -815,6 +869,25 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
** **
*******************************************************************************/ *******************************************************************************/
private String getAccessTokenFromSessionUUID(Auth0AuthenticationMetaData metaData, String sessionUUID) throws QAuthenticationException private String getAccessTokenFromSessionUUID(Auth0AuthenticationMetaData metaData, String sessionUUID) throws QAuthenticationException
{
if(mayMemoize)
{
return getAccessTokenFromSessionUUIDMemoization.getResultThrowing(sessionUUID, (String x) ->
doGetAccessTokenFromSessionUUID(sessionUUID)
).orElse(null);
}
else
{
return (doGetAccessTokenFromSessionUUID(sessionUUID));
}
}
/*******************************************************************************
**
*******************************************************************************/
private String doGetAccessTokenFromSessionUUID(String sessionUUID) throws QAuthenticationException
{ {
String accessToken = null; String accessToken = null;
QSession beforeSession = QContext.getQSession(); QSession beforeSession = QContext.getQSession();
@ -982,4 +1055,39 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
} }
} }
/*******************************************************************************
**
*******************************************************************************/
private QAuthenticationModuleCustomizerInterface getCustomizer()
{
try
{
if(!customizerHasBeenRequested)
{
customizerHasBeenRequested = true;
if(this.metaData == null)
{
this.metaData = (Auth0AuthenticationMetaData) QContext.getQInstance().getAuthentication();
}
if(this.metaData.getCustomizer() != null)
{
this._customizer = QCodeLoader.getAdHoc(QAuthenticationModuleCustomizerInterface.class, this.metaData.getCustomizer());
}
}
return (this._customizer);
}
catch(Exception e)
{
////////////////////////
// should this throw? //
////////////////////////
LOG.warn("Error getting customizer.", e);
return (null);
}
}
} }

View File

@ -1813,6 +1813,19 @@ class QInstanceValidatorTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAuthenticationCustomizer()
{
assertValidationSuccess((qInstance -> qInstance.getAuthentication().withCustomizer(null)));
assertValidationSuccess((qInstance -> qInstance.getAuthentication().withCustomizer(new QCodeReference(ValidAuthCustomizer.class))));
assertValidationFailureReasons((qInstance -> qInstance.getAuthentication().withCustomizer(new QCodeReference(ArrayList.class))), "not of the expected type");
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -1987,5 +2000,13 @@ class QInstanceValidatorTest extends BaseTest
return null; return null;
} }
} }
/*******************************************************************************
**
*******************************************************************************/
public static class ValidAuthCustomizer implements QAuthenticationModuleCustomizerInterface {}
} }

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.modules.authentication.implementations; package com.kingsrook.qqq.backend.core.modules.authentication.implementations;
import java.io.Serializable;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Base64; import java.util.Base64;
@ -36,7 +37,9 @@ import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface;
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider; import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
import com.kingsrook.qqq.backend.core.state.SimpleStateKey; import com.kingsrook.qqq.backend.core.state.SimpleStateKey;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -354,7 +357,7 @@ public class Auth0AuthenticationModuleTest extends BaseTest
"iat": 1673379451 "iat": 1673379451
} }
"""); """);
Auth0AuthenticationModule.setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession); new Auth0AuthenticationModule().setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
assertEquals(List.of("true"), qSession.getSecurityKeyValues("storeAllAccess")); assertEquals(List.of("true"), qSession.getSecurityKeyValues("storeAllAccess"));
///////////////////////////////////////////// /////////////////////////////////////////////
@ -373,7 +376,7 @@ public class Auth0AuthenticationModuleTest extends BaseTest
"iat": 1673379451 "iat": 1673379451
} }
"""); """);
Auth0AuthenticationModule.setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession); new Auth0AuthenticationModule().setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
assertEquals(List.of("2"), qSession.getSecurityKeyValues("store")); assertEquals(List.of("2"), qSession.getSecurityKeyValues("store"));
////////////////////////// //////////////////////////
@ -394,7 +397,7 @@ public class Auth0AuthenticationModuleTest extends BaseTest
"iat": 1673379451 "iat": 1673379451
} }
"""); """);
Auth0AuthenticationModule.setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession); new Auth0AuthenticationModule().setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
assertEquals(List.of("3", "4", "5"), qSession.getSecurityKeyValues("store")); assertEquals(List.of("3", "4", "5"), qSession.getSecurityKeyValues("store"));
assertEquals(List.of("internal"), qSession.getSecurityKeyValues("internalOrExternal")); assertEquals(List.of("internal"), qSession.getSecurityKeyValues("internalOrExternal"));
@ -409,7 +412,7 @@ public class Auth0AuthenticationModuleTest extends BaseTest
"iat": 1673379451 "iat": 1673379451
} }
"""); """);
Auth0AuthenticationModule.setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession); new Auth0AuthenticationModule().setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
assertTrue(CollectionUtils.nullSafeIsEmpty(qSession.getSecurityKeyValues())); assertTrue(CollectionUtils.nullSafeIsEmpty(qSession.getSecurityKeyValues()));
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -428,7 +431,7 @@ public class Auth0AuthenticationModuleTest extends BaseTest
"iat": 1673379451 "iat": 1673379451
} }
"""); """);
Auth0AuthenticationModule.setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession); new Auth0AuthenticationModule().setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
assertTrue(CollectionUtils.nullSafeIsEmpty(qSession.getSecurityKeyValues())); assertTrue(CollectionUtils.nullSafeIsEmpty(qSession.getSecurityKeyValues()));
} }
@ -491,4 +494,73 @@ public class Auth0AuthenticationModuleTest extends BaseTest
assertEquals("123456******", maskForLog("1234567890")); assertEquals("123456******", maskForLog("1234567890"));
} }
/*******************************************************************************
**
*******************************************************************************/
@Test
void testCustomizer()
{
QInstance qInstance = QContext.getQInstance();
qInstance.setAuthentication(new Auth0AuthenticationMetaData()
.withCustomizer(new QCodeReference(Customizer.class)));
{
/////////////////////////////////////////////////////////////////
// baseline case - value in json becomes value in security key //
/////////////////////////////////////////////////////////////////
QSession qSession = new QSession();
JSONObject payload = new JSONObject("""
{
"com.kingsrook.qqq.app_metadata": {
"securityKeyValues": {
"store": "1"
}
}
}
""");
new Auth0AuthenticationModule().setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
assertEquals(List.of("1"), qSession.getSecurityKeyValues("store"));
}
{
QSession qSession = new QSession();
JSONObject payload = new JSONObject("""
{
"com.kingsrook.qqq.app_metadata": {
"securityKeyValues": {
"store": "oddDigits"
}
}
}
""");
new Auth0AuthenticationModule().setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
assertEquals(List.of("1", "3", "5", "7", "9"), qSession.getSecurityKeyValues("store"));
}
}
/*******************************************************************************
**
*******************************************************************************/
public static class Customizer implements QAuthenticationModuleCustomizerInterface
{
@Override
public void addSecurityKeyValueToSession(QSession session, String keyName, Serializable value)
{
if("oddDigits".equals(value))
{
for(String oddValue : List.of("1", "3", "5", "7", "9"))
{
QAuthenticationModuleCustomizerInterface.super.addSecurityKeyValueToSession(session, keyName, oddValue);
}
}
else
{
QAuthenticationModuleCustomizerInterface.super.addSecurityKeyValueToSession(session, keyName, value);
}
}
}
} }