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

This commit is contained in:
2023-05-01 20:00:16 -05:00
20 changed files with 413 additions and 20 deletions

View File

@ -56,6 +56,10 @@
<groupId>software.amazon.awssdk</groupId> <groupId>software.amazon.awssdk</groupId>
<artifactId>quicksight</artifactId> <artifactId>quicksight</artifactId>
</dependency> </dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apigateway</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.amazonaws</groupId> <groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-secretsmanager</artifactId> <artifactId>aws-java-sdk-secretsmanager</artifactId>
@ -100,8 +104,18 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.auth0</groupId> <groupId>com.auth0</groupId>
<artifactId>mvc-auth-commons</artifactId> <artifactId>auth0</artifactId>
<version>1.9.2</version> <version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.22.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.github.cdimascio</groupId> <groupId>io.github.cdimascio</groupId>

View File

@ -0,0 +1,65 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.customizers;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
**
*******************************************************************************/
public abstract class AbstractPreInsertCustomizer
{
protected InsertInput insertInput;
/*******************************************************************************
**
*******************************************************************************/
public abstract List<QRecord> apply(List<QRecord> records);
/*******************************************************************************
** Getter for insertInput
**
*******************************************************************************/
public InsertInput getInsertInput()
{
return insertInput;
}
/*******************************************************************************
** Setter for insertInput
**
*******************************************************************************/
public void setInsertInput(InsertInput insertInput)
{
this.insertInput = insertInput;
}
}

View File

@ -29,16 +29,10 @@ package com.kingsrook.qqq.backend.core.actions.customizers;
** Works with TableCustomizer (singular version of this name) objects, during ** Works with TableCustomizer (singular version of this name) objects, during
** instance validation, to provide validation of the referenced code (and to ** instance validation, to provide validation of the referenced code (and to
** make such validation from sub-backend-modules possible in the future). ** make such validation from sub-backend-modules possible in the future).
**
** The idea of the 3rd argument here is to provide a way that we can enforce
** the type-parameters for the custom code. E.g., if it's a Function - how
** can we check at run-time that the type-params are correct? We couldn't find
** how to do this "reflectively", so we can instead try to run the custom code,
** passing it objects of the type that this customizer expects, and a validation
** error will raise upon ClassCastException... This maybe could improve!
*******************************************************************************/ *******************************************************************************/
public enum TableCustomizers public enum TableCustomizers
{ {
PRE_INSERT_RECORD(new TableCustomizer("preInsertRecord", AbstractPreInsertCustomizer.class)),
POST_QUERY_RECORD(new TableCustomizer("postQueryRecord", AbstractPostQueryCustomizer.class)), POST_QUERY_RECORD(new TableCustomizer("postQueryRecord", AbstractPostQueryCustomizer.class)),
POST_INSERT_RECORD(new TableCustomizer("postInsertRecord", AbstractPostInsertCustomizer.class)); POST_INSERT_RECORD(new TableCustomizer("postInsertRecord", AbstractPostInsertCustomizer.class));

View File

@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.interfaces.GetInterface; import com.kingsrook.qqq.backend.core.actions.interfaces.GetInterface;
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator; import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter; import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.LogPair; import com.kingsrook.qqq.backend.core.logging.LogPair;
import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.logging.QLogger;
@ -51,6 +52,8 @@ 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.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
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.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase; import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher; import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
@ -372,6 +375,29 @@ public class GetAction
QValueFormatter.setDisplayValuesInRecords(getInput.getTable(), List.of(returnRecord)); QValueFormatter.setDisplayValuesInRecords(getInput.getTable(), List.of(returnRecord));
} }
if(getInput.getShouldOmitHiddenFields() || getInput.getShouldMaskPasswords())
{
Map<String, QFieldMetaData> fields = QContext.getQInstance().getTable(getInput.getTableName()).getFields();
for(String fieldName : fields.keySet())
{
QFieldType fieldType = fields.get(fieldName).getType();
if(fieldType != null && fieldType.needsMasked())
{
//////////////////////////////////////////////////////////////////////
// empty out the value completely first (which will remove from //
// display fields as well) then update display value if flag is set //
//////////////////////////////////////////////////////////////////////
returnRecord.removeValue(fieldName);
returnRecord.setValue(fieldName, "************");
if(getInput.getShouldGenerateDisplayValues())
{
returnRecord.setDisplayValue(fieldName, record.getValueString(fieldName));
}
}
}
QValueFormatter.setDisplayValuesInRecords(getInput.getTable(), List.of(returnRecord));
}
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// note - shouldFetchHeavyFields should be handled by the underlying action // // note - shouldFetchHeavyFields should be handled by the underlying action //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////

