From d300ec162d983da848219eae072235366e01a589 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Tue, 2 May 2023 15:15:49 -0500 Subject: [PATCH] CTLE-421: checkpoint commit --- .../core/actions/tables/GetAction.java | 2 + .../Auth0AuthenticationMetaData.java | 99 +++++++++++------ .../Auth0AuthenticationModule.java | 103 +++++++++++++++++- .../javalin/QJavalinImplementation.java | 16 ++- 4 files changed, 181 insertions(+), 39 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/GetAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/GetAction.java index 6f377bbb..0d16b5c9 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/GetAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/GetAction.java @@ -334,6 +334,8 @@ public class GetAction queryInput.setIncludeAssociations(getInput.getIncludeAssociations()); queryInput.setAssociationNamesToInclude(getInput.getAssociationNamesToInclude()); queryInput.setShouldFetchHeavyFields(getInput.getShouldFetchHeavyFields()); + queryInput.setShouldMaskPasswords(getInput.getShouldMaskPasswords()); + queryInput.setShouldOmitHiddenFields(getInput.getShouldOmitHiddenFields()); QueryOutput queryOutput = new QueryAction().execute(queryInput); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/authentication/Auth0AuthenticationMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/authentication/Auth0AuthenticationMetaData.java index 3cf6eabb..c0bc5e09 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/authentication/Auth0AuthenticationMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/authentication/Auth0AuthenticationMetaData.java @@ -57,7 +57,7 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData ///////////////////////////////////////// private String applicationNameField; private String auth0ClientIdField; - private String auth0ClientSecretMaskedField; + private String auth0ClientSecretField; private Serializable qqqRecordIdField; @@ -67,6 +67,7 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData private String clientAuth0ApplicationIdField; private String auth0AccessTokenField; private String qqqAccessTokenField; + private String qqqApiKeyField; private String expiresInSecondsField; @@ -387,40 +388,6 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData - /******************************************************************************* - ** Getter for auth0ClientSecretMaskedField - ** - *******************************************************************************/ - public String getAuth0ClientSecretMaskedField() - { - return auth0ClientSecretMaskedField; - } - - - - /******************************************************************************* - ** Setter for auth0ClientSecretMaskedField - ** - *******************************************************************************/ - public void setAuth0ClientSecretMaskedField(String auth0ClientSecretMaskedField) - { - this.auth0ClientSecretMaskedField = auth0ClientSecretMaskedField; - } - - - - /******************************************************************************* - ** Fluent setter for auth0ClientSecretMaskedField - ** - *******************************************************************************/ - public Auth0AuthenticationMetaData withAuth0ClientSecretMaskedField(String auth0ClientSecretMaskedField) - { - this.auth0ClientSecretMaskedField = auth0ClientSecretMaskedField; - return (this); - } - - - /******************************************************************************* ** Getter for clientAuth0ApplicationIdField ** @@ -555,4 +522,66 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData return (this); } + + + /******************************************************************************* + ** Getter for qqqApiKeyField + *******************************************************************************/ + public String getQqqApiKeyField() + { + return (this.qqqApiKeyField); + } + + + + /******************************************************************************* + ** Setter for qqqApiKeyField + *******************************************************************************/ + public void setQqqApiKeyField(String qqqApiKeyField) + { + this.qqqApiKeyField = qqqApiKeyField; + } + + + + /******************************************************************************* + ** Fluent setter for qqqApiKeyField + *******************************************************************************/ + public Auth0AuthenticationMetaData withQqqApiKeyField(String qqqApiKeyField) + { + this.qqqApiKeyField = qqqApiKeyField; + return (this); + } + + + + /******************************************************************************* + ** Getter for auth0ClientSecretField + *******************************************************************************/ + public String getAuth0ClientSecretField() + { + return (this.auth0ClientSecretField); + } + + + + /******************************************************************************* + ** Setter for auth0ClientSecretField + *******************************************************************************/ + public void setAuth0ClientSecretField(String auth0ClientSecretField) + { + this.auth0ClientSecretField = auth0ClientSecretField; + } + + + + /******************************************************************************* + ** Fluent setter for auth0ClientSecretField + *******************************************************************************/ + public Auth0AuthenticationMetaData withAuth0ClientSecretField(String auth0ClientSecretField) + { + this.auth0ClientSecretField = auth0ClientSecretField; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java index 0960faed..68b0dd0a 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java @@ -48,19 +48,23 @@ import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.net.Response; +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.QueryAction; +import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction; import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.AccessTokenException; import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.logging.QLogger; +import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; 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.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData; @@ -101,6 +105,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface public static final int ID_TOKEN_VALIDATION_INTERVAL_SECONDS = 1800; public static final String AUTH0_ACCESS_TOKEN_KEY = "sessionId"; + public static final String API_KEY = "apiKey"; public static final String BASIC_AUTH_KEY = "basicAuthString"; public static final String TOKEN_NOT_PROVIDED_ERROR = "Access Token was not provided"; @@ -185,6 +190,18 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface accessToken = lookupActualAccessToken(metaData, accessToken); } + //////////////////////////////////////////////////////// + // if access token is still null, look for an api key // + //////////////////////////////////////////////////////// + if(accessToken == null) + { + String apiKey = context.get(API_KEY); + if(apiKey != null) + { + accessToken = getAccessTokenFromApiKey(metaData, apiKey); + } + } + if(accessToken == null) { LOG.warn(TOKEN_NOT_PROVIDED_ERROR); @@ -603,7 +620,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface /******************************************************************************* - ** Load an instance of the appropriate state provider + ** create a new auth0 access token ** *******************************************************************************/ public String createAccessToken(QAuthenticationMetaData metaData, String clientId, String clientSecret) throws AccessTokenException @@ -759,6 +776,90 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface + /******************************************************************************* + ** Look up access_token from api key + ** + *******************************************************************************/ + String getAccessTokenFromApiKey(Auth0AuthenticationMetaData metaData, String apiKey) + { + String accessToken = null; + QSession beforeSession = QContext.getQSession(); + + try + { + QContext.setQSession(getChickenAndEggSession()); + + ////////////////////////////////////////////////////////////////////////////////////// + // try to look up existing auth0 application from database, insert one if not found // + ////////////////////////////////////////////////////////////////////////////////////// + QueryInput queryInput = new QueryInput(); + queryInput.setTableName(metaData.getAccessTokenTableName()); + queryInput.setShouldOmitHiddenFields(false); + queryInput.setShouldMaskPasswords(false); + queryInput.setFilter(new QQueryFilter(new QFilterCriteria(metaData.getQqqApiKeyField(), QCriteriaOperator.EQUALS, apiKey))); + QueryOutput queryOutput = new QueryAction().execute(queryInput); + + if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords())) + { + QRecord clientAuth0ApiKey = queryOutput.getRecords().get(0); + accessToken = clientAuth0ApiKey.getValueString(metaData.getAuth0AccessTokenField()); + + //////////////////////////////////////////////////////////// + // decode the accessToken and make sure it is not expired // + //////////////////////////////////////////////////////////// + boolean needNewToken = true; + if(accessToken != null) + { + DecodedJWT jwt = JWT.decode(accessToken); + String payload = jwt.getPayload(); + System.out.println("IOK"); + needNewToken = false; + } + + if(needNewToken) + { + /////////////////////////////////////////////// + // get id/secret from the application record // + /////////////////////////////////////////////// + GetInput getInput = new GetInput(); + getInput.setTableName(metaData.getClientAuth0ApplicationTableName()); + getInput.setShouldOmitHiddenFields(false); + getInput.setShouldMaskPasswords(false); + getInput.setPrimaryKey(clientAuth0ApiKey.getValueString(metaData.getClientAuth0ApplicationIdField())); + + /////////////////////////////// + // create a new access token // + /////////////////////////////// + QRecord clientAuth0Application = new GetAction().execute(getInput).getRecord(); + String clientId = clientAuth0Application.getValueString(metaData.getAuth0ClientIdField()); + String clientSecret = clientAuth0Application.getValueString(metaData.getAuth0ClientSecretField()); + accessToken = createAccessToken(metaData, clientId, clientSecret); + + ////////////////////////////////////////////////////////// + // update the api key record and store new access token // + ////////////////////////////////////////////////////////// + clientAuth0ApiKey.setValue(metaData.getAuth0AccessTokenField(), accessToken); + UpdateInput updateInput = new UpdateInput(); + updateInput.setTableName(metaData.getAccessTokenTableName()); + updateInput.setRecords(List.of(clientAuth0ApiKey)); + new UpdateAction().execute(updateInput); + } + } + } + catch(Exception e) + { + LOG.warn("Could not find Auth0 access token for provided qqq API Key", e); + } + finally + { + QContext.setQSession(beforeSession); + } + + return (accessToken); + } + + + /******************************************************************************* ** Look up client_auth0_application record, return if found. ** diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java index cb6adfff..da8ee4ea 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java @@ -134,9 +134,10 @@ public class QJavalinImplementation { private static final QLogger LOG = QLogger.getLogger(QJavalinImplementation.class); - private static final int SESSION_COOKIE_AGE = 60 * 60 * 24; - private static final String SESSION_ID_COOKIE_NAME = "sessionId"; - private static final String BASIC_AUTH_NAME = "basicAuthString"; + public static final int SESSION_COOKIE_AGE = 60 * 60 * 24; + public static final String SESSION_ID_COOKIE_NAME = "sessionId"; + public static final String BASIC_AUTH_NAME = "basicAuthString"; + public static final String API_KEY_NAME = "apiKey"; static QInstance qInstance; static QJavalinMetaData javalinMetaData; @@ -422,6 +423,7 @@ public class QJavalinImplementation String sessionIdCookieValue = context.cookie(SESSION_ID_COOKIE_NAME); String authorizationHeaderValue = context.header("Authorization"); + String apiKeyHeaderValue = context.header("x-api-key"); if(StringUtils.hasContent(sessionIdCookieValue)) { @@ -430,6 +432,14 @@ public class QJavalinImplementation //////////////////////////////////////// authenticationContext.put(SESSION_ID_COOKIE_NAME, sessionIdCookieValue); } + else if(apiKeyHeaderValue != null) + { + ///////////////////////////////////////////////////////////////// + // next, look for an api key header: // + // this will be used to look up auth0 values via an auth table // + ///////////////////////////////////////////////////////////////// + authenticationContext.put(API_KEY_NAME, apiKeyHeaderValue); + } else if(authorizationHeaderValue != null) { /////////////////////////////////////////////////////////////////////////////////////////////////