From 912b3885f57e317fd353918fba62884b7a97e88c Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Thu, 20 Apr 2023 16:12:22 -0500 Subject: [PATCH 1/5] updated nf scripts name --- qqq-dev-tools/bin/xbar-circleci-latest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From be094127550a7e8ce7c554d3742abe72e1ec57d5 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Thu, 27 Apr 2023 10:31:13 -0500 Subject: [PATCH 2/5] CTLE-421: checkpoint commit of api key story so that CTL test coverage can be worked on instead --- qqq-backend-core/pom.xml | 18 ++++- .../AbstractPreInsertCustomizer.java | 65 +++++++++++++++++++ .../actions/customizers/TableCustomizers.java | 8 +-- .../core/actions/tables/InsertAction.java | 8 +++ .../Auth0AuthenticationModule.java | 5 +- 5 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/customizers/AbstractPreInsertCustomizer.java 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/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/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()); } From acfcc422f99aa3a2827d209d7430c584a9df5188 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Fri, 28 Apr 2023 10:05:25 -0500 Subject: [PATCH 3/5] script to open jacoco report --- qqq-dev-tools/bin/open-jacoco-test-report.sh | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 qqq-dev-tools/bin/open-jacoco-test-report.sh 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 From b0d0de5d4957b9ff26ce3901ba2b9605f11394a3 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Fri, 28 Apr 2023 13:21:08 -0500 Subject: [PATCH 4/5] CTLE-421: implemented fieldLevel is hidden, updated to mask password fields --- .../core/actions/tables/GetAction.java | 50 +++++++++++++++ .../core/actions/tables/QueryAction.java | 56 ++++++++++++++++ .../core/instances/QInstanceEnricher.java | 3 +- .../core/instances/QInstanceValidator.java | 9 ++- .../model/actions/tables/get/GetInput.java | 64 +++++++++++++++++++ .../actions/tables/query/QueryInput.java | 64 +++++++++++++++++++ .../qqq/backend/core/model/data/QField.java | 5 ++ .../qqq/backend/core/model/data/QRecord.java | 11 ++++ .../model/metadata/fields/QFieldMetaData.java | 34 ++++++++++ .../model/metadata/fields/QFieldType.java | 10 +++ .../implementations/mock/MockQueryAction.java | 4 +- .../core/actions/tables/QueryActionTest.java | 31 ++++++++- .../qqq/backend/core/utils/TestUtils.java | 3 +- 13 files changed, 336 insertions(+), 8 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 84a6ec91..cb9b20db 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,53 @@ public class GetAction QValueFormatter.setDisplayValuesInRecords(getInput.getTable(), List.of(returnRecord)); } + if(getInput.getShouldOmitHiddenFields() || getInput.getShouldMaskPasswords()) + { + Map fields = QContext.getQInstance().getTable(record.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)); + } + + ////////////////////////////// + // mask any password fields // + ////////////////////////////// + Map fields = QContext.getQInstance().getTable(record.getTableName()).getFields(); + for(String fieldName : fields.keySet()) + { + QFieldMetaData field = fields.get(fieldName); + if(getInput.getShouldOmitHiddenFields()) + { + if(field.getIsHidden()) + { + returnRecord.removeValue(fieldName); + } + } + else if(getInput.getShouldMaskPasswords()) + { + if(field.getType() != null && field.getType().needsMasked()) + { + returnRecord.setValue(fieldName, "************"); + returnRecord.setDisplayValue(fieldName, "************"); + } + } + } + ////////////////////////////////////////////////////////////////////////////// // 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/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/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)); } From 2832566dbd35fd004978c44e60f9e40c967d49a0 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Fri, 28 Apr 2023 13:59:08 -0500 Subject: [PATCH 5/5] CTLE-421: fixes for failing tests, removed no longer necessary loop --- .../core/actions/tables/GetAction.java | 26 +------------------ 1 file changed, 1 insertion(+), 25 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 cb9b20db..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 @@ -377,7 +377,7 @@ public class GetAction if(getInput.getShouldOmitHiddenFields() || getInput.getShouldMaskPasswords()) { - Map fields = QContext.getQInstance().getTable(record.getTableName()).getFields(); + Map fields = QContext.getQInstance().getTable(getInput.getTableName()).getFields(); for(String fieldName : fields.keySet()) { QFieldType fieldType = fields.get(fieldName).getType(); @@ -398,30 +398,6 @@ public class GetAction QValueFormatter.setDisplayValuesInRecords(getInput.getTable(), List.of(returnRecord)); } - ////////////////////////////// - // mask any password fields // - ////////////////////////////// - Map fields = QContext.getQInstance().getTable(record.getTableName()).getFields(); - for(String fieldName : fields.keySet()) - { - QFieldMetaData field = fields.get(fieldName); - if(getInput.getShouldOmitHiddenFields()) - { - if(field.getIsHidden()) - { - returnRecord.removeValue(fieldName); - } - } - else if(getInput.getShouldMaskPasswords()) - { - if(field.getType() != null && field.getType().needsMasked()) - { - returnRecord.setValue(fieldName, "************"); - returnRecord.setDisplayValue(fieldName, "************"); - } - } - } - ////////////////////////////////////////////////////////////////////////////// // note - shouldFetchHeavyFields should be handled by the underlying action // //////////////////////////////////////////////////////////////////////////////