View File

@ -38,6 +38,7 @@ import com.kingsrook.qqq.backend.core.actions.audits.DMLAuditAction;
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus; import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater; import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostInsertCustomizer; import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostInsertCustomizer;
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPreInsertCustomizer;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader; import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers; import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.tables.helpers.UniqueKeyHelper; import com.kingsrook.qqq.backend.core.actions.tables.helpers.UniqueKeyHelper;
@ -86,6 +87,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
throw (new QException("Error: Undefined table: " + insertInput.getTableName())); throw (new QException("Error: Undefined table: " + insertInput.getTableName()));
} }
Optional<AbstractPreInsertCustomizer> preInsertCustomizer = QCodeLoader.getTableCustomizer(AbstractPreInsertCustomizer.class, table, TableCustomizers.PRE_INSERT_RECORD.getRole());
Optional<AbstractPostInsertCustomizer> postInsertCustomizer = QCodeLoader.getTableCustomizer(AbstractPostInsertCustomizer.class, table, TableCustomizers.POST_INSERT_RECORD.getRole()); Optional<AbstractPostInsertCustomizer> postInsertCustomizer = QCodeLoader.getTableCustomizer(AbstractPostInsertCustomizer.class, table, TableCustomizers.POST_INSERT_RECORD.getRole());
setAutomationStatusField(insertInput); setAutomationStatusField(insertInput);
@ -97,6 +99,12 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
validateRequiredFields(insertInput); validateRequiredFields(insertInput);
ValidateRecordSecurityLockHelper.validateSecurityFields(insertInput.getTable(), insertInput.getRecords(), ValidateRecordSecurityLockHelper.Action.INSERT); ValidateRecordSecurityLockHelper.validateSecurityFields(insertInput.getTable(), insertInput.getRecords(), ValidateRecordSecurityLockHelper.Action.INSERT);
if(preInsertCustomizer.isPresent())
{
preInsertCustomizer.get().setInsertInput(insertInput);
insertInput.setRecords(preInsertCustomizer.get().apply(insertInput.getRecords()));
}
InsertOutput insertOutput = qModule.getInsertInterface().execute(insertInput); InsertOutput insertOutput = qModule.getInsertInterface().execute(insertInput);
List<String> errors = insertOutput.getRecords().stream().flatMap(r -> r.getErrors().stream()).toList(); List<String> errors = insertOutput.getRecords().stream().flatMap(r -> r.getErrors().stream()).toList();
if(CollectionUtils.nullSafeHasContents(errors)) if(CollectionUtils.nullSafeHasContents(errors))

View File

