Merge branch 'feature/CTLE-421-migrate-to-use-api-keys' into integration/sprint-25

This commit is contained in:
Tim Chamberlain
2023-05-02 15:16:05 -05:00
4 changed files with 181 additions and 39 deletions

View File

@ -334,6 +334,8 @@ public class GetAction
queryInput.setIncludeAssociations(getInput.getIncludeAssociations()); queryInput.setIncludeAssociations(getInput.getIncludeAssociations());
queryInput.setAssociationNamesToInclude(getInput.getAssociationNamesToInclude()); queryInput.setAssociationNamesToInclude(getInput.getAssociationNamesToInclude());
queryInput.setShouldFetchHeavyFields(getInput.getShouldFetchHeavyFields()); queryInput.setShouldFetchHeavyFields(getInput.getShouldFetchHeavyFields());
queryInput.setShouldMaskPasswords(getInput.getShouldMaskPasswords());
queryInput.setShouldOmitHiddenFields(getInput.getShouldOmitHiddenFields());
QueryOutput queryOutput = new QueryAction().execute(queryInput); QueryOutput queryOutput = new QueryAction().execute(queryInput);

View File

@ -57,7 +57,7 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData
///////////////////////////////////////// /////////////////////////////////////////
private String applicationNameField; private String applicationNameField;
private String auth0ClientIdField; private String auth0ClientIdField;
private String auth0ClientSecretMaskedField; private String auth0ClientSecretField;
private Serializable qqqRecordIdField; private Serializable qqqRecordIdField;
@ -67,6 +67,7 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData
private String clientAuth0ApplicationIdField; private String clientAuth0ApplicationIdField;
private String auth0AccessTokenField; private String auth0AccessTokenField;
private String qqqAccessTokenField; private String qqqAccessTokenField;
private String qqqApiKeyField;
private String expiresInSecondsField; 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 ** Getter for clientAuth0ApplicationIdField
** **
@ -555,4 +522,66 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData
return (this); 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);
}
} }

View File

@ -48,19 +48,23 @@ 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.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;
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.AccessTokenException; import com.kingsrook.qqq.backend.core.exceptions.AccessTokenException;
import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger; 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.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; 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.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; 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.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; 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.data.QRecord;
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;
@ -101,6 +105,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
public static final int ID_TOKEN_VALIDATION_INTERVAL_SECONDS = 1800; public static final int ID_TOKEN_VALIDATION_INTERVAL_SECONDS = 1800;
public static final String AUTH0_ACCESS_TOKEN_KEY = "sessionId"; 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 BASIC_AUTH_KEY = "basicAuthString";
public static final String TOKEN_NOT_PROVIDED_ERROR = "Access Token was not provided"; 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); 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) if(accessToken == null)
{ {
LOG.warn(TOKEN_NOT_PROVIDED_ERROR); 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 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. ** Look up client_auth0_application record, return if found.
** **

View File

@ -134,9 +134,10 @@ public class QJavalinImplementation
{ {
private static final QLogger LOG = QLogger.getLogger(QJavalinImplementation.class); private static final QLogger LOG = QLogger.getLogger(QJavalinImplementation.class);
private static final int SESSION_COOKIE_AGE = 60 * 60 * 24; public static final int SESSION_COOKIE_AGE = 60 * 60 * 24;
private static final String SESSION_ID_COOKIE_NAME = "sessionId"; public static final String SESSION_ID_COOKIE_NAME = "sessionId";
private static final String BASIC_AUTH_NAME = "basicAuthString"; public static final String BASIC_AUTH_NAME = "basicAuthString";
public static final String API_KEY_NAME = "apiKey";
static QInstance qInstance; static QInstance qInstance;
static QJavalinMetaData javalinMetaData; static QJavalinMetaData javalinMetaData;
@ -422,6 +423,7 @@ public class QJavalinImplementation
String sessionIdCookieValue = context.cookie(SESSION_ID_COOKIE_NAME); String sessionIdCookieValue = context.cookie(SESSION_ID_COOKIE_NAME);
String authorizationHeaderValue = context.header("Authorization"); String authorizationHeaderValue = context.header("Authorization");
String apiKeyHeaderValue = context.header("x-api-key");
if(StringUtils.hasContent(sessionIdCookieValue)) if(StringUtils.hasContent(sessionIdCookieValue))
{ {
@ -430,6 +432,14 @@ public class QJavalinImplementation
//////////////////////////////////////// ////////////////////////////////////////
authenticationContext.put(SESSION_ID_COOKIE_NAME, sessionIdCookieValue); 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) else if(authorizationHeaderValue != null)
{ {
///////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////