diff --git a/pom.xml b/pom.xml index 4e88d244..392cbf2b 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-SNAPSHOT + 0.2.0-20220721.162748-8 scm:git:git@github.com:Kingsrook/qqq-backend-core.git @@ -53,6 +53,11 @@ + + com.auth0 + mvc-auth-commons + 1.9.2 + com.fasterxml.jackson.core jackson-databind diff --git a/src/main/java/com/kingsrook/qqq/backend/core/actions/ActionHelper.java b/src/main/java/com/kingsrook/qqq/backend/core/actions/ActionHelper.java index ae4e8f8a..d5ab7318 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/actions/ActionHelper.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/actions/ActionHelper.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.backend.core.actions; +import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleDispatcher; @@ -43,7 +44,7 @@ public class ActionHelper QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(request.getAuthenticationMetaData()); if(!authenticationModule.isSessionValid(request.getSession())) { - throw new QException("Invalid session in request"); + throw new QAuthenticationException("Invalid session in request"); } } diff --git a/src/main/java/com/kingsrook/qqq/backend/core/exceptions/QAuthenticationException.java b/src/main/java/com/kingsrook/qqq/backend/core/exceptions/QAuthenticationException.java new file mode 100644 index 00000000..c2237629 --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/backend/core/exceptions/QAuthenticationException.java @@ -0,0 +1,51 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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 . + */ + +package com.kingsrook.qqq.backend.core.exceptions; + + +/******************************************************************************* + * Exception thrown doing authentication + * + *******************************************************************************/ +public class QAuthenticationException extends QException +{ + + /******************************************************************************* + ** Constructor of message + ** + *******************************************************************************/ + public QAuthenticationException(String message) + { + super(message); + } + + + + /******************************************************************************* + ** Constructor of message & cause + ** + *******************************************************************************/ + public QAuthenticationException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/src/main/java/com/kingsrook/qqq/backend/core/model/actions/AbstractActionInput.java b/src/main/java/com/kingsrook/qqq/backend/core/model/actions/AbstractActionInput.java index 91c3e6f4..552cba50 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/model/actions/AbstractActionInput.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/model/actions/AbstractActionInput.java @@ -27,9 +27,9 @@ import com.kingsrook.qqq.backend.core.actions.async.AsyncJobCallback; import com.kingsrook.qqq.backend.core.actions.async.AsyncJobStatus; import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException; import com.kingsrook.qqq.backend.core.instances.QInstanceValidator; -import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.session.QSession; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QAuthenticationType.java b/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QAuthenticationType.java new file mode 100644 index 00000000..afc24aaa --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QAuthenticationType.java @@ -0,0 +1,59 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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 . + */ + +package com.kingsrook.qqq.backend.core.model.metadata; + + +/******************************************************************************* + ** Enum to define the possible authentication types + ** + *******************************************************************************/ +@SuppressWarnings("rawtypes") +public enum QAuthenticationType +{ + AUTH_0("auth0"), + FULLY_ANONYMOUS("fullyAnonymous"), + MOCK("mock"); + + private final String name; + + + + /******************************************************************************* + ** enum constructor + *******************************************************************************/ + QAuthenticationType(String name) + { + this.name = name; + } + + + + /******************************************************************************* + ** Getter for name + ** + *******************************************************************************/ + public String getName() + { + return this.name; + } + +} diff --git a/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QInstance.java b/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QInstance.java index af087399..620f9f65 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QInstance.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QInstance.java @@ -32,6 +32,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleVal import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData; /******************************************************************************* diff --git a/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java b/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java index 9a37780c..f0ba6a22 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.backend.core.model.session; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; @@ -29,7 +30,7 @@ import java.util.Map; /******************************************************************************* ** *******************************************************************************/ -public class QSession +public class QSession implements Serializable { private String idReference; private QUser user; diff --git a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/Auth0AuthenticationModule.java b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/Auth0AuthenticationModule.java new file mode 100644 index 00000000..0bf8d5f8 --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/Auth0AuthenticationModule.java @@ -0,0 +1,317 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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 . + */ + +package com.kingsrook.qqq.backend.core.modules.authentication; + + +import java.security.interfaces.RSAPublicKey; +import java.time.Duration; +import java.time.Instant; +import java.util.Base64; +import java.util.Map; +import java.util.Optional; +import com.auth0.jwk.Jwk; +import com.auth0.jwk.JwkException; +import com.auth0.jwk.JwkProvider; +import com.auth0.jwk.UrlJwkProvider; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.exceptions.TokenExpiredException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.JWTVerifier; +import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.session.QSession; +import com.kingsrook.qqq.backend.core.model.session.QUser; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.Auth0AuthenticationMetaData; +import com.kingsrook.qqq.backend.core.state.AbstractStateKey; +import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider; +import com.kingsrook.qqq.backend.core.state.StateProviderInterface; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.json.JSONObject; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class Auth0AuthenticationModule implements QAuthenticationModuleInterface +{ + private static final Logger logger = LogManager.getLogger(Auth0AuthenticationModule.class); + + private static final int ID_TOKEN_VALIDATION_INTERVAL_SECONDS = 300; + + public static final String AUTH0_ID_TOKEN_KEY = "sessionId"; + + public static final String TOKEN_NOT_PROVIDED_ERROR = "Id Token was not provided"; + public static final String COULD_NOT_DECODE_ERROR = "Unable to decode id token"; + public static final String EXPIRED_TOKEN_ERROR = "Token has expired"; + public static final String INVALID_TOKEN_ERROR = "An invalid token was provided"; + + + private Instant now; + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public QSession createSession(QInstance qInstance, Map context) throws QAuthenticationException + { + ////////////////////////////////////////////////// + // get the jwt id token from the context object // + ////////////////////////////////////////////////// + String idToken = context.get(AUTH0_ID_TOKEN_KEY); + if(idToken == null) + { + logger.warn(TOKEN_NOT_PROVIDED_ERROR); + throw (new QAuthenticationException(TOKEN_NOT_PROVIDED_ERROR)); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // decode the token locally to make sure it is valid and to look at when it expires // + ////////////////////////////////////////////////////////////////////////////////////// + try + { + ///////////////////////////////////////////////////// + // try to build session to see if still valid // + // then call method to check more session validity // + ///////////////////////////////////////////////////// + QSession qSession = buildQSessionFromToken(idToken); + if(isSessionValid(qSession)) + { + return (qSession); + } + + /////////////////////////////////////////////////////////////////////////////////////// + // if we make it here it means we have never validated this token or its been a long // + // enough duration so we need to re-verify the token // + /////////////////////////////////////////////////////////////////////////////////////// + qSession = revalidateToken(qInstance, idToken); + + //////////////////////////////////////////////////////////////////// + // put now into state so we dont check until next interval passes // + /////////////////////////////////////////////////////////////////// + StateProviderInterface spi = getStateProvider(); + Auth0StateKey key = new Auth0StateKey(qSession.getIdReference()); + spi.put(key, Instant.now()); + + return (qSession); + } + catch(JWTDecodeException jde) + { + //////////////////////////////// + // could not decode the token // + //////////////////////////////// + logger.warn(COULD_NOT_DECODE_ERROR, jde); + throw (new QAuthenticationException(COULD_NOT_DECODE_ERROR)); + } + catch(TokenExpiredException tee) + { + logger.info(EXPIRED_TOKEN_ERROR, tee); + throw (new QAuthenticationException(EXPIRED_TOKEN_ERROR)); + } + catch(JWTVerificationException | JwkException jve) + { + /////////////////////////////////////////// + // token had invalid signature or claims // + /////////////////////////////////////////// + logger.warn(INVALID_TOKEN_ERROR, jve); + throw (new QAuthenticationException(INVALID_TOKEN_ERROR)); + } + catch(Exception e) + { + //////////////// + // ¯\_(ツ)_/¯ // + //////////////// + String message = "An unknown error occurred"; + logger.error(message, e); + throw (new QAuthenticationException(message)); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public boolean isSessionValid(QSession session) + { + if(session == null) + { + return (false); + } + + StateProviderInterface spi = getStateProvider(); + Auth0StateKey key = new Auth0StateKey(session.getIdReference()); + Optional lastTimeCheckedOptional = spi.get(Instant.class, key); + if(lastTimeCheckedOptional.isPresent()) + { + Instant lastTimeChecked = lastTimeCheckedOptional.get(); + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // returns negative int if less than compared duration, 0 if equal, positive int if greater than // + // - so this is basically saying, if the time between the last time we checked the token and // + // right now is more than ID_TOKEN_VALIDATION_INTERVAL_SECTIONS, then session needs revalidated // + /////////////////////////////////////////////////////////////////////////////////////////////////// + return (Duration.between(lastTimeChecked, Instant.now()).compareTo(Duration.ofSeconds(ID_TOKEN_VALIDATION_INTERVAL_SECONDS)) < 0); + } + + return (false); + } + + + + /******************************************************************************* + ** makes request to check if a token is still valid and build new qSession if it is + ** + *******************************************************************************/ + private QSession revalidateToken(QInstance qInstance, String idToken) throws JwkException + { + Auth0AuthenticationMetaData metaData = (Auth0AuthenticationMetaData) qInstance.getAuthentication(); + + DecodedJWT jwt = JWT.decode(idToken); + JwkProvider provider = new UrlJwkProvider(metaData.getBaseUrl()); + Jwk jwk = provider.get(jwt.getKeyId()); + Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null); + JWTVerifier verifier = JWT.require(algorithm) + .withIssuer(metaData.getBaseUrl()) + .build(); + + /////////////////////////////////// + // make call to verify the token // + /////////////////////////////////// + verifier.verify(idToken); + + return (buildQSessionFromToken(idToken)); + } + + + + /******************************************************************************* + ** extracts info from token creating a QSession + ** + *******************************************************************************/ + private QSession buildQSessionFromToken(String idToken) throws JwkException + { + //////////////////////////////////// + // decode and extract the payload // + //////////////////////////////////// + DecodedJWT jwt = JWT.decode(idToken); + Base64.Decoder decoder = Base64.getUrlDecoder(); + String payloadString = new String(decoder.decode(jwt.getPayload())); + JSONObject payload = new JSONObject(payloadString); + + QUser qUser = new QUser(); + qUser.setFullName(payload.getString("name")); + if(payload.has("email")) + { + qUser.setIdReference(payload.getString("email")); + } + else + { + qUser.setIdReference(payload.getString("nickname")); + } + + QSession qSession = new QSession(); + qSession.setIdReference(idToken); + qSession.setUser(qUser); + + return (qSession); + } + + + + /******************************************************************************* + ** Load an instance of the appropriate state provider + ** + *******************************************************************************/ + public static StateProviderInterface getStateProvider() + { + // TODO - read this from somewhere in meta data eh? + return (InMemoryStateProvider.getInstance()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static class Auth0StateKey extends AbstractStateKey + { + private final String key; + + + + /******************************************************************************* + ** Constructor. + ** + *******************************************************************************/ + Auth0StateKey(String key) + { + this.key = key; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public String toString() + { + return (this.key); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + Auth0StateKey that = (Auth0StateKey) o; + return key.equals(that.key); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public int hashCode() + { + return key.hashCode(); + } + } +} diff --git a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModule.java b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModule.java index ebc573c4..442941b5 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModule.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModule.java @@ -24,9 +24,9 @@ package com.kingsrook.qqq.backend.core.modules.authentication; import java.util.Map; import java.util.UUID; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QUser; -import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface; /******************************************************************************* @@ -40,7 +40,7 @@ public class FullyAnonymousAuthenticationModule implements QAuthenticationModule ** *******************************************************************************/ @Override - public QSession createSession(Map context) + public QSession createSession(QInstance qInstance, Map context) { QUser qUser = new QUser(); qUser.setIdReference("anonymous"); @@ -68,10 +68,6 @@ public class FullyAnonymousAuthenticationModule implements QAuthenticationModule @Override public boolean isSessionValid(QSession session) { - if(session == null) - { - return (false); - } - return (true); + return session != null; } } diff --git a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/MockAuthenticationModule.java b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/MockAuthenticationModule.java index 1bd72ae2..21b25c2a 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/MockAuthenticationModule.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/MockAuthenticationModule.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.modules.authentication; import java.util.Map; import java.util.UUID; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QUser; import org.apache.logging.log4j.LogManager; @@ -43,7 +44,7 @@ public class MockAuthenticationModule implements QAuthenticationModuleInterface ** *******************************************************************************/ @Override - public QSession createSession(Map context) + public QSession createSession(QInstance qInstance, Map context) { QUser qUser = new QUser(); qUser.setIdReference("User:" + (System.currentTimeMillis() % USER_ID_MODULO)); diff --git a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcher.java b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcher.java index 0e32a1d7..258509f2 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcher.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcher.java @@ -25,8 +25,8 @@ package com.kingsrook.qqq.backend.core.modules.authentication; import java.util.HashMap; import java.util.Map; import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException; -import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData; -import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface; +import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData; /******************************************************************************* @@ -38,7 +38,7 @@ import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModu *******************************************************************************/ public class QAuthenticationModuleDispatcher { - private Map authenticationTypeToModuleClassNameMap; + private final Map authenticationTypeToModuleClassNameMap; @@ -48,9 +48,9 @@ public class QAuthenticationModuleDispatcher public QAuthenticationModuleDispatcher() { authenticationTypeToModuleClassNameMap = new HashMap<>(); - authenticationTypeToModuleClassNameMap.put("mock", "com.kingsrook.qqq.backend.core.modules.authentication.MockAuthenticationModule"); - authenticationTypeToModuleClassNameMap.put("fullyAnonymous", "com.kingsrook.qqq.backend.core.modules.authentication.FullyAnonymousAuthenticationModule"); - authenticationTypeToModuleClassNameMap.put("TODO:google", "com.kingsrook.qqq.authentication.module.google.GoogleAuthenticationModule"); + authenticationTypeToModuleClassNameMap.put(QAuthenticationType.MOCK.getName(), "com.kingsrook.qqq.backend.core.modules.authentication.MockAuthenticationModule"); + authenticationTypeToModuleClassNameMap.put(QAuthenticationType.FULLY_ANONYMOUS.getName(), "com.kingsrook.qqq.backend.core.modules.authentication.FullyAnonymousAuthenticationModule"); + authenticationTypeToModuleClassNameMap.put(QAuthenticationType.AUTH_0.getName(), "com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule"); // todo - let user define custom type -> classes } @@ -66,7 +66,7 @@ public class QAuthenticationModuleDispatcher throw (new QModuleDispatchException("No authentication meta data defined.")); } - return getQModule(authenticationMetaData.getType()); + return getQModule(authenticationMetaData.getType().getName()); } diff --git a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java index 8ab7b07c..ec5db589 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java @@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.modules.authentication; import java.util.Map; +import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.session.QSession; @@ -35,7 +37,7 @@ public interface QAuthenticationModuleInterface /******************************************************************************* ** *******************************************************************************/ - QSession createSession(Map context); + QSession createSession(QInstance qInstance, Map context) throws QAuthenticationException; /******************************************************************************* diff --git a/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/metadata/Auth0AuthenticationMetaData.java b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/metadata/Auth0AuthenticationMetaData.java new file mode 100644 index 00000000..ad0b9dd6 --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/metadata/Auth0AuthenticationMetaData.java @@ -0,0 +1,79 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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 . + */ + +package com.kingsrook.qqq.backend.core.modules.authentication.metadata; + + +import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType; + + +/******************************************************************************* + ** Meta-data to provide details of an RDBMS backend (e.g., connection params) + *******************************************************************************/ +public class Auth0AuthenticationMetaData extends QAuthenticationMetaData +{ + private String baseUrl; + + + + /******************************************************************************* + ** Default Constructor. + *******************************************************************************/ + public Auth0AuthenticationMetaData() + { + super(); + setType(QAuthenticationType.AUTH_0); + } + + + + /******************************************************************************* + ** Fluent setter, override to help fluent flows + *******************************************************************************/ + public Auth0AuthenticationMetaData withBaseUrl(String baseUrl) + { + setBaseUrl(baseUrl); + return this; + } + + + + /******************************************************************************* + ** Getter for baseUrl + ** + *******************************************************************************/ + public String getBaseUrl() + { + return baseUrl; + } + + + + /******************************************************************************* + ** Setter for baseUrl + ** + *******************************************************************************/ + public void setBaseUrl(String baseUrl) + { + this.baseUrl = baseUrl; + } + +} diff --git a/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QAuthenticationMetaData.java b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/metadata/QAuthenticationMetaData.java similarity index 93% rename from src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QAuthenticationMetaData.java rename to src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/metadata/QAuthenticationMetaData.java index a14e58ac..7e259de5 100644 --- a/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QAuthenticationMetaData.java +++ b/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/metadata/QAuthenticationMetaData.java @@ -19,12 +19,13 @@ * along with this program. If not, see . */ -package com.kingsrook.qqq.backend.core.model.metadata; +package com.kingsrook.qqq.backend.core.modules.authentication.metadata; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonFilter; +import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType; /******************************************************************************* @@ -35,7 +36,7 @@ import com.fasterxml.jackson.annotation.JsonFilter; public class QAuthenticationMetaData { private String name; - private String type; + private QAuthenticationType type; @JsonFilter("secretsFilter") private Map values; @@ -120,7 +121,7 @@ public class QAuthenticationMetaData ** Getter for type ** *******************************************************************************/ - public String getType() + public QAuthenticationType getType() { return type; } @@ -131,7 +132,7 @@ public class QAuthenticationMetaData ** Setter for type ** *******************************************************************************/ - public void setType(String type) + public void setType(QAuthenticationType type) { this.type = type; } @@ -141,7 +142,7 @@ public class QAuthenticationMetaData /******************************************************************************* ** *******************************************************************************/ - public QAuthenticationMetaData withType(String type) + public QAuthenticationMetaData withType(QAuthenticationType type) { this.type = type; return (this); diff --git a/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/Auth0AuthenticationModuleTest.java b/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/Auth0AuthenticationModuleTest.java new file mode 100644 index 00000000..99cbb6b6 --- /dev/null +++ b/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/Auth0AuthenticationModuleTest.java @@ -0,0 +1,223 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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 . + */ + +package com.kingsrook.qqq.backend.core.modules.authentication; + + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.session.QSession; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.Auth0AuthenticationMetaData; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData; +import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider; +import com.kingsrook.qqq.backend.core.state.StateProviderInterface; +import com.kingsrook.qqq.backend.core.utils.TestUtils; +import org.junit.jupiter.api.Test; +import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.AUTH0_ID_TOKEN_KEY; +import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.COULD_NOT_DECODE_ERROR; +import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.EXPIRED_TOKEN_ERROR; +import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.INVALID_TOKEN_ERROR; +import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.TOKEN_NOT_PROVIDED_ERROR; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + + +/******************************************************************************* + ** Unit test for the FullyAnonymousAuthenticationModule + *******************************************************************************/ +public class Auth0AuthenticationModuleTest +{ + private static final String VALID_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllrY2FkWTA0Q3RFVUFxQUdLNTk3ayJ9.eyJnaXZlbl9uYW1lIjoiVGltIiwiZmFtaWx5X25hbWUiOiJDaGFtYmVybGFpbiIsIm5pY2tuYW1lIjoidGltLmNoYW1iZXJsYWluIiwibmFtZSI6IlRpbSBDaGFtYmVybGFpbiIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUZkWnVjcXVSaUFvTzk1RG9URklnbUtseVA1akVBVnZmWXFnS0lHTkVubzE9czk2LWMiLCJsb2NhbGUiOiJlbiIsInVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDE2OjI0OjQ1LjgyMloiLCJlbWFpbCI6InRpbS5jaGFtYmVybGFpbkBraW5nc3Jvb2suY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8va2luZ3Nyb29rLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODk2NDEyNjE3MjY1NzAzNDg2NyIsImF1ZCI6InNwQ1NtczAzcHpVZGRYN1BocHN4ZDlUd2FLMDlZZmNxIiwiaWF0IjoxNjU4MjQ3OTAyLCJleHAiOjE2NTgyODM5MDIsIm5vbmNlIjoiZUhOdFMxbEtUR2N5ZG5KS1VVY3RkRTFVT0ZKNmJFNUxVVkEwZEdsRGVXOXZkVkl4UW41eVRrUlJlZz09In0.hib7JR8NDU2kx8Fj1bnzo3IUuabE6Hb-Z7HHZAJPQuF_Zdg3L1KDypn6SY7HAd_dsz2N8RkXfvQto-Y2g2ukuz7FxzNFgcVL99cyEO3YqmyCa6JTOTCrxdeaIE8QZpCEKvC28oeJBv0wO1Dwc--OVJMsK2vSzyxj1WNok64YYjWKLL4c0dFf-nj0KWFr1IU-tMiyWLDDiJw2Sa8M4YxXZYqdlkgNmrBPExgcm9l9SiT2l3Ts3Sgc_IyMVyMrnV8XX50EWdsm6vuCOSUcqf0XhjDQ7urZveoVwVLnYq3GcLhVBcy1Hr9RL8zPdPynOzsbX6uCww2Esrv6iwWrgQ5zBA"; + private static final String INVALID_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllrY2FkWTA0Q3RFVUFxQUdLNTk3ayJ9.eyJnaXZlbl9uYW1lIjoiVGltIiwiZmFtaWx5X25hbWUiOiJDaGFtYmVybGFpbiIsIm5pY2tuYW1lIjoidGltLmNoYW1iZXJsYWluIiwibmFtZSI6IlRpbSBDaGFtYmVybGFpbiIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUZkWnVjcXVSaUFvTzk1RG9URklnbUtseVA1akVBVnZmWXFnS0lHTkVubzE9czk2LWMiLCJsb2NhbGUiOiJlbiIsInVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDE2OjI0OjQ1LjgyMloiLCJlbWFpbCI6InRpbS5jaGFtYmVybGFpbkBraW5nc3Jvb2suY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8va2luZ3Nyb29rLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODk2NDEyNjE3MjY1NzAzNDg2NyIsImF1ZCI6InNwQ1NtczAzcHpVZGRYN1BocHN4ZDlUd2FLMDlZZmNxIiwiaWF0IjoxNjU4MjQ3OTAyLCJleHAiOjE2NTgyODM5MDIsIm5vbmNlIjoiZUhOdFMxbEtUR2N5ZG5KS1VVY3RkRTFVT0ZKNmJFNUxVVkEwZEdsRGVXOXZkVkl4UW41eVRrUlJlZz09In0.hib7JR8NDU2kx8Fj1bnzo3IUuabE6Hb-Z7HHZAJPQuF_Zdg3L1KDypn6SY7HAd_dsz2N8RkXfvQto-Y2g2ukuz7FxzNFgcVL99cyEO3YqmyCa6JTOTCrxdeaIE8QZpCEKvC28oeJBv0wO1Dwc--OVJMsK2vSzyxj1WNok64YYjWKLL4c0dFf-nj0KWFr1IU-tMiyWLDDiJw2Sa8M4YxXZYqdlkgNmrBPExgcm9l9SiT2l3Ts3Sgc_IyMVyMrnV8XX50EWdsm6vuCOSUcqf0XhjDQ7urZveoVwVLnYq3GcLhVBcy1Hr9RL8zPdPynOzsbX6uCww2Esrv6iwWrgQ5zBA-thismakesinvalid"; + private static final String EXPIRED_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllrY2FkWTA0Q3RFVUFxQUdLNTk3ayJ9.eyJnaXZlbl9uYW1lIjoiVGltIiwiZmFtaWx5X25hbWUiOiJDaGFtYmVybGFpbiIsIm5pY2tuYW1lIjoidGltLmNoYW1iZXJsYWluIiwibmFtZSI6IlRpbSBDaGFtYmVybGFpbiIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUZkWnVjcXVSaUFvTzk1RG9URklnbUtseVA1akVBVnZmWXFnS0lHTkVubzE9czk2LWMiLCJsb2NhbGUiOiJlbiIsInVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE4VDIxOjM4OjE1LjM4NloiLCJlbWFpbCI6InRpbS5jaGFtYmVybGFpbkBraW5nc3Jvb2suY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8va2luZ3Nyb29rLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODk2NDEyNjE3MjY1NzAzNDg2NyIsImF1ZCI6InNwQ1NtczAzcHpVZGRYN1BocHN4ZDlUd2FLMDlZZmNxIiwiaWF0IjoxNjU4MTgwNDc3LCJleHAiOjE2NTgyMTY0NzcsIm5vbmNlIjoiVkZkQlYzWmplR2hvY1cwMk9WZEtabHBLU0c1K1ZXbElhMEV3VkZaeFpVdEJVMDErZUZaT1RtMTNiZz09In0.fU7EwUgNrupOPz_PX_aQKON2xG1-LWD85xVo1Bn41WNEek-iMyJoch8l6NUihi7Bou14BoOfeWIG_sMqsLHqI2Pk7el7l1kigsjURx0wpiXadBt8piMxdIlxdToZEMuZCBzg7eJvXh4sM8tlV5cm0gPa6FT9Ih3VGJajNlXi5BcYS_JRpIvFvHn8-Bxj4KiAlZ5XPPkopjnDgP8kFfc4cMn_nxDkqWYlhj-5TaGW2xCLC9Qr_9UNxX0fm-CkKjYs3Z5ezbiXNkc-bxrCYvxeBeDPf8-T3EqrxCRVqCZSJ85BHdOc_E7UZC_g8bNj0umoplGwlCbzO4XIuOO-KlIaOg"; + private static final String UNDECODABLE_TOKEN = "UNDECODABLE"; + + public static final String AUTH0_BASE_URL = "https://kingsrook.us.auth0.com/"; + + + + /******************************************************************************* + ** Test a valid token where 'now' is set to a time that would be valid for it + ** + *******************************************************************************/ + @Test + public void testLastTimeChecked() throws QAuthenticationException + { + ////////////////////////////////////////////////////////// + // Tuesday, July 19, 2022 12:40:27.299 PM GMT-05:00 DST // + ////////////////////////////////////////////////////////// + Instant now = Instant.now(); + + ///////////////////////////////////////////////////////// + // put the 'now' from the past into the state provider // + ///////////////////////////////////////////////////////// + StateProviderInterface spi = InMemoryStateProvider.getInstance(); + Auth0AuthenticationModule.Auth0StateKey key = new Auth0AuthenticationModule.Auth0StateKey(VALID_TOKEN); + spi.put(key, now); + + ////////////////////// + // build up session // + ////////////////////// + QSession session = new QSession(); + session.setIdReference(VALID_TOKEN); + + Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule(); + assertEquals(true, auth0AuthenticationModule.isSessionValid(session), "Session should return as still valid."); + } + + + + /******************************************************************************* + ** Test failure case, token is invalid + ** + *******************************************************************************/ + @Test + public void testInvalidToken() + { + Map context = new HashMap<>(); + context.put(AUTH0_ID_TOKEN_KEY, INVALID_TOKEN); + + try + { + Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule(); + auth0AuthenticationModule.createSession(getQInstance(), context); + fail("Should never get here"); + } + catch(QAuthenticationException qae) + { + assertThat(qae.getMessage()).contains(INVALID_TOKEN_ERROR); + } + } + + + + /******************************************************************************* + ** Test failure case, token cant be decoded + ** + *******************************************************************************/ + @Test + public void testUndecodableToken() + { + Map context = new HashMap<>(); + context.put(AUTH0_ID_TOKEN_KEY, UNDECODABLE_TOKEN); + + try + { + Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule(); + auth0AuthenticationModule.createSession(getQInstance(), context); + fail("Should never get here"); + } + catch(QAuthenticationException qae) + { + assertThat(qae.getMessage()).contains(COULD_NOT_DECODE_ERROR); + } + } + + + + /******************************************************************************* + ** Test failure case, token is expired + ** + *******************************************************************************/ + @Test + public void testProperlyFormattedButExpiredToken() + { + Map context = new HashMap<>(); + context.put(AUTH0_ID_TOKEN_KEY, EXPIRED_TOKEN); + + try + { + Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule(); + auth0AuthenticationModule.createSession(getQInstance(), context); + fail("Should never get here"); + } + catch(QAuthenticationException qae) + { + assertThat(qae.getMessage()).contains(EXPIRED_TOKEN_ERROR); + } + } + + + + /******************************************************************************* + ** Test failure case, empty context + ** + *******************************************************************************/ + @Test + public void testEmptyContext() + { + try + { + Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule(); + auth0AuthenticationModule.createSession(getQInstance(), new HashMap<>()); + fail("Should never get here"); + } + catch(QAuthenticationException qae) + { + assertThat(qae.getMessage()).contains(TOKEN_NOT_PROVIDED_ERROR); + } + } + + + + /******************************************************************************* + ** Test failure case, null token + ** + *******************************************************************************/ + @Test + public void testNullToken() + { + Map context = new HashMap<>(); + context.put(AUTH0_ID_TOKEN_KEY, null); + + try + { + Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule(); + auth0AuthenticationModule.createSession(getQInstance(), context); + fail("Should never get here"); + } + catch(QAuthenticationException qae) + { + assertThat(qae.getMessage()).contains(TOKEN_NOT_PROVIDED_ERROR); + } + } + + + + /******************************************************************************* + ** utility method to prime a qInstance for auth0 tests + ** + *******************************************************************************/ + private QInstance getQInstance() + { + QAuthenticationMetaData authenticationMetaData = new Auth0AuthenticationMetaData() + .withBaseUrl(AUTH0_BASE_URL) + .withName("auth0"); + + QInstance qInstance = TestUtils.defineInstance(); + qInstance.setAuthentication(authenticationMetaData); + return (qInstance); + } +} diff --git a/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModuleTest.java b/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModuleTest.java index 7f46b9f7..68c1e7d8 100644 --- a/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModuleTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/FullyAnonymousAuthenticationModuleTest.java @@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.modules.authentication; import com.kingsrook.qqq.backend.core.model.session.QSession; -import com.kingsrook.qqq.backend.core.modules.authentication.FullyAnonymousAuthenticationModule; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -36,12 +35,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class FullyAnonymousAuthenticationModuleTest { + /******************************************************************************* + ** + *******************************************************************************/ @Test public void test() { FullyAnonymousAuthenticationModule fullyAnonymousAuthenticationModule = new FullyAnonymousAuthenticationModule(); - QSession session = fullyAnonymousAuthenticationModule.createSession(null); + QSession session = fullyAnonymousAuthenticationModule.createSession(null, null); assertNotNull(session, "Session should not be null"); assertNotNull(session.getIdReference(), "Session id ref should not be null"); @@ -51,4 +53,4 @@ public class FullyAnonymousAuthenticationModuleTest assertFalse(fullyAnonymousAuthenticationModule.isSessionValid(null), "null should be not valid"); } -} \ No newline at end of file +} diff --git a/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcherTest.java b/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcherTest.java index 9b366a3c..44a0ae3b 100644 --- a/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcherTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleDispatcherTest.java @@ -23,9 +23,7 @@ package com.kingsrook.qqq.backend.core.modules.authentication; import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException; -import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData; -import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleDispatcher; -import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.utils.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java b/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java index 76c4e30b..379caad5 100644 --- a/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java +++ b/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java @@ -31,8 +31,9 @@ import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType; import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep; -import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData; +import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType; @@ -114,7 +115,7 @@ public class TestUtils { return new QAuthenticationMetaData() .withName("mock") - .withType("mock"); + .withType(QAuthenticationType.MOCK); } @@ -309,7 +310,7 @@ public class TestUtils public static QSession getMockSession() { MockAuthenticationModule mockAuthenticationModule = new MockAuthenticationModule(); - return (mockAuthenticationModule.createSession(null)); + return (mockAuthenticationModule.createSession(null, null)); }