@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.ActionHelper;
@ -45,6 +46,7 @@ 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.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn; import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association; import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
@ -247,5 +249,59 @@ public class QueryAction
{ {
QValueFormatter.setDisplayValuesInRecords(queryInput.getTable(), records); QValueFormatter.setDisplayValuesInRecords(queryInput.getTable(), records);
} }
//////////////////////////////
// mask any password fields //
//////////////////////////////
if(queryInput.getShouldOmitHiddenFields() || queryInput.getShouldMaskPasswords())
{
Set<String> maskedFields = new HashSet<>();
Set<String> hiddenFields = new HashSet<>();
//////////////////////////////////////////////////
// build up sets of passwords and hidden fields //
//////////////////////////////////////////////////
Map<String, QFieldMetaData> fields = QContext.getQInstance().getTable(queryInput.getTableName()).getFields();
for(String fieldName : fields.keySet())
{
QFieldMetaData field = fields.get(fieldName);
if(queryInput.getShouldOmitHiddenFields() && field.getIsHidden())
{
hiddenFields.add(fieldName);
}
else if(field.getType() != null && field.getType().needsMasked())
{
maskedFields.add(fieldName);
}
}
/////////////////////////////////////////////////////
// iterate over records replacing values with mask //
/////////////////////////////////////////////////////
for(QRecord record : records)
{
/////////////////////////
// clear hidden fields //
/////////////////////////
for(String hiddenFieldName : hiddenFields)
{
record.removeValue(hiddenFieldName);
}
for(String maskedFieldName : maskedFields)
{
//////////////////////////////////////////////////////////////////////
// empty out the value completely first (which will remove from //
// display fields as well) then update display value if flag is set //
//////////////////////////////////////////////////////////////////////
record.removeValue(maskedFieldName);
record.setValue(maskedFieldName, "************");
if(queryInput.getShouldGenerateDisplayValues())
{
record.setDisplayValue(maskedFieldName, record.getValueString(maskedFieldName));
}
}
}
}
} }
} }

View File

