mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
CE-937 Add concept of customizers to authentication modules; basic implementation within auth0 for customizing session/security keys
This commit is contained in:
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 //
|
||||||
|
//////////
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user