diff --git a/qqq-backend-core/pom.xml b/qqq-backend-core/pom.xml
index 04d00d1a..d6fc2da1 100644
--- a/qqq-backend-core/pom.xml
+++ b/qqq-backend-core/pom.xml
@@ -56,6 +56,10 @@
software.amazon.awssdk
quicksight
+
+ software.amazon.awssdk
+ apigateway
+
com.amazonaws
aws-java-sdk-secretsmanager
@@ -100,8 +104,18 @@
com.auth0
- mvc-auth-commons
- 1.9.2
+ auth0
+ 2.1.0
+
+
+ com.auth0
+ java-jwt
+ 4.4.0
+
+
+ com.auth0
+ jwks-rsa
+ 0.22.0
io.github.cdimascio
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/AbstractPreInsertCustomizer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/AbstractPreInsertCustomizer.java
new file mode 100644
index 00000000..1b3ab771
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/AbstractPreInsertCustomizer.java
@@ -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 .
+ */
+
+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 apply(List records);
+
+
+
+ /*******************************************************************************
+ ** Getter for insertInput
+ **
+ *******************************************************************************/
+ public InsertInput getInsertInput()
+ {
+ return insertInput;
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for insertInput
+ **
+ *******************************************************************************/
+ public void setInsertInput(InsertInput insertInput)
+ {
+ this.insertInput = insertInput;
+ }
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/TableCustomizers.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/TableCustomizers.java
index 2d2d6621..548b18bf 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/TableCustomizers.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/TableCustomizers.java
@@ -29,16 +29,10 @@ package com.kingsrook.qqq.backend.core.actions.customizers;
** Works with TableCustomizer (singular version of this name) objects, during
** instance validation, to provide validation of the referenced code (and to
** 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
{
+ PRE_INSERT_RECORD(new TableCustomizer("preInsertRecord", AbstractPreInsertCustomizer.class)),
POST_QUERY_RECORD(new TableCustomizer("postQueryRecord", AbstractPostQueryCustomizer.class)),
POST_INSERT_RECORD(new TableCustomizer("postInsertRecord", AbstractPostInsertCustomizer.class));
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 84a6ec91..0f8f99a3 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
@@ -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.values.QPossibleValueTranslator;
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.logging.LogPair;
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.UpdateOutput;
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.cache.CacheUseCase;
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
@@ -372,6 +375,29 @@ public class GetAction
QValueFormatter.setDisplayValuesInRecords(getInput.getTable(), List.of(returnRecord));
}
+ if(getInput.getShouldOmitHiddenFields() || getInput.getShouldMaskPasswords())
+ {
+ Map 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 //
//////////////////////////////////////////////////////////////////////////////
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/InsertAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/InsertAction.java
index 5cbbb2ad..8a532325 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/InsertAction.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/InsertAction.java
@@ -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.RecordAutomationStatusUpdater;
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.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.tables.helpers.UniqueKeyHelper;
@@ -86,6 +87,7 @@ public class InsertAction extends AbstractQActionFunction preInsertCustomizer = QCodeLoader.getTableCustomizer(AbstractPreInsertCustomizer.class, table, TableCustomizers.PRE_INSERT_RECORD.getRole());
Optional postInsertCustomizer = QCodeLoader.getTableCustomizer(AbstractPostInsertCustomizer.class, table, TableCustomizers.POST_INSERT_RECORD.getRole());
setAutomationStatusField(insertInput);
@@ -97,6 +99,12 @@ public class InsertAction extends AbstractQActionFunction errors = insertOutput.getRecords().stream().flatMap(r -> r.getErrors().stream()).toList();
if(CollectionUtils.nullSafeHasContents(errors))
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/QueryAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/QueryAction.java
index 11763ab8..2154e7a9 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/QueryAction.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/QueryAction.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
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.QueryOutput;
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.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
@@ -247,5 +249,59 @@ public class QueryAction
{
QValueFormatter.setDisplayValuesInRecords(queryInput.getTable(), records);
}
+
+ //////////////////////////////
+ // mask any password fields //
+ //////////////////////////////
+ if(queryInput.getShouldOmitHiddenFields() || queryInput.getShouldMaskPasswords())
+ {
+ Set maskedFields = new HashSet<>();
+ Set hiddenFields = new HashSet<>();
+
+ //////////////////////////////////////////////////
+ // build up sets of passwords and hidden fields //
+ //////////////////////////////////////////////////
+ Map 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));
+ }
+ }
+ }
+ }
}
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java
index 8746b4a1..df6fca5a 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java
@@ -955,7 +955,8 @@ public class QInstanceEnricher
{
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);
usedFieldNames.add(fieldName);
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java
index 7dfa6963..1a211992 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java
@@ -437,7 +437,14 @@ public class QInstanceValidator
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)
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/get/GetInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/get/GetInput.java
index 6bb0ff25..0e3f8e59 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/get/GetInput.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/get/GetInput.java
@@ -43,6 +43,8 @@ public class GetInput extends AbstractTableActionInput
private boolean shouldTranslatePossibleValues = false;
private boolean shouldGenerateDisplayValues = false;
private boolean shouldFetchHeavyFields = true;
+ private boolean shouldOmitHiddenFields = true;
+ private boolean shouldMaskPasswords = true;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -323,4 +325,66 @@ public class GetInput extends AbstractTableActionInput
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);
+ }
+
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java
index 3a49001e..76c4101f 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java
@@ -47,6 +47,8 @@ public class QueryInput extends AbstractTableActionInput
private boolean shouldTranslatePossibleValues = false;
private boolean shouldGenerateDisplayValues = false;
private boolean shouldFetchHeavyFields = false;
+ private boolean shouldOmitHiddenFields = true;
+ private boolean shouldMaskPasswords = true;
/////////////////////////////////////////////////////////////////////////////////////////
// this field - only applies if shouldTranslatePossibleValues is true. //
@@ -497,4 +499,66 @@ public class QueryInput extends AbstractTableActionInput
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);
+ }
+
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QField.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QField.java
index 3cdec2a9..9fecaf12 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QField.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QField.java
@@ -58,6 +58,11 @@ public @interface QField
*******************************************************************************/
boolean isEditable() default true;
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ boolean isHidden() default false;
+
/*******************************************************************************
**
*******************************************************************************/
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecord.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecord.java
index 8a2f0a91..f6950ef3 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecord.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecord.java
@@ -196,6 +196,17 @@ public class QRecord implements Serializable
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public void removeValue(String fieldName)
+ {
+ values.remove(fieldName);
+ displayValues.remove(fieldName);
+ }
+
+
+
/*******************************************************************************
**
*******************************************************************************/
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java
index 56ba9f8e..298ffaf4 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java
@@ -54,6 +54,7 @@ public class QFieldMetaData implements Cloneable
private QFieldType type;
private boolean isRequired = false;
private boolean isEditable = true;
+ private boolean isHidden = false;
private boolean isHeavy = false;
private FieldSecurityLock fieldSecurityLock;
@@ -183,6 +184,7 @@ public class QFieldMetaData implements Cloneable
QField fieldAnnotation = optionalFieldAnnotation.get();
setIsRequired(fieldAnnotation.isRequired());
setIsEditable(fieldAnnotation.isEditable());
+ setIsHidden(fieldAnnotation.isHidden());
if(StringUtils.hasContent(fieldAnnotation.label()))
{
@@ -851,4 +853,36 @@ public class QFieldMetaData implements Cloneable
this.middlewareMetaData.put(middlewareMetaData.getType(), middlewareMetaData);
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);
+ }
+
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldType.java
index 89507db3..367a4960 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldType.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldType.java
@@ -108,4 +108,14 @@ public enum QFieldType
{
return this == QFieldType.INTEGER || this == QFieldType.DECIMAL;
}
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public boolean needsMasked()
+ {
+ return this == QFieldType.PASSWORD;
+ }
}
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 0f6550a8..a0279503 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
@@ -47,6 +47,7 @@ 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.net.Response;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext;
@@ -297,12 +298,12 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
/////////////////////////////////////
// call auth0 with a login request //
/////////////////////////////////////
- TokenHolder result = auth.login(credentials.split(":")[0], credentials.split(":")[1].toCharArray())
+ Response result = auth.login(credentials.split(":")[0], credentials.split(":")[1].toCharArray())
.setScope("openid email nickname")
.setAudience(metaData.getAudience())
.execute();
- return (result.getAccessToken());
+ return (result.getBody().getAccessToken());
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/mock/MockQueryAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/mock/MockQueryAction.java
index 0d4b918a..f99b6efb 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/mock/MockQueryAction.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/mock/MockQueryAction.java
@@ -67,7 +67,7 @@ public class MockQueryAction implements QueryInterface
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);
}
@@ -95,7 +95,7 @@ public class MockQueryAction implements QueryInterface
**
*******************************************************************************/
@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...
return switch(table.getField(field).getType())
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java
index b3be058f..564cdf36 100644
--- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java
@@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.actions.tables;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
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.QueryOutput;
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.TestUtils;
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 //
///////////////////////////////////////////////////////////////
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.setShouldMaskPasswords(false);
+ queryInput.setShouldOmitHiddenFields(false);
assertThat(queryOutput.getRecords()).isNotEmpty();
queryOutput = new QueryAction().execute(queryInput);
for(QRecord record : queryOutput.getRecords())
{
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);
}
}
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java
index 70b74a32..b3953b6a 100644
--- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java
@@ -528,7 +528,8 @@ public class TestUtils
.withField(new QFieldMetaData("noOfShoes", QFieldType.INTEGER).withDisplayFormat(DisplayFormat.COMMAS))
.withField(new QFieldMetaData("cost", 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));
}
diff --git a/qqq-dev-tools/bin/open-jacoco-test-report.sh b/qqq-dev-tools/bin/open-jacoco-test-report.sh
new file mode 100755
index 00000000..502631e6
--- /dev/null
+++ b/qqq-dev-tools/bin/open-jacoco-test-report.sh
@@ -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
diff --git a/qqq-dev-tools/bin/xbar-circleci-latest.sh b/qqq-dev-tools/bin/xbar-circleci-latest.sh
index 4c1967ac..eb7b4171 100755
--- a/qqq-dev-tools/bin/xbar-circleci-latest.sh
+++ b/qqq-dev-tools/bin/xbar-circleci-latest.sh
@@ -48,7 +48,7 @@ checkBuild()
qqq-frontend-core) shortRepo="f'core";;
qqq-frontend-material-dashboard) shortRepo="m-db";;
ColdTrack-Live) shortRepo="ctl";;
- Nutrifresh-One-Scripts) shortRepo="nf1-scr";;
+ ColdTrack-Live-Scripts) shortRepo="ct1-scr";;
esac
timestamp=$(date -j -f "%Y-%m-%dT%H:%M:%S%z" $(echo "$startDate" | sed 's/\....Z/+0000/') +%s)