@ -955,7 +955,8 @@ public class QInstanceEnricher
{ {
for(String fieldName : table.getFields().keySet()) for(String fieldName : table.getFields().keySet())
{ {
if(!usedFieldNames.contains(fieldName)) QFieldMetaData field = table.getField(fieldName);
if(!field.getIsHidden() && !usedFieldNames.contains(fieldName))
{ {
otherSection.getFieldNames().add(fieldName); otherSection.getFieldNames().add(fieldName);
usedFieldNames.add(fieldName); usedFieldNames.add(fieldName);

View File

@ -437,7 +437,14 @@ public class QInstanceValidator
for(String fieldName : CollectionUtils.nonNullMap(table.getFields()).keySet()) for(String fieldName : CollectionUtils.nonNullMap(table.getFields()).keySet())
{ {
assertCondition(fieldNamesInSections.contains(fieldName), "Table " + tableName + " field " + fieldName + " is not listed in any field sections."); if(table.getField(fieldName).getIsHidden())
{
assertCondition(!fieldNamesInSections.contains(fieldName), "Table " + tableName + " field " + fieldName + " is listed in a field section, but it is marked as hidden.");
}
else
{
assertCondition(fieldNamesInSections.contains(fieldName), "Table " + tableName + " field " + fieldName + " is not listed in any field sections.");
}
} }
if(table.getRecordLabelFields() != null && table.getFields() != null) if(table.getRecordLabelFields() != null && table.getFields() != null)

View File

@ -43,6 +43,8 @@ public class GetInput extends AbstractTableActionInput
private boolean shouldTranslatePossibleValues = false; private boolean shouldTranslatePossibleValues = false;
private boolean shouldGenerateDisplayValues = false; private boolean shouldGenerateDisplayValues = false;
private boolean shouldFetchHeavyFields = true; private boolean shouldFetchHeavyFields = true;
private boolean shouldOmitHiddenFields = true;
private boolean shouldMaskPasswords = true;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -323,4 +325,66 @@ public class GetInput extends AbstractTableActionInput
return (this); return (this);
} }
/*******************************************************************************
** Getter for shouldMaskPasswords
*******************************************************************************/
public boolean getShouldMaskPasswords()
{
return (this.shouldMaskPasswords);
}
/*******************************************************************************
** Setter for shouldMaskPasswords
*******************************************************************************/
public void setShouldMaskPasswords(boolean shouldMaskPasswords)
{
this.shouldMaskPasswords = shouldMaskPasswords;
}
/*******************************************************************************
** Fluent setter for shouldMaskPasswords
*******************************************************************************/
public GetInput withShouldMaskPasswords(boolean shouldMaskPasswords)
{
this.shouldMaskPasswords = shouldMaskPasswords;
return (this);
}
/*******************************************************************************
** Getter for shouldOmitHiddenFields
*******************************************************************************/
public boolean getShouldOmitHiddenFields()
{
return (this.shouldOmitHiddenFields);
}
/*******************************************************************************
** Setter for shouldOmitHiddenFields
*******************************************************************************/
public void setShouldOmitHiddenFields(boolean shouldOmitHiddenFields)
{
this.shouldOmitHiddenFields = shouldOmitHiddenFields;
}
/*******************************************************************************
** Fluent setter for shouldOmitHiddenFields
*******************************************************************************/
public GetInput withShouldOmitHiddenFields(boolean shouldOmitHiddenFields)
{
this.shouldOmitHiddenFields = shouldOmitHiddenFields;
return (this);
}
} }

View File

@ -47,6 +47,8 @@ public class QueryInput extends AbstractTableActionInput
private boolean shouldTranslatePossibleValues = false; private boolean shouldTranslatePossibleValues = false;
private boolean shouldGenerateDisplayValues = false; private boolean shouldGenerateDisplayValues = false;
private boolean shouldFetchHeavyFields = false; private boolean shouldFetchHeavyFields = false;
private boolean shouldOmitHiddenFields = true;
private boolean shouldMaskPasswords = true;
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
// this field - only applies if shouldTranslatePossibleValues is true. // // this field - only applies if shouldTranslatePossibleValues is true. //
@ -497,4 +499,66 @@ public class QueryInput extends AbstractTableActionInput
return (this); return (this);
} }
/*******************************************************************************
** Getter for shouldMaskPasswords
*******************************************************************************/
public boolean getShouldMaskPasswords()
{
return (this.shouldMaskPasswords);
}
/*******************************************************************************
** Setter for shouldMaskPasswords
*******************************************************************************/
public void setShouldMaskPasswords(boolean shouldMaskPasswords)
{
this.shouldMaskPasswords = shouldMaskPasswords;
}
/*******************************************************************************
** Fluent setter for shouldMaskPasswords
*******************************************************************************/
public QueryInput withShouldMaskPasswords(boolean shouldMaskPasswords)
{
this.shouldMaskPasswords = shouldMaskPasswords;
return (this);
}
/*******************************************************************************
** Getter for shouldOmitHiddenFields
*******************************************************************************/
public boolean getShouldOmitHiddenFields()
{
return (this.shouldOmitHiddenFields);
}
/*******************************************************************************
** Setter for shouldOmitHiddenFields
*******************************************************************************/
public void setShouldOmitHiddenFields(boolean shouldOmitHiddenFields)
{
this.shouldOmitHiddenFields = shouldOmitHiddenFields;
}
/*******************************************************************************
** Fluent setter for shouldOmitHiddenFields
*******************************************************************************/
public QueryInput withShouldOmitHiddenFields(boolean shouldOmitHiddenFields)
{
this.shouldOmitHiddenFields = shouldOmitHiddenFields;
return (this);
}
} }

View File

@ -58,6 +58,11 @@ public @interface QField
*******************************************************************************/ *******************************************************************************/
boolean isEditable() default true; boolean isEditable() default true;
/*******************************************************************************
**
*******************************************************************************/
boolean isHidden() default false;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -196,6 +196,17 @@ public class QRecord implements Serializable
/*******************************************************************************
**
*******************************************************************************/
public void removeValue(String fieldName)
{
values.remove(fieldName);
displayValues.remove(fieldName);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -54,6 +54,7 @@ public class QFieldMetaData implements Cloneable
private QFieldType type; private QFieldType type;
private boolean isRequired = false; private boolean isRequired = false;
private boolean isEditable = true; private boolean isEditable = true;
private boolean isHidden = false;
private boolean isHeavy = false; private boolean isHeavy = false;
private FieldSecurityLock fieldSecurityLock; private FieldSecurityLock fieldSecurityLock;
@ -183,6 +184,7 @@ public class QFieldMetaData implements Cloneable
QField fieldAnnotation = optionalFieldAnnotation.get(); QField fieldAnnotation = optionalFieldAnnotation.get();
setIsRequired(fieldAnnotation.isRequired()); setIsRequired(fieldAnnotation.isRequired());
setIsEditable(fieldAnnotation.isEditable()); setIsEditable(fieldAnnotation.isEditable());
setIsHidden(fieldAnnotation.isHidden());
if(StringUtils.hasContent(fieldAnnotation.label())) if(StringUtils.hasContent(fieldAnnotation.label()))
{ {
@ -851,4 +853,36 @@ public class QFieldMetaData implements Cloneable
this.middlewareMetaData.put(middlewareMetaData.getType(), middlewareMetaData); this.middlewareMetaData.put(middlewareMetaData.getType(), middlewareMetaData);
return (this); return (this);
} }
/*******************************************************************************
** Getter for isHidden
*******************************************************************************/
public boolean getIsHidden()
{
return (this.isHidden);
}
/*******************************************************************************
** Setter for isHidden
*******************************************************************************/
public void setIsHidden(boolean isHidden)
{
this.isHidden = isHidden;
}
/*******************************************************************************
** Fluent setter for isHidden
*******************************************************************************/
public QFieldMetaData withIsHidden(boolean isHidden)
{
this.isHidden = isHidden;
return (this);
}
} }

View File

@ -108,4 +108,14 @@ public enum QFieldType
{ {
return this == QFieldType.INTEGER || this == QFieldType.DECIMAL; return this == QFieldType.INTEGER || this == QFieldType.DECIMAL;
} }
/*******************************************************************************
**
*******************************************************************************/
public boolean needsMasked()
{
return this == QFieldType.PASSWORD;
}
} }

View File

@ -47,6 +47,7 @@ import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException; 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.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.context.QContext; import com.kingsrook.qqq.backend.core.context.QContext;
@ -297,12 +298,12 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
///////////////////////////////////// /////////////////////////////////////
// call auth0 with a login request // // call auth0 with a login request //
///////////////////////////////////// /////////////////////////////////////
TokenHolder result = auth.login(credentials.split(":")[0], credentials.split(":")[1].toCharArray()) Response<TokenHolder> result = auth.login(credentials.split(":")[0], credentials.split(":")[1].toCharArray())
.setScope("openid email nickname") .setScope("openid email nickname")
.setAudience(metaData.getAudience()) .setAudience(metaData.getAudience())
.execute(); .execute();
return (result.getAccessToken()); return (result.getBody().getAccessToken());
} }

View File

@ -67,7 +67,7 @@ public class MockQueryAction implements QueryInterface
for(String field : table.getFields().keySet()) for(String field : table.getFields().keySet())
{ {
Serializable value = field.equals("id") ? (i + 1) : getValue(table, field); Serializable value = field.equals("id") ? (i + 1) : getMockValue(table, field);
record.setValue(field, value); record.setValue(field, value);
} }
@ -95,7 +95,7 @@ public class MockQueryAction implements QueryInterface
** **
*******************************************************************************/ *******************************************************************************/
@SuppressWarnings("checkstyle:MagicNumber") @SuppressWarnings("checkstyle:MagicNumber")
private Serializable getValue(QTableMetaData table, String field) public static Serializable getMockValue(QTableMetaData table, String field)
{ {
// @formatter:off // IJ can't do new-style switch correctly yet... // @formatter:off // IJ can't do new-style switch correctly yet...
return switch(table.getField(field).getType()) return switch(table.getField(field).getType())

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.actions.tables; package com.kingsrook.qqq.backend.core.actions.tables;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.BaseTest;
@ -33,6 +34,7 @@ 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.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockQueryAction;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.TestUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -73,17 +75,40 @@ class QueryActionTest extends BaseTest
// this SHOULD be empty, based on the default for the should // // this SHOULD be empty, based on the default for the should //
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
assertThat(record.getDisplayValues()).isEmpty(); assertThat(record.getDisplayValues()).isEmpty();
//////////////////////////////////////////
// hidden field should not be in record //
//////////////////////////////////////////
assertThat(record.getValue("superSecret")).isNull();
/////////////////////////////////////
// password field should be masked //
/////////////////////////////////////
assertThat(record.getValueString("ssn")).contains("****");
} }
//////////////////////////////////// ////////////////////////////////////////////////////////
// now flip that field and re-run // // now flip some fields, re-run, and validate results //
//////////////////////////////////// ////////////////////////////////////////////////////////
queryInput.setShouldGenerateDisplayValues(true); queryInput.setShouldGenerateDisplayValues(true);
queryInput.setShouldMaskPasswords(false);
queryInput.setShouldOmitHiddenFields(false);
assertThat(queryOutput.getRecords()).isNotEmpty(); assertThat(queryOutput.getRecords()).isNotEmpty();
queryOutput = new QueryAction().execute(queryInput); queryOutput = new QueryAction().execute(queryInput);
for(QRecord record : queryOutput.getRecords()) for(QRecord record : queryOutput.getRecords())
{ {
assertThat(record.getDisplayValues()).isNotEmpty(); assertThat(record.getDisplayValues()).isNotEmpty();
//////////////////////////////////////////
// hidden field should now be in record //
//////////////////////////////////////////
assertThat(record.getValue("superSecret")).isNotNull();
/////////////////////////////////////
// password field should be masked //
/////////////////////////////////////
Serializable mockValue = MockQueryAction.getMockValue(QContext.getQInstance().getTable("person"), "ssn");
assertThat(record.getValueString("ssn")).isEqualTo(mockValue);
} }
} }

View File

@ -528,7 +528,8 @@ public class TestUtils
.withField(new QFieldMetaData("noOfShoes", QFieldType.INTEGER).withDisplayFormat(DisplayFormat.COMMAS)) .withField(new QFieldMetaData("noOfShoes", QFieldType.INTEGER).withDisplayFormat(DisplayFormat.COMMAS))
.withField(new QFieldMetaData("cost", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY)) .withField(new QFieldMetaData("cost", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY))
.withField(new QFieldMetaData("price", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY)) .withField(new QFieldMetaData("price", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY))
; .withField(new QFieldMetaData("ssn", QFieldType.STRING).withType(QFieldType.PASSWORD))
.withField(new QFieldMetaData("superSecret", QFieldType.STRING).withType(QFieldType.PASSWORD).withIsHidden(true));
} }

View File

@ -0,0 +1,7 @@
#!/bin/bash
############################################################################
## open-jacoco-test-report.sh
## opens the jacoco generated file from the target directory in browser
############################################################################
open target/site/jacoco/index.html

View File

@ -48,7 +48,7 @@ checkBuild()
qqq-frontend-core) shortRepo="f'core";; qqq-frontend-core) shortRepo="f'core";;
qqq-frontend-material-dashboard) shortRepo="m-db";; qqq-frontend-material-dashboard) shortRepo="m-db";;
ColdTrack-Live) shortRepo="ctl";; ColdTrack-Live) shortRepo="ctl";;
Nutrifresh-One-Scripts) shortRepo="nf1-scr";; ColdTrack-Live-Scripts) shortRepo="ct1-scr";;
esac esac
timestamp=$(date -j -f "%Y-%m-%dT%H:%M:%S%z" $(echo "$startDate" | sed 's/\....Z/+0000/') +%s) timestamp=$(date -j -f "%Y-%m-%dT%H:%M:%S%z" $(echo "$startDate" | sed 's/\....Z/+0000/') +%s)