mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 21:50:45 +00:00
implementation of record security locks, and permissions
This commit is contained in:
@ -27,6 +27,8 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
@ -40,6 +42,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMeta
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendWidgetMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
@ -70,10 +73,18 @@ public class MetaDataAction
|
||||
Map<String, QFrontendTableMetaData> tables = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QTableMetaData> entry : metaDataInput.getInstance().getTables().entrySet())
|
||||
{
|
||||
String tableName = entry.getKey();
|
||||
String tableName = entry.getKey();
|
||||
QTableMetaData table = entry.getValue();
|
||||
|
||||
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, table);
|
||||
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QBackendMetaData backendForTable = metaDataInput.getInstance().getBackendForTable(tableName);
|
||||
tables.put(tableName, new QFrontendTableMetaData(backendForTable, entry.getValue(), false));
|
||||
treeNodes.put(tableName, new AppTreeNode(entry.getValue()));
|
||||
tables.put(tableName, new QFrontendTableMetaData(metaDataInput, backendForTable, table, false));
|
||||
treeNodes.put(tableName, new AppTreeNode(table));
|
||||
}
|
||||
metaDataOutput.setTables(tables);
|
||||
|
||||
@ -83,8 +94,17 @@ public class MetaDataAction
|
||||
Map<String, QFrontendProcessMetaData> processes = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QProcessMetaData> entry : metaDataInput.getInstance().getProcesses().entrySet())
|
||||
{
|
||||
processes.put(entry.getKey(), new QFrontendProcessMetaData(entry.getValue(), false));
|
||||
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
|
||||
String processName = entry.getKey();
|
||||
QProcessMetaData process = entry.getValue();
|
||||
|
||||
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, process);
|
||||
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
processes.put(processName, new QFrontendProcessMetaData(metaDataInput, process, false));
|
||||
treeNodes.put(processName, new AppTreeNode(process));
|
||||
}
|
||||
metaDataOutput.setProcesses(processes);
|
||||
|
||||
@ -94,8 +114,17 @@ public class MetaDataAction
|
||||
Map<String, QFrontendReportMetaData> reports = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QReportMetaData> entry : metaDataInput.getInstance().getReports().entrySet())
|
||||
{
|
||||
reports.put(entry.getKey(), new QFrontendReportMetaData(entry.getValue(), false));
|
||||
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
|
||||
String reportName = entry.getKey();
|
||||
QReportMetaData report = entry.getValue();
|
||||
|
||||
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, report);
|
||||
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
reports.put(reportName, new QFrontendReportMetaData(metaDataInput, report, false));
|
||||
treeNodes.put(reportName, new AppTreeNode(report));
|
||||
}
|
||||
metaDataOutput.setReports(reports);
|
||||
|
||||
@ -105,7 +134,16 @@ public class MetaDataAction
|
||||
Map<String, QFrontendWidgetMetaData> widgets = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QWidgetMetaDataInterface> entry : metaDataInput.getInstance().getWidgets().entrySet())
|
||||
{
|
||||
widgets.put(entry.getKey(), new QFrontendWidgetMetaData(entry.getValue()));
|
||||
String widgetName = entry.getKey();
|
||||
QWidgetMetaDataInterface widget = entry.getValue();
|
||||
|
||||
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, widget);
|
||||
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
widgets.put(widgetName, new QFrontendWidgetMetaData(metaDataInput, widget));
|
||||
}
|
||||
metaDataOutput.setWidgets(widgets);
|
||||
|
||||
@ -115,14 +153,32 @@ public class MetaDataAction
|
||||
Map<String, QFrontendAppMetaData> apps = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QAppMetaData> entry : metaDataInput.getInstance().getApps().entrySet())
|
||||
{
|
||||
apps.put(entry.getKey(), new QFrontendAppMetaData(entry.getValue()));
|
||||
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
|
||||
String appName = entry.getKey();
|
||||
QAppMetaData app = entry.getValue();
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(entry.getValue().getChildren()))
|
||||
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, app);
|
||||
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
|
||||
{
|
||||
for(QAppChildMetaData child : entry.getValue().getChildren())
|
||||
continue;
|
||||
}
|
||||
|
||||
apps.put(appName, new QFrontendAppMetaData(app, metaDataOutput));
|
||||
treeNodes.put(appName, new AppTreeNode(app));
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(app.getChildren()))
|
||||
{
|
||||
for(QAppChildMetaData child : app.getChildren())
|
||||
{
|
||||
apps.get(entry.getKey()).addChild(new AppTreeNode(child));
|
||||
if(child instanceof MetaDataWithPermissionRules metaDataWithPermissionRules)
|
||||
{
|
||||
PermissionCheckResult childPermissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, metaDataWithPermissionRules);
|
||||
if(childPermissionResult.equals(PermissionCheckResult.DENY_HIDE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
apps.get(appName).addChild(new AppTreeNode(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,7 +192,7 @@ public class MetaDataAction
|
||||
{
|
||||
if(appMetaData.getParentAppName() == null)
|
||||
{
|
||||
buildAppTree(treeNodes, appTree, appMetaData);
|
||||
buildAppTree(metaDataInput, treeNodes, appTree, appMetaData);
|
||||
}
|
||||
}
|
||||
metaDataOutput.setAppTree(appTree);
|
||||
@ -161,7 +217,7 @@ public class MetaDataAction
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void buildAppTree(Map<String, AppTreeNode> treeNodes, List<AppTreeNode> nodeList, QAppChildMetaData childMetaData)
|
||||
private void buildAppTree(MetaDataInput metaDataInput, Map<String, AppTreeNode> treeNodes, List<AppTreeNode> nodeList, QAppChildMetaData childMetaData)
|
||||
{
|
||||
AppTreeNode treeNode = treeNodes.get(childMetaData.getName());
|
||||
if(treeNode == null)
|
||||
@ -176,7 +232,16 @@ public class MetaDataAction
|
||||
{
|
||||
for(QAppChildMetaData child : app.getChildren())
|
||||
{
|
||||
buildAppTree(treeNodes, treeNode.getChildren(), child);
|
||||
if(child instanceof MetaDataWithPermissionRules metaDataWithPermissionRules)
|
||||
{
|
||||
PermissionCheckResult permissionResult = PermissionsHelper.getPermissionCheckResult(metaDataInput, metaDataWithPermissionRules);
|
||||
if(permissionResult.equals(PermissionCheckResult.DENY_HIDE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
buildAppTree(metaDataInput, treeNodes, treeNode.getChildren(), child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class ProcessMetaDataAction
|
||||
{
|
||||
throw (new QNotFoundException("Process [" + processMetaDataInput.getProcessName() + "] was not found."));
|
||||
}
|
||||
processMetaDataOutput.setProcess(new QFrontendProcessMetaData(process, true));
|
||||
processMetaDataOutput.setProcess(new QFrontendProcessMetaData(processMetaDataInput, process, true));
|
||||
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
||||
|
@ -54,7 +54,7 @@ public class TableMetaDataAction
|
||||
throw (new QNotFoundException("Table [" + tableMetaDataInput.getTableName() + "] was not found."));
|
||||
}
|
||||
QBackendMetaData backendForTable = tableMetaDataInput.getInstance().getBackendForTable(table.getName());
|
||||
tableMetaDataOutput.setTable(new QFrontendTableMetaData(backendForTable, table, true));
|
||||
tableMetaDataOutput.setTable(new QFrontendTableMetaData(tableMetaDataInput, backendForTable, table, true));
|
||||
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
||||
|
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AvailablePermission extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "availablePermission";
|
||||
|
||||
@QField(label = "Permission Name")
|
||||
private String name;
|
||||
|
||||
@QField(label = "Object")
|
||||
private String objectName;
|
||||
|
||||
@QField()
|
||||
private String objectType;
|
||||
|
||||
@QField()
|
||||
private String permissionType;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if(this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AvailablePermission that = (AvailablePermission) o;
|
||||
return Objects.equals(name, that.name) && Objects.equals(objectName, that.objectName) && Objects.equals(objectType, that.objectType) && Objects.equals(permissionType, that.permissionType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(name, objectName, objectType, permissionType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public AvailablePermission withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for objectType
|
||||
*******************************************************************************/
|
||||
public String getObjectType()
|
||||
{
|
||||
return (this.objectType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for objectType
|
||||
*******************************************************************************/
|
||||
public void setObjectType(String objectType)
|
||||
{
|
||||
this.objectType = objectType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for objectType
|
||||
*******************************************************************************/
|
||||
public AvailablePermission withObjectType(String objectType)
|
||||
{
|
||||
this.objectType = objectType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionType
|
||||
*******************************************************************************/
|
||||
public String getPermissionType()
|
||||
{
|
||||
return (this.permissionType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionType
|
||||
*******************************************************************************/
|
||||
public void setPermissionType(String permissionType)
|
||||
{
|
||||
this.permissionType = permissionType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionType
|
||||
*******************************************************************************/
|
||||
public AvailablePermission withPermissionType(String permissionType)
|
||||
{
|
||||
this.permissionType = permissionType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for objectName
|
||||
*******************************************************************************/
|
||||
public String getObjectName()
|
||||
{
|
||||
return (this.objectName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for objectName
|
||||
*******************************************************************************/
|
||||
public void setObjectName(String objectName)
|
||||
{
|
||||
this.objectName = objectName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for objectName
|
||||
*******************************************************************************/
|
||||
public AvailablePermission withObjectName(String objectName)
|
||||
{
|
||||
this.objectName = objectName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class BulkTableActionProcessPermissionChecker implements CustomPermissionChecker
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(BulkTableActionProcessPermissionChecker.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void checkPermissionsThrowing(AbstractActionInput actionInput, MetaDataWithPermissionRules metaDataWithPermissionRules) throws QPermissionDeniedException
|
||||
{
|
||||
String processName = metaDataWithPermissionRules.getName();
|
||||
if(processName != null && processName.indexOf('.') > -1)
|
||||
{
|
||||
String[] parts = processName.split("\\.", 2);
|
||||
String tableName = parts[0];
|
||||
String bulkActionName = parts[1];
|
||||
|
||||
AbstractTableActionInput tableActionInput = new AbstractTableActionInput(actionInput.getInstance());
|
||||
tableActionInput.setSession(actionInput.getSession());
|
||||
tableActionInput.setTableName(tableName);
|
||||
|
||||
switch(bulkActionName)
|
||||
{
|
||||
case "bulkInsert" -> PermissionsHelper.checkTablePermissionThrowing(tableActionInput, TablePermissionSubType.INSERT);
|
||||
case "bulkEdit" -> PermissionsHelper.checkTablePermissionThrowing(tableActionInput, TablePermissionSubType.EDIT);
|
||||
case "bulkDelete" -> PermissionsHelper.checkTablePermissionThrowing(tableActionInput, TablePermissionSubType.DELETE);
|
||||
default -> LOG.warn("Unexpected bulk action name when checking permissions for process: " + processName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface CustomPermissionChecker
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
void checkPermissionsThrowing(AbstractActionInput actionInput, MetaDataWithPermissionRules metaDataWithPermissionRules) throws QPermissionDeniedException;
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum PermissionCheckResult
|
||||
{
|
||||
ALLOW,
|
||||
DENY_HIDE,
|
||||
DENY_DISABLE;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
sealed interface PermissionSubType permits PrivatePermissionSubType, TablePermissionSubType
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
String getPermissionSuffix();
|
||||
|
||||
}
|
@ -0,0 +1,579 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.DenyBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithName;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class PermissionsHelper
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(PermissionsHelper.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void checkTablePermissionThrowing(AbstractTableActionInput tableActionInput, TablePermissionSubType permissionSubType) throws QPermissionDeniedException
|
||||
{
|
||||
checkTablePermissionThrowing(tableActionInput, tableActionInput.getTableName(), permissionSubType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void checkTablePermissionThrowing(AbstractActionInput actionInput, String tableName, TablePermissionSubType permissionSubType) throws QPermissionDeniedException
|
||||
{
|
||||
warnAboutPermissionSubTypeForTables(permissionSubType);
|
||||
QTableMetaData table = actionInput.getInstance().getTable(tableName);
|
||||
|
||||
commonCheckPermissionThrowing(getEffectivePermissionRules(table, actionInput.getInstance()), permissionSubType, table.getName(), actionInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static boolean hasTablePermission(AbstractActionInput actionInput, String tableName, TablePermissionSubType permissionSubType)
|
||||
{
|
||||
try
|
||||
{
|
||||
checkTablePermissionThrowing(actionInput, tableName, permissionSubType);
|
||||
return (true);
|
||||
}
|
||||
catch(QPermissionDeniedException e)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static PermissionCheckResult getPermissionCheckResult(AbstractActionInput actionInput, MetaDataWithPermissionRules metaDataWithPermissionRules)
|
||||
{
|
||||
QPermissionRules rules = getEffectivePermissionRules(metaDataWithPermissionRules, actionInput.getInstance());
|
||||
String permissionBaseName = getEffectivePermissionBaseName(rules, metaDataWithPermissionRules.getName());
|
||||
|
||||
switch(rules.getLevel())
|
||||
{
|
||||
case NOT_PROTECTED:
|
||||
{
|
||||
/////////////////////////////////////////////////
|
||||
// if the entity isn't protected, always ALLOW //
|
||||
/////////////////////////////////////////////////
|
||||
return PermissionCheckResult.ALLOW;
|
||||
}
|
||||
case HAS_ACCESS_PERMISSION:
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// if the entity just has a 'has access', then check for 'has access' //
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
return getPermissionCheckResult(actionInput, rules, permissionBaseName, PrivatePermissionSubType.HAS_ACCESS);
|
||||
}
|
||||
case READ_WRITE_PERMISSIONS:
|
||||
{
|
||||
////////////////////////////////////////////////////////////////
|
||||
// if the table is configured w/ read/write, check for either //
|
||||
////////////////////////////////////////////////////////////////
|
||||
if(metaDataWithPermissionRules instanceof QTableMetaData)
|
||||
{
|
||||
return getPermissionCheckResult(actionInput, rules, permissionBaseName, PrivatePermissionSubType.READ, PrivatePermissionSubType.WRITE);
|
||||
}
|
||||
return getPermissionCheckResult(actionInput, rules, permissionBaseName, PrivatePermissionSubType.HAS_ACCESS);
|
||||
}
|
||||
case READ_INSERT_EDIT_DELETE_PERMISSIONS:
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// if the table is configured w/ read/insert/edit/delete, check for any //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if(metaDataWithPermissionRules instanceof QTableMetaData)
|
||||
{
|
||||
return getPermissionCheckResult(actionInput, rules, permissionBaseName, TablePermissionSubType.READ, TablePermissionSubType.INSERT, TablePermissionSubType.EDIT, TablePermissionSubType.DELETE);
|
||||
}
|
||||
return getPermissionCheckResult(actionInput, rules, permissionBaseName, PrivatePermissionSubType.HAS_ACCESS);
|
||||
}
|
||||
default:
|
||||
{
|
||||
return getPermissionDeniedCheckResult(rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void checkProcessPermissionThrowing(AbstractActionInput actionInput, String processName) throws QPermissionDeniedException
|
||||
{
|
||||
checkProcessPermissionThrowing(actionInput, processName, Collections.emptyMap());
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Map<String, CustomPermissionChecker> customPermissionCheckerMap = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void checkProcessPermissionThrowing(AbstractActionInput actionInput, String processName, Map<String, Serializable> processValues) throws QPermissionDeniedException
|
||||
{
|
||||
QProcessMetaData process = actionInput.getInstance().getProcess(processName);
|
||||
QPermissionRules effectivePermissionRules = getEffectivePermissionRules(process, actionInput.getInstance());
|
||||
|
||||
if(effectivePermissionRules.getCustomPermissionChecker() != null)
|
||||
{
|
||||
/////////////////////////////////////
|
||||
// todo - avoid stack overflows... //
|
||||
/////////////////////////////////////
|
||||
if(!customPermissionCheckerMap.containsKey(effectivePermissionRules.getCustomPermissionChecker().getName()))
|
||||
{
|
||||
CustomPermissionChecker customPermissionChecker = QCodeLoader.getAdHoc(CustomPermissionChecker.class, effectivePermissionRules.getCustomPermissionChecker());
|
||||
customPermissionCheckerMap.put(effectivePermissionRules.getCustomPermissionChecker().getName(), customPermissionChecker);
|
||||
}
|
||||
CustomPermissionChecker customPermissionChecker = customPermissionCheckerMap.get(effectivePermissionRules.getCustomPermissionChecker().getName());
|
||||
customPermissionChecker.checkPermissionsThrowing(actionInput, process);
|
||||
return;
|
||||
}
|
||||
|
||||
commonCheckPermissionThrowing(effectivePermissionRules, PrivatePermissionSubType.HAS_ACCESS, process.getName(), actionInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static boolean hasProcessPermission(AbstractActionInput actionInput, String processName)
|
||||
{
|
||||
try
|
||||
{
|
||||
checkProcessPermissionThrowing(actionInput, processName);
|
||||
return (true);
|
||||
}
|
||||
catch(QPermissionDeniedException e)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void checkAppPermissionThrowing(AbstractActionInput actionInput, String appName) throws QPermissionDeniedException
|
||||
{
|
||||
QAppMetaData app = actionInput.getInstance().getApp(appName);
|
||||
commonCheckPermissionThrowing(getEffectivePermissionRules(app, actionInput.getInstance()), PrivatePermissionSubType.HAS_ACCESS, app.getName(), actionInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static boolean hasAppPermission(AbstractActionInput actionInput, String appName)
|
||||
{
|
||||
try
|
||||
{
|
||||
checkAppPermissionThrowing(actionInput, appName);
|
||||
return (true);
|
||||
}
|
||||
catch(QPermissionDeniedException e)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void checkReportPermissionThrowing(AbstractActionInput actionInput, String reportName) throws QPermissionDeniedException
|
||||
{
|
||||
QReportMetaData report = actionInput.getInstance().getReport(reportName);
|
||||
commonCheckPermissionThrowing(getEffectivePermissionRules(report, actionInput.getInstance()), PrivatePermissionSubType.HAS_ACCESS, report.getName(), actionInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static boolean hasReportPermission(AbstractActionInput actionInput, String reportName)
|
||||
{
|
||||
try
|
||||
{
|
||||
checkReportPermissionThrowing(actionInput, reportName);
|
||||
return (true);
|
||||
}
|
||||
catch(QPermissionDeniedException e)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void checkWidgetPermissionThrowing(AbstractActionInput actionInput, String widgetName) throws QPermissionDeniedException
|
||||
{
|
||||
QWidgetMetaDataInterface widget = actionInput.getInstance().getWidget(widgetName);
|
||||
commonCheckPermissionThrowing(getEffectivePermissionRules(widget, actionInput.getInstance()), PrivatePermissionSubType.HAS_ACCESS, widget.getName(), actionInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static boolean hasWidgetPermission(AbstractActionInput actionInput, String widgetName)
|
||||
{
|
||||
try
|
||||
{
|
||||
checkWidgetPermissionThrowing(actionInput, widgetName);
|
||||
return (true);
|
||||
}
|
||||
catch(QPermissionDeniedException e)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Collection<String> getAllAvailablePermissionNames(QInstance instance)
|
||||
{
|
||||
return (getAllAvailablePermissions(instance).stream()
|
||||
.map(AvailablePermission::getName)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Collection<AvailablePermission> getAllAvailablePermissions(QInstance instance)
|
||||
{
|
||||
Collection<AvailablePermission> rs = new LinkedHashSet<>();
|
||||
|
||||
for(QTableMetaData tableMetaData : instance.getTables().values())
|
||||
{
|
||||
if(tableMetaData.getIsHidden())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QPermissionRules rules = getEffectivePermissionRules(tableMetaData, instance);
|
||||
String baseName = getEffectivePermissionBaseName(rules, tableMetaData.getName());
|
||||
|
||||
for(TablePermissionSubType permissionSubType : TablePermissionSubType.values())
|
||||
{
|
||||
addEffectiveAvailablePermission(rules, permissionSubType, rs, baseName, tableMetaData, "Table");
|
||||
}
|
||||
}
|
||||
|
||||
for(QProcessMetaData processMetaData : instance.getProcesses().values())
|
||||
{
|
||||
if(processMetaData.getIsHidden())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QPermissionRules rules = getEffectivePermissionRules(processMetaData, instance);
|
||||
String baseName = getEffectivePermissionBaseName(rules, processMetaData.getName());
|
||||
addEffectiveAvailablePermission(rules, PrivatePermissionSubType.HAS_ACCESS, rs, baseName, processMetaData, "Process");
|
||||
}
|
||||
|
||||
for(QAppMetaData appMetaData : instance.getApps().values())
|
||||
{
|
||||
QPermissionRules rules = getEffectivePermissionRules(appMetaData, instance);
|
||||
String baseName = getEffectivePermissionBaseName(rules, appMetaData.getName());
|
||||
addEffectiveAvailablePermission(rules, PrivatePermissionSubType.HAS_ACCESS, rs, baseName, appMetaData, "App");
|
||||
}
|
||||
|
||||
for(QReportMetaData reportMetaData : instance.getReports().values())
|
||||
{
|
||||
QPermissionRules rules = getEffectivePermissionRules(reportMetaData, instance);
|
||||
String baseName = getEffectivePermissionBaseName(rules, reportMetaData.getName());
|
||||
addEffectiveAvailablePermission(rules, PrivatePermissionSubType.HAS_ACCESS, rs, baseName, reportMetaData, "Report");
|
||||
}
|
||||
|
||||
for(QWidgetMetaDataInterface widgetMetaData : instance.getWidgets().values())
|
||||
{
|
||||
QPermissionRules rules = getEffectivePermissionRules(widgetMetaData, instance);
|
||||
String baseName = getEffectivePermissionBaseName(rules, widgetMetaData.getName());
|
||||
addEffectiveAvailablePermission(rules, PrivatePermissionSubType.HAS_ACCESS, rs, baseName, widgetMetaData, "Widget");
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void addEffectiveAvailablePermission(QPermissionRules rules, PermissionSubType permissionSubType, Collection<AvailablePermission> rs, String baseName, MetaDataWithName metaDataWithName, String objectType)
|
||||
{
|
||||
PermissionSubType effectivePermissionSubType = getEffectivePermissionSubType(rules, permissionSubType);
|
||||
if(effectivePermissionSubType != null)
|
||||
{
|
||||
rs.add(new AvailablePermission()
|
||||
.withName(getPermissionName(baseName, effectivePermissionSubType))
|
||||
.withObjectName(metaDataWithName.getLabel())
|
||||
.withPermissionType(effectivePermissionSubType.toString())
|
||||
.withObjectType(objectType));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QPermissionRules getEffectivePermissionRules(MetaDataWithPermissionRules metaDataWithPermissionRules, QInstance instance)
|
||||
{
|
||||
return (metaDataWithPermissionRules.getPermissionRules());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
static boolean hasPermission(QSession session, String permissionBaseName, PermissionSubType permissionSubType)
|
||||
{
|
||||
if(permissionSubType == null)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
String permissionName = getPermissionName(permissionBaseName, permissionSubType);
|
||||
return (session.hasPermission(permissionName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
static PermissionCheckResult getPermissionCheckResult(AbstractActionInput actionInput, QPermissionRules rules, String permissionBaseName, PermissionSubType... permissionSubTypes)
|
||||
{
|
||||
for(PermissionSubType permissionSubType : permissionSubTypes)
|
||||
{
|
||||
PermissionSubType effectivePermissionSubType = getEffectivePermissionSubType(rules, permissionSubType);
|
||||
if(hasPermission(actionInput.getSession(), permissionBaseName, effectivePermissionSubType))
|
||||
{
|
||||
return (PermissionCheckResult.ALLOW);
|
||||
}
|
||||
}
|
||||
|
||||
return (getPermissionDeniedCheckResult(rules));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
static String getEffectivePermissionBaseName(QPermissionRules rules, String standardName)
|
||||
{
|
||||
if(rules != null && StringUtils.hasContent(rules.getPermissionBaseName()))
|
||||
{
|
||||
return (rules.getPermissionBaseName());
|
||||
}
|
||||
|
||||
return (standardName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
static PermissionSubType getEffectivePermissionSubType(QPermissionRules rules, PermissionSubType originalPermissionSubType)
|
||||
{
|
||||
if(rules == null || rules.getLevel() == null)
|
||||
{
|
||||
return (originalPermissionSubType);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the original permission sub-type is "hasAccess" - then this is a check for a process/report/widget. //
|
||||
// in that case - never return the table-level read/write/insert/edit/delete options //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(PrivatePermissionSubType.HAS_ACCESS.equals(originalPermissionSubType))
|
||||
{
|
||||
return switch(rules.getLevel())
|
||||
{
|
||||
case NOT_PROTECTED -> null;
|
||||
default -> PrivatePermissionSubType.HAS_ACCESS;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else, this is a table check - so - based on the rules being used for this table, map the requested //
|
||||
// permission sub-type to what we expect to be set for the table //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
return switch(rules.getLevel())
|
||||
{
|
||||
case NOT_PROTECTED -> null;
|
||||
case HAS_ACCESS_PERMISSION -> PrivatePermissionSubType.HAS_ACCESS;
|
||||
case READ_WRITE_PERMISSIONS ->
|
||||
{
|
||||
if(PrivatePermissionSubType.READ.equals(originalPermissionSubType) || PrivatePermissionSubType.WRITE.equals(originalPermissionSubType))
|
||||
{
|
||||
yield (originalPermissionSubType);
|
||||
}
|
||||
else if(TablePermissionSubType.INSERT.equals(originalPermissionSubType) || TablePermissionSubType.EDIT.equals(originalPermissionSubType) || TablePermissionSubType.DELETE.equals(originalPermissionSubType))
|
||||
{
|
||||
yield (PrivatePermissionSubType.WRITE);
|
||||
}
|
||||
else if(TablePermissionSubType.READ.equals(originalPermissionSubType))
|
||||
{
|
||||
yield (PrivatePermissionSubType.READ);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("Unexpected permissionSubType: " + originalPermissionSubType);
|
||||
}
|
||||
}
|
||||
case READ_INSERT_EDIT_DELETE_PERMISSIONS -> originalPermissionSubType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void commonCheckPermissionThrowing(QPermissionRules rules, PermissionSubType permissionSubType, String name, AbstractActionInput actionInput) throws QPermissionDeniedException
|
||||
{
|
||||
PermissionSubType effectivePermissionSubType = getEffectivePermissionSubType(rules, permissionSubType);
|
||||
String permissionBaseName = getEffectivePermissionBaseName(rules, name);
|
||||
|
||||
if(effectivePermissionSubType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!hasPermission(actionInput.getSession(), permissionBaseName, effectivePermissionSubType))
|
||||
{
|
||||
LOG.debug("Throwing permission denied for: " + getPermissionName(permissionBaseName, effectivePermissionSubType) + " for " + actionInput.getSession().getUser());
|
||||
throw (new QPermissionDeniedException("Permission denied."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static String getPermissionName(String permissionBaseName, PermissionSubType permissionSubType)
|
||||
{
|
||||
return permissionBaseName + "." + permissionSubType.getPermissionSuffix();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static PermissionCheckResult getPermissionDeniedCheckResult(QPermissionRules rules)
|
||||
{
|
||||
if(rules == null || rules.getDenyBehavior() == null || rules.getDenyBehavior().equals(DenyBehavior.HIDDEN))
|
||||
{
|
||||
return (PermissionCheckResult.DENY_HIDE);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PermissionCheckResult.DENY_DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void warnAboutPermissionSubTypeForTables(PermissionSubType permissionSubType)
|
||||
{
|
||||
if(permissionSubType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(permissionSubType == PrivatePermissionSubType.HAS_ACCESS)
|
||||
{
|
||||
LOG.warn("PermissionSubType.HAS_ACCESS should not be checked for a table");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
enum PrivatePermissionSubType implements PermissionSubType
|
||||
{
|
||||
HAS_ACCESS("hasAccess"), // for processes, reports, etc - basically, not tables.
|
||||
READ("read"), // for a table in read/write mode - or - for read (query, get, count) on a table in full-mode
|
||||
WRITE("write");
|
||||
|
||||
private final String permissionSuffix;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
PrivatePermissionSubType(String permissionSuffix)
|
||||
{
|
||||
this.permissionSuffix = permissionSuffix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionSuffix
|
||||
*******************************************************************************/
|
||||
public String getPermissionSuffix()
|
||||
{
|
||||
return (this.permissionSuffix);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ReportProcessPermissionChecker implements CustomPermissionChecker
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void checkPermissionsThrowing(AbstractActionInput actionInput, MetaDataWithPermissionRules metaDataWithPermissionRules) throws QPermissionDeniedException
|
||||
{
|
||||
if(actionInput instanceof RunProcessInput runProcessInput)
|
||||
{
|
||||
String reportName = runProcessInput.getValueString("reportName");
|
||||
if(reportName != null)
|
||||
{
|
||||
PermissionsHelper.checkReportPermissionThrowing(actionInput, reportName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum TablePermissionSubType implements PermissionSubType
|
||||
{
|
||||
READ("read"), // for a table in read/write mode - or - for read (query, get, count) on a table in full-mode
|
||||
INSERT("insert"), // for table-insert.
|
||||
EDIT("edit"), // for table-edit.
|
||||
DELETE("delete"); // for table-delete.
|
||||
|
||||
private final String permissionSuffix;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
TablePermissionSubType(String permissionSuffix)
|
||||
{
|
||||
this.permissionSuffix = permissionSuffix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionSuffix
|
||||
*******************************************************************************/
|
||||
public String getPermissionSuffix()
|
||||
{
|
||||
return (this.permissionSuffix);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.exceptions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Exception thrown if user doesn't have permission for an action
|
||||
*
|
||||
*******************************************************************************/
|
||||
public class QPermissionDeniedException extends QException
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor of message
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QPermissionDeniedException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor of message & cause
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QPermissionDeniedException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -32,8 +32,12 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.BulkTableActionProcessPermissionChecker;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
@ -42,6 +46,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||
@ -133,6 +139,21 @@ public class QInstanceEnricher
|
||||
{
|
||||
qInstance.getPossibleValueSources().values().forEach(this::enrichPossibleValueSource);
|
||||
}
|
||||
|
||||
if(qInstance.getWidgets() != null)
|
||||
{
|
||||
qInstance.getWidgets().values().forEach(this::enrichWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void enrichWidget(QWidgetMetaDataInterface widgetMetaData)
|
||||
{
|
||||
enrichPermissionRules(widgetMetaData);
|
||||
}
|
||||
|
||||
|
||||
@ -175,6 +196,60 @@ public class QInstanceEnricher
|
||||
{
|
||||
table.setRecordLabelFormat(String.join(" ", Collections.nCopies(table.getRecordLabelFields().size(), "%s")));
|
||||
}
|
||||
|
||||
enrichPermissionRules(table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void enrichPermissionRules(MetaDataWithPermissionRules metaDataWithPermissionRules)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// make sure there's a permissionsRule object in the metaData object //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
if(metaDataWithPermissionRules.getPermissionRules() == null)
|
||||
{
|
||||
if(qInstance.getDefaultPermissionRules() != null)
|
||||
{
|
||||
metaDataWithPermissionRules.setPermissionRules(qInstance.getDefaultPermissionRules().clone());
|
||||
}
|
||||
else
|
||||
{
|
||||
metaDataWithPermissionRules.setPermissionRules(QPermissionRules.defaultInstance().clone());
|
||||
}
|
||||
}
|
||||
|
||||
QPermissionRules permissionRules = metaDataWithPermissionRules.getPermissionRules();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// now make sure the required fields are all set in the permissionRules object //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
if(permissionRules.getLevel() == null)
|
||||
{
|
||||
if(qInstance.getDefaultPermissionRules() != null && qInstance.getDefaultPermissionRules().getLevel() != null)
|
||||
{
|
||||
permissionRules.setLevel(qInstance.getDefaultPermissionRules().getLevel());
|
||||
}
|
||||
else
|
||||
{
|
||||
permissionRules.setLevel(QPermissionRules.defaultInstance().getLevel());
|
||||
}
|
||||
}
|
||||
|
||||
if(permissionRules.getDenyBehavior() == null)
|
||||
{
|
||||
if(qInstance.getDefaultPermissionRules() != null && qInstance.getDefaultPermissionRules().getDenyBehavior() != null)
|
||||
{
|
||||
permissionRules.setDenyBehavior(qInstance.getDefaultPermissionRules().getDenyBehavior());
|
||||
}
|
||||
else
|
||||
{
|
||||
permissionRules.setDenyBehavior(QPermissionRules.defaultInstance().getDenyBehavior());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -193,6 +268,8 @@ public class QInstanceEnricher
|
||||
{
|
||||
process.getStepList().forEach(this::enrichStep);
|
||||
}
|
||||
|
||||
enrichPermissionRules(process);
|
||||
}
|
||||
|
||||
|
||||
@ -323,6 +400,8 @@ public class QInstanceEnricher
|
||||
{
|
||||
enrichAppSection(section);
|
||||
}
|
||||
|
||||
enrichPermissionRules(app);
|
||||
}
|
||||
|
||||
|
||||
@ -411,6 +490,8 @@ public class QInstanceEnricher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enrichPermissionRules(report);
|
||||
}
|
||||
|
||||
|
||||
@ -506,7 +587,9 @@ public class QInstanceEnricher
|
||||
.withName(processName)
|
||||
.withLabel(table.getLabel() + " Bulk Insert")
|
||||
.withTableName(table.getName())
|
||||
.withIsHidden(true);
|
||||
.withIsHidden(true)
|
||||
.withPermissionRules(qInstance.getDefaultPermissionRules().clone()
|
||||
.withCustomPermissionChecker(new QCodeReference(BulkTableActionProcessPermissionChecker.class, QCodeUsage.CUSTOMIZER)));
|
||||
|
||||
List<QFieldMetaData> editableFields = new ArrayList<>();
|
||||
for(QFieldSection section : CollectionUtils.nonNullList(table.getSections()))
|
||||
@ -568,7 +651,9 @@ public class QInstanceEnricher
|
||||
.withName(processName)
|
||||
.withLabel(table.getLabel() + " Bulk Edit")
|
||||
.withTableName(table.getName())
|
||||
.withIsHidden(true);
|
||||
.withIsHidden(true)
|
||||
.withPermissionRules(qInstance.getDefaultPermissionRules().clone()
|
||||
.withCustomPermissionChecker(new QCodeReference(BulkTableActionProcessPermissionChecker.class, QCodeUsage.CUSTOMIZER)));
|
||||
|
||||
List<QFieldMetaData> editableFields = table.getFields().values().stream()
|
||||
.filter(QFieldMetaData::getIsEditable)
|
||||
@ -613,7 +698,9 @@ public class QInstanceEnricher
|
||||
.withName(processName)
|
||||
.withLabel(table.getLabel() + " Bulk Delete")
|
||||
.withTableName(table.getName())
|
||||
.withIsHidden(true);
|
||||
.withIsHidden(true)
|
||||
.withPermissionRules(qInstance.getDefaultPermissionRules().clone()
|
||||
.withCustomPermissionChecker(new QCodeReference(BulkTableActionProcessPermissionChecker.class, QCodeUsage.CUSTOMIZER)));
|
||||
|
||||
List<QFieldMetaData> tableFields = table.getFields().values().stream().toList();
|
||||
process.getFrontendStep("review").setRecordListFields(tableFields);
|
||||
|
@ -61,6 +61,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMeta
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportField;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
@ -138,6 +140,7 @@ public class QInstanceValidator
|
||||
validatePossibleValueSources(qInstance);
|
||||
validateQueuesAndProviders(qInstance);
|
||||
validateJoins(qInstance);
|
||||
validateSecurityKeyTypes(qInstance);
|
||||
|
||||
validateUniqueTopLevelNames(qInstance);
|
||||
}
|
||||
@ -156,6 +159,35 @@ public class QInstanceValidator
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void validateSecurityKeyTypes(QInstance qInstance)
|
||||
{
|
||||
Set<String> usedNames = new HashSet<>();
|
||||
qInstance.getSecurityKeyTypes().forEach((name, securityKeyType) ->
|
||||
{
|
||||
if(assertCondition(StringUtils.hasContent(securityKeyType.getName()), "Missing name for a securityKeyType"))
|
||||
{
|
||||
assertCondition(Objects.equals(name, securityKeyType.getName()), "Inconsistent naming for securityKeyType: " + name + "/" + securityKeyType.getName() + ".");
|
||||
assertCondition(!usedNames.contains(name), "More than one SecurityKeyType with name (or allAccessKeyName) of: " + name);
|
||||
usedNames.add(name);
|
||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
||||
{
|
||||
assertCondition(!usedNames.contains(securityKeyType.getAllAccessKeyName()), "More than one SecurityKeyType with name (or allAccessKeyName) of: " + securityKeyType.getAllAccessKeyName());
|
||||
usedNames.add(securityKeyType.getAllAccessKeyName());
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(securityKeyType.getPossibleValueSourceName()))
|
||||
{
|
||||
assertCondition(qInstance.getPossibleValueSource(securityKeyType.getPossibleValueSourceName()) != null, "Unrecognized possibleValueSourceName in securityKeyType: " + name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -387,17 +419,11 @@ public class QInstanceValidator
|
||||
}
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(table.getFields()))
|
||||
for(String fieldName : CollectionUtils.nonNullMap(table.getFields()).keySet())
|
||||
{
|
||||
for(String fieldName : table.getFields().keySet())
|
||||
{
|
||||
assertCondition(fieldNamesInSections.contains(fieldName), "Table " + tableName + " field " + fieldName + " is not listed in any field sections.");
|
||||
}
|
||||
assertCondition(fieldNamesInSections.contains(fieldName), "Table " + tableName + " field " + fieldName + " is not listed in any field sections.");
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// validate the record label //
|
||||
///////////////////////////////
|
||||
if(table.getRecordLabelFields() != null && table.getFields() != null)
|
||||
{
|
||||
for(String recordLabelField : table.getRecordLabelFields())
|
||||
@ -406,51 +432,51 @@ public class QInstanceValidator
|
||||
}
|
||||
}
|
||||
|
||||
if(table.getCustomizers() != null)
|
||||
for(Map.Entry<String, QCodeReference> entry : CollectionUtils.nonNullMap(table.getCustomizers()).entrySet())
|
||||
{
|
||||
for(Map.Entry<String, QCodeReference> entry : table.getCustomizers().entrySet())
|
||||
{
|
||||
validateTableCustomizer(tableName, entry.getKey(), entry.getValue());
|
||||
}
|
||||
validateTableCustomizer(tableName, entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// validate the table's automations //
|
||||
//////////////////////////////////////
|
||||
if(table.getAutomationDetails() != null)
|
||||
{
|
||||
validateTableAutomationDetails(qInstance, table);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// validate the table's unique keys //
|
||||
//////////////////////////////////////
|
||||
if(table.getUniqueKeys() != null)
|
||||
{
|
||||
validateTableUniqueKeys(table);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// validate the table's associated scripts //
|
||||
/////////////////////////////////////////////
|
||||
if(table.getAssociatedScripts() != null)
|
||||
{
|
||||
validateAssociatedScripts(table);
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// validate cacheOf //
|
||||
//////////////////////
|
||||
if(table.getCacheOf() != null)
|
||||
{
|
||||
validateTableCacheOf(qInstance, table);
|
||||
}
|
||||
validateTableAutomationDetails(qInstance, table);
|
||||
validateTableUniqueKeys(table);
|
||||
validateAssociatedScripts(table);
|
||||
validateTableCacheOf(qInstance, table);
|
||||
validateTableRecordSecurityLocks(qInstance, table);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void validateTableRecordSecurityLocks(QInstance qInstance, QTableMetaData table)
|
||||
{
|
||||
String prefix = "Table " + table.getName() + " ";
|
||||
|
||||
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(table.getRecordSecurityLocks()))
|
||||
{
|
||||
String securityKeyTypeName = recordSecurityLock.getSecurityKeyType();
|
||||
if(assertCondition(StringUtils.hasContent(securityKeyTypeName), prefix + "has a recordSecurityLock that is missing a securityKeyType"))
|
||||
{
|
||||
assertCondition(qInstance.getSecurityKeyType(securityKeyTypeName) != null, prefix + "has a recordSecurityLock with an unrecognized securityKeyType: " + securityKeyTypeName);
|
||||
}
|
||||
|
||||
prefix = "Table " + table.getName() + " recordSecurityLock (of key type " + securityKeyTypeName + ") ";
|
||||
|
||||
String fieldName = recordSecurityLock.getFieldName();
|
||||
if(assertCondition(StringUtils.hasContent(fieldName), prefix + "is missing a fieldName"))
|
||||
{
|
||||
assertCondition(findField(qInstance, table, null, fieldName), prefix + "has an unrecognized fieldName: " + fieldName);
|
||||
}
|
||||
|
||||
assertCondition(recordSecurityLock.getNullValueBehavior() != null, prefix + "is missing a nullValueBehavior");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -465,16 +491,36 @@ public class QInstanceValidator
|
||||
"Unrecognized possibleValueSourceName " + field.getPossibleValueSourceName() + " in table " + tableName + " for field " + fieldName + ".");
|
||||
}
|
||||
|
||||
String prefix = "Field " + fieldName + " in table " + tableName + " ";
|
||||
|
||||
ValueTooLongBehavior behavior = field.getBehavior(qInstance, ValueTooLongBehavior.class);
|
||||
if(behavior != null && !behavior.equals(ValueTooLongBehavior.PASS_THROUGH))
|
||||
{
|
||||
assertCondition(field.getMaxLength() != null, "Field " + fieldName + " in table " + tableName + " specifies a ValueTooLongBehavior, but not a maxLength.");
|
||||
assertCondition(field.getMaxLength() != null, prefix + "specifies a ValueTooLongBehavior, but not a maxLength.");
|
||||
}
|
||||
|
||||
if(field.getMaxLength() != null)
|
||||
{
|
||||
assertCondition(field.getMaxLength() > 0, "Field " + fieldName + " in table " + tableName + " has an invalid maxLength (" + field.getMaxLength() + ") - must be greater than 0.");
|
||||
assertCondition(field.getType().isStringLike(), "Field " + fieldName + " in table " + tableName + " has maxLength, but is not of a supported type (" + field.getType() + ") - must be a string-like type.");
|
||||
assertCondition(field.getMaxLength() > 0, prefix + "has an invalid maxLength (" + field.getMaxLength() + ") - must be greater than 0.");
|
||||
assertCondition(field.getType().isStringLike(), prefix + "has maxLength, but is not of a supported type (" + field.getType() + ") - must be a string-like type.");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this condition doesn't make sense/apply - because the default value-too-long behavior is pass-through, so, idk, just omit //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// assertCondition(behavior != null, prefix + "specifies a maxLength, but no ValueTooLongBehavior.");
|
||||
}
|
||||
|
||||
FieldSecurityLock fieldSecurityLock = field.getFieldSecurityLock();
|
||||
if(fieldSecurityLock != null)
|
||||
{
|
||||
String securityKeyTypeName = fieldSecurityLock.getSecurityKeyType();
|
||||
if(assertCondition(StringUtils.hasContent(securityKeyTypeName), prefix + "has a fieldSecurityLock that is missing a securityKeyType"))
|
||||
{
|
||||
assertCondition(qInstance.getSecurityKeyType(securityKeyTypeName) != null, prefix + "has a fieldSecurityLock with an unrecognized securityKeyType: " + securityKeyTypeName);
|
||||
}
|
||||
|
||||
assertCondition(fieldSecurityLock.getDefaultBehavior() != null, prefix + "has a fieldSecurityLock that is missing a defaultBehavior");
|
||||
assertCondition(CollectionUtils.nullSafeHasContents(fieldSecurityLock.getOverrideValues()), prefix + "has a fieldSecurityLock that is missing overrideValues");
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,9 +531,14 @@ public class QInstanceValidator
|
||||
*******************************************************************************/
|
||||
private void validateTableCacheOf(QInstance qInstance, QTableMetaData table)
|
||||
{
|
||||
CacheOf cacheOf = table.getCacheOf();
|
||||
String prefix = "Table " + table.getName() + " cacheOf ";
|
||||
String sourceTableName = cacheOf.getSourceTable();
|
||||
CacheOf cacheOf = table.getCacheOf();
|
||||
if(cacheOf == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String prefix = "Table " + table.getName() + " cacheOf ";
|
||||
String sourceTableName = cacheOf.getSourceTable();
|
||||
if(assertCondition(StringUtils.hasContent(sourceTableName), prefix + "is missing a sourceTable name"))
|
||||
{
|
||||
assertCondition(qInstance.getTable(sourceTableName) != null, prefix + "is referencing an unknown sourceTable: " + sourceTableName);
|
||||
@ -519,7 +570,7 @@ public class QInstanceValidator
|
||||
private void validateAssociatedScripts(QTableMetaData table)
|
||||
{
|
||||
Set<String> usedFieldNames = new HashSet<>();
|
||||
for(AssociatedScript associatedScript : table.getAssociatedScripts())
|
||||
for(AssociatedScript associatedScript : CollectionUtils.nonNullList(table.getAssociatedScripts()))
|
||||
{
|
||||
if(assertCondition(StringUtils.hasContent(associatedScript.getFieldName()), "Table " + table.getName() + " has an associatedScript without a fieldName"))
|
||||
{
|
||||
@ -548,7 +599,7 @@ public class QInstanceValidator
|
||||
private void validateTableUniqueKeys(QTableMetaData table)
|
||||
{
|
||||
Set<Set<String>> ukSets = new HashSet<>();
|
||||
for(UniqueKey uniqueKey : table.getUniqueKeys())
|
||||
for(UniqueKey uniqueKey : CollectionUtils.nonNullList(table.getUniqueKeys()))
|
||||
{
|
||||
if(assertCondition(CollectionUtils.nullSafeHasContents(uniqueKey.getFieldNames()), table.getName() + " has a uniqueKey with no fields"))
|
||||
{
|
||||
@ -573,11 +624,15 @@ public class QInstanceValidator
|
||||
*******************************************************************************/
|
||||
private void validateTableAutomationDetails(QInstance qInstance, QTableMetaData table)
|
||||
{
|
||||
QTableAutomationDetails automationDetails = table.getAutomationDetails();
|
||||
if(automationDetails == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String tableName = table.getName();
|
||||
String prefix = "Table " + tableName + " automationDetails ";
|
||||
|
||||
QTableAutomationDetails automationDetails = table.getAutomationDetails();
|
||||
|
||||
//////////////////////////////////////
|
||||
// validate the automation provider //
|
||||
//////////////////////////////////////
|
||||
@ -1028,7 +1083,7 @@ public class QInstanceValidator
|
||||
assertCondition(usedDataSourceNames.contains(view.getVarianceDataSourceName()), viewErrorPrefix + "has an unrecognized varianceDataSourceName: " + view.getVarianceDataSourceName());
|
||||
}
|
||||
|
||||
boolean hasColumns = CollectionUtils.nullSafeHasContents(view.getColumns());
|
||||
boolean hasColumns = CollectionUtils.nullSafeHasContents(view.getColumns());
|
||||
boolean hasViewCustomizer = view.getViewCustomizer() != null;
|
||||
assertCondition(hasColumns || hasViewCustomizer, viewErrorPrefix + "does not have any columns or a view customizer.");
|
||||
|
||||
@ -1092,7 +1147,7 @@ public class QInstanceValidator
|
||||
/*******************************************************************************
|
||||
** Look for a field name in either a table, or the tables referenced in a list of query joins.
|
||||
*******************************************************************************/
|
||||
private static boolean findField(QInstance qInstance, QTableMetaData table, List<QueryJoin> queryJoins, String fieldName)
|
||||
private boolean findField(QInstance qInstance, QTableMetaData table, List<QueryJoin> queryJoins, String fieldName)
|
||||
{
|
||||
boolean foundField = false;
|
||||
try
|
||||
@ -1105,22 +1160,34 @@ public class QInstanceValidator
|
||||
if(fieldName.contains("."))
|
||||
{
|
||||
String fieldNameAfterDot = fieldName.substring(fieldName.lastIndexOf(".") + 1);
|
||||
for(QueryJoin queryJoin : CollectionUtils.nonNullList(queryJoins))
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(queryJoins))
|
||||
{
|
||||
QTableMetaData joinTable = qInstance.getTable(queryJoin.getJoinTable());
|
||||
if(joinTable != null)
|
||||
for(QueryJoin queryJoin : CollectionUtils.nonNullList(queryJoins))
|
||||
{
|
||||
try
|
||||
QTableMetaData joinTable = qInstance.getTable(queryJoin.getJoinTable());
|
||||
if(joinTable != null)
|
||||
{
|
||||
joinTable.getField(fieldNameAfterDot);
|
||||
foundField = true;
|
||||
}
|
||||
catch(Exception e2)
|
||||
{
|
||||
continue;
|
||||
try
|
||||
{
|
||||
joinTable.getField(fieldNameAfterDot);
|
||||
foundField = true;
|
||||
}
|
||||
catch(Exception e2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errors.add("QInstanceValidator does not yet support finding a field that looks like a join field, but isn't associated with a query.");
|
||||
return (true);
|
||||
// todo! for(QJoinMetaData join : CollectionUtils.nonNullMap(qInstance.getJoins()).values())
|
||||
// {
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundField;
|
||||
|
@ -186,4 +186,27 @@ public class AbstractActionInput
|
||||
{
|
||||
this.asyncJobCallback = asyncJobCallback;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for instance
|
||||
*******************************************************************************/
|
||||
public AbstractActionInput withInstance(QInstance instance)
|
||||
{
|
||||
this.instance = instance;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for session
|
||||
*******************************************************************************/
|
||||
public AbstractActionInput withSession(QSession session)
|
||||
{
|
||||
this.session = session;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,13 +25,14 @@ package com.kingsrook.qqq.backend.core.model.actions;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base class for input for any qqq action that works against a table.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public abstract class AbstractTableActionInput extends AbstractActionInput
|
||||
public class AbstractTableActionInput extends AbstractActionInput
|
||||
{
|
||||
private String tableName;
|
||||
|
||||
@ -95,4 +96,28 @@ public abstract class AbstractTableActionInput extends AbstractActionInput
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName
|
||||
*******************************************************************************/
|
||||
public AbstractTableActionInput withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for session
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public AbstractTableActionInput withSession(QSession session)
|
||||
{
|
||||
super.withSession(session);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
@ -30,11 +30,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
** Input for meta-data for a table.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TableMetaDataInput extends AbstractActionInput
|
||||
public class TableMetaDataInput extends AbstractTableActionInput
|
||||
{
|
||||
private String tableName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@ -53,25 +50,4 @@ public class TableMetaDataInput extends AbstractActionInput
|
||||
super(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
@ -69,6 +70,37 @@ public class JoinsContext
|
||||
}
|
||||
aliasToTableNameMap.put(tableNameOrAlias, joinTable.getName());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// ensure any joins that contribute a recordLock are present //
|
||||
///////////////////////////////////////////////////////////////
|
||||
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(instance.getTable(tableName).getRecordSecurityLocks()))
|
||||
{
|
||||
for(String joinName : CollectionUtils.nonNullList(recordSecurityLock.getJoinChain()))
|
||||
{
|
||||
if(this.queryJoins.stream().anyMatch(qj -> qj.getJoinMetaData().getName().equals(joinName)))
|
||||
{
|
||||
///////////////////////////////////////////////////////
|
||||
// we're good - we're already joining on this table! //
|
||||
///////////////////////////////////////////////////////
|
||||
}
|
||||
else
|
||||
{
|
||||
this.queryJoins.add(new QueryJoin().withJoinMetaData(instance.getJoin(joinName)).withType(QueryJoin.Type.INNER)); // todo aliases? probably.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* todo!!
|
||||
for(QueryJoin queryJoin : queryJoins)
|
||||
{
|
||||
QTableMetaData joinTable = instance.getTable(queryJoin.getJoinTable());
|
||||
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(joinTable.getRecordSecurityLocks()))
|
||||
{
|
||||
// addCriteriaForRecordSecurityLock(instance, session, joinTable, securityCriteria, recordSecurityLock, joinsContext, queryJoin.getJoinTableOrItsAlias());
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -32,6 +32,7 @@ public enum QCriteriaOperator
|
||||
NOT_EQUALS,
|
||||
IN,
|
||||
NOT_IN,
|
||||
IS_NULL_OR_IN,
|
||||
STARTS_WITH,
|
||||
ENDS_WITH,
|
||||
CONTAINS,
|
||||
|
@ -340,7 +340,7 @@ public class QQueryFilter implements Serializable, Cloneable
|
||||
{
|
||||
for(QFilterCriteria criterion : CollectionUtils.nonNullList(criteria))
|
||||
{
|
||||
rs.append(criterion).append(" ").append(getBooleanOperator());
|
||||
rs.append(criterion).append(" ").append(getBooleanOperator()).append(" ");
|
||||
}
|
||||
|
||||
for(QQueryFilter subFilter : CollectionUtils.nonNullList(subFilters))
|
||||
|
@ -25,8 +25,10 @@ package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
@ -42,13 +44,16 @@ import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNodeType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.QQueueMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.QQueueProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
import io.github.cdimascio.dotenv.DotenvEntry;
|
||||
@ -73,20 +78,21 @@ public class QInstance
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Important to use LinkedHashmap here, to preserve the order in which entries are added. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private Map<String, QTableMetaData> tables = new LinkedHashMap<>();
|
||||
private Map<String, QJoinMetaData> joins = new LinkedHashMap<>();
|
||||
private Map<String, QPossibleValueSource> possibleValueSources = new LinkedHashMap<>();
|
||||
private Map<String, QProcessMetaData> processes = new LinkedHashMap<>();
|
||||
private Map<String, QAppMetaData> apps = new LinkedHashMap<>();
|
||||
private Map<String, QReportMetaData> reports = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, QWidgetMetaDataInterface> widgets = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, QQueueProviderMetaData> queueProviders = new LinkedHashMap<>();
|
||||
private Map<String, QQueueMetaData> queues = new LinkedHashMap<>();
|
||||
private Map<String, QTableMetaData> tables = new LinkedHashMap<>();
|
||||
private Map<String, QJoinMetaData> joins = new LinkedHashMap<>();
|
||||
private Map<String, QPossibleValueSource> possibleValueSources = new LinkedHashMap<>();
|
||||
private Map<String, QProcessMetaData> processes = new LinkedHashMap<>();
|
||||
private Map<String, QAppMetaData> apps = new LinkedHashMap<>();
|
||||
private Map<String, QReportMetaData> reports = new LinkedHashMap<>();
|
||||
private Map<String, QSecurityKeyType> securityKeyTypes = new LinkedHashMap<>();
|
||||
private Map<String, QWidgetMetaDataInterface> widgets = new LinkedHashMap<>();
|
||||
private Map<String, QQueueProviderMetaData> queueProviders = new LinkedHashMap<>();
|
||||
private Map<String, QQueueMetaData> queues = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, String> environmentValues = new LinkedHashMap<>();
|
||||
|
||||
private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance();
|
||||
|
||||
// todo - lock down the object (no more changes allowed) after it's been validated?
|
||||
|
||||
@JsonIgnore
|
||||
@ -249,16 +255,7 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addBackend(QBackendMetaData backend)
|
||||
{
|
||||
addBackend(backend.getName(), backend);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addBackend(String name, QBackendMetaData backend)
|
||||
{
|
||||
String name = backend.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a backend without a name."));
|
||||
@ -309,16 +306,7 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addTable(QTableMetaData table)
|
||||
{
|
||||
addTable(table.getName(), table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addTable(String name, QTableMetaData table)
|
||||
{
|
||||
String name = table.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a table without a name."));
|
||||
@ -374,16 +362,7 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addJoin(QJoinMetaData join)
|
||||
{
|
||||
addJoin(join.getName(), join);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addJoin(String name, QJoinMetaData join)
|
||||
{
|
||||
String name = join.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a join without a name."));
|
||||
@ -439,16 +418,7 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addPossibleValueSource(QPossibleValueSource possibleValueSource)
|
||||
{
|
||||
this.addPossibleValueSource(possibleValueSource.getName(), possibleValueSource);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addPossibleValueSource(String name, QPossibleValueSource possibleValueSource)
|
||||
{
|
||||
String name = possibleValueSource.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a possibleValueSource without a name."));
|
||||
@ -515,16 +485,7 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addProcess(QProcessMetaData process)
|
||||
{
|
||||
this.addProcess(process.getName(), process);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addProcess(String name, QProcessMetaData process)
|
||||
{
|
||||
String name = process.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a process without a name."));
|
||||
@ -575,19 +536,10 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addApp(QAppMetaData app)
|
||||
{
|
||||
this.addApp(app.getName(), app);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addApp(String name, QAppMetaData app)
|
||||
{
|
||||
String name = app.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add an app without a name."));
|
||||
throw (new IllegalArgumentException("Attempted to add a app without a name."));
|
||||
}
|
||||
if(this.apps.containsKey(name))
|
||||
{
|
||||
@ -635,19 +587,10 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addReport(QReportMetaData report)
|
||||
{
|
||||
this.addReport(report.getName(), report);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addReport(String name, QReportMetaData report)
|
||||
{
|
||||
String name = report.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add an report without a name."));
|
||||
throw (new IllegalArgumentException("Attempted to add a report without a name."));
|
||||
}
|
||||
if(this.reports.containsKey(name))
|
||||
{
|
||||
@ -693,9 +636,14 @@ public class QInstance
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addAutomationProvider(QAutomationProviderMetaData automationProvider)
|
||||
public void addSecurityKeyType(QSecurityKeyType securityKeyType)
|
||||
{
|
||||
this.addAutomationProvider(automationProvider.getName(), automationProvider);
|
||||
String name = securityKeyType.getName();
|
||||
if(this.securityKeyTypes.containsKey(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a second securityKeyType with name: " + name));
|
||||
}
|
||||
this.securityKeyTypes.put(name, securityKeyType);
|
||||
}
|
||||
|
||||
|
||||
@ -703,8 +651,41 @@ public class QInstance
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addAutomationProvider(String name, QAutomationProviderMetaData automationProvider)
|
||||
public QSecurityKeyType getSecurityKeyType(String name)
|
||||
{
|
||||
return (this.securityKeyTypes.get(name));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for securityKeyTypes
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, QSecurityKeyType> getSecurityKeyTypes()
|
||||
{
|
||||
return securityKeyTypes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for securityKeyTypes
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSecurityKeyTypes(Map<String, QSecurityKeyType> securityKeyTypes)
|
||||
{
|
||||
this.securityKeyTypes = securityKeyTypes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addAutomationProvider(QAutomationProviderMetaData automationProvider)
|
||||
{
|
||||
String name = automationProvider.getName();
|
||||
if(this.automationProviders.containsKey(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a second automationProvider with name: " + name));
|
||||
@ -839,16 +820,7 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addWidget(QWidgetMetaDataInterface widget)
|
||||
{
|
||||
this.addWidget(widget.getName(), widget);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addWidget(String name, QWidgetMetaDataInterface widget)
|
||||
{
|
||||
String name = widget.getName();
|
||||
if(this.widgets.containsKey(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a second widget with name: " + name));
|
||||
@ -873,19 +845,10 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addQueueProvider(QQueueProviderMetaData queueProvider)
|
||||
{
|
||||
this.addQueueProvider(queueProvider.getName(), queueProvider);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addQueueProvider(String name, QQueueProviderMetaData queueProvider)
|
||||
{
|
||||
String name = queueProvider.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add an queueProvider without a name."));
|
||||
throw (new IllegalArgumentException("Attempted to add a queueProvider without a name."));
|
||||
}
|
||||
if(this.queueProviders.containsKey(name))
|
||||
{
|
||||
@ -933,19 +896,10 @@ public class QInstance
|
||||
*******************************************************************************/
|
||||
public void addQueue(QQueueMetaData queue)
|
||||
{
|
||||
this.addQueue(queue.getName(), queue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addQueue(String name, QQueueMetaData queue)
|
||||
{
|
||||
String name = queue.getName();
|
||||
if(!StringUtils.hasContent(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add an queue without a name."));
|
||||
throw (new IllegalArgumentException("Attempted to add a queue without a name."));
|
||||
}
|
||||
if(this.queues.containsKey(name))
|
||||
{
|
||||
@ -1008,4 +962,54 @@ public class QInstance
|
||||
this.environmentValues = environmentValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for defaultPermissionRules
|
||||
*******************************************************************************/
|
||||
public QPermissionRules getDefaultPermissionRules()
|
||||
{
|
||||
return (this.defaultPermissionRules);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for defaultPermissionRules
|
||||
*******************************************************************************/
|
||||
public void setDefaultPermissionRules(QPermissionRules defaultPermissionRules)
|
||||
{
|
||||
this.defaultPermissionRules = defaultPermissionRules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for defaultPermissionRules
|
||||
*******************************************************************************/
|
||||
public QInstance withDefaultPermissionRules(QPermissionRules defaultPermissionRules)
|
||||
{
|
||||
this.defaultPermissionRules = defaultPermissionRules;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<String> getAllowedSecurityKeyNames()
|
||||
{
|
||||
Set<String> rs = new LinkedHashSet<>();
|
||||
for(QSecurityKeyType securityKeyType : CollectionUtils.nonNullMap(getSecurityKeyTypes()).values())
|
||||
{
|
||||
rs.add(securityKeyType.getName());
|
||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
|
||||
{
|
||||
rs.add(securityKeyType.getAllAccessKeyName());
|
||||
}
|
||||
}
|
||||
return (rs);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData
|
||||
{
|
||||
private String baseUrl;
|
||||
private String clientId;
|
||||
private String audience;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// keep this secret, on the server - don't let it be serialized and sent to a client! //
|
||||
@ -156,4 +157,36 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData
|
||||
{
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for audience
|
||||
*******************************************************************************/
|
||||
public String getAudience()
|
||||
{
|
||||
return (this.audience);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for audience
|
||||
*******************************************************************************/
|
||||
public void setAudience(String audience)
|
||||
{
|
||||
this.audience = audience;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for audience
|
||||
*******************************************************************************/
|
||||
public Auth0AuthenticationMetaData withAudience(String audience)
|
||||
{
|
||||
this.audience = audience;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -44,6 +45,8 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
|
||||
protected Integer gridColumns;
|
||||
protected QCodeReference codeReference;
|
||||
|
||||
private QPermissionRules permissionRules;
|
||||
|
||||
private List<WidgetDropdownData> dropdowns;
|
||||
protected Map<String, Serializable> defaultValues = new LinkedHashMap<>();
|
||||
|
||||
@ -387,4 +390,37 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionRules
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QPermissionRules getPermissionRules()
|
||||
{
|
||||
return (this.permissionRules);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionRules
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void setPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QWidgetMetaData withPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,13 +25,15 @@ package com.kingsrook.qqq.backend.core.model.metadata.dashboard;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface for qqq widget meta data
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface QWidgetMetaDataInterface
|
||||
public interface QWidgetMetaDataInterface extends MetaDataWithPermissionRules
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
@ -148,5 +150,17 @@ public interface QWidgetMetaDataInterface
|
||||
*******************************************************************************/
|
||||
QWidgetMetaData withDefaultValue(String key, Serializable value);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionRules
|
||||
*******************************************************************************/
|
||||
QPermissionRules getPermissionRules();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionRules
|
||||
*******************************************************************************/
|
||||
void setPermissionRules(QPermissionRules permissionRules);
|
||||
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
@ -51,6 +52,8 @@ public class QFieldMetaData implements Cloneable
|
||||
private boolean isRequired = false;
|
||||
private boolean isEditable = true;
|
||||
|
||||
private FieldSecurityLock fieldSecurityLock;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// if we need "only edit on insert" or "only edit on update" in the future, //
|
||||
// propose doing that in a secondary field, e.g., "onlyEditableOn=insert|update" //
|
||||
@ -689,4 +692,35 @@ public class QFieldMetaData implements Cloneable
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldSecurityLock
|
||||
*******************************************************************************/
|
||||
public FieldSecurityLock getFieldSecurityLock()
|
||||
{
|
||||
return (this.fieldSecurityLock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldSecurityLock
|
||||
*******************************************************************************/
|
||||
public void setFieldSecurityLock(FieldSecurityLock fieldSecurityLock)
|
||||
{
|
||||
this.fieldSecurityLock = fieldSecurityLock;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldSecurityLock
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withFieldSecurityLock(FieldSecurityLock fieldSecurityLock)
|
||||
{
|
||||
this.fieldSecurityLock = fieldSecurityLock;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
@ -55,7 +56,7 @@ public class QFrontendAppMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendAppMetaData(QAppMetaData appMetaData)
|
||||
public QFrontendAppMetaData(QAppMetaData appMetaData, MetaDataOutput metaDataOutput)
|
||||
{
|
||||
this.name = appMetaData.getName();
|
||||
this.label = appMetaData.getLabel();
|
||||
@ -65,14 +66,31 @@ public class QFrontendAppMetaData
|
||||
this.iconName = appMetaData.getIcon().getName();
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(appMetaData.getWidgets()))
|
||||
List<String> filteredWidgets = CollectionUtils.nonNullList(appMetaData.getWidgets()).stream().filter(n -> metaDataOutput.getWidgets().containsKey(n)).toList();
|
||||
if(CollectionUtils.nullSafeHasContents(filteredWidgets))
|
||||
{
|
||||
this.widgets = appMetaData.getWidgets();
|
||||
this.widgets = filteredWidgets;
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(appMetaData.getSections()))
|
||||
List<QAppSection> filteredSections = new ArrayList<>();
|
||||
for(QAppSection section : CollectionUtils.nonNullList(appMetaData.getSections()))
|
||||
{
|
||||
this.sections = appMetaData.getSections();
|
||||
List<String> filteredTables = CollectionUtils.nonNullList(section.getTables()).stream().filter(n -> metaDataOutput.getTables().containsKey(n)).toList();
|
||||
List<String> filteredProcesses = CollectionUtils.nonNullList(section.getProcesses()).stream().filter(n -> metaDataOutput.getProcesses().containsKey(n)).toList();
|
||||
List<String> filteredReports = CollectionUtils.nonNullList(section.getReports()).stream().filter(n -> metaDataOutput.getReports().containsKey(n)).toList();
|
||||
if(!filteredTables.isEmpty() || !filteredProcesses.isEmpty() || !filteredReports.isEmpty())
|
||||
{
|
||||
QAppSection clonedSection = section.clone();
|
||||
clonedSection.setTables(filteredTables);
|
||||
clonedSection.setProcesses(filteredProcesses);
|
||||
clonedSection.setReports(filteredReports);
|
||||
filteredSections.add(clonedSection);
|
||||
}
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(filteredSections))
|
||||
{
|
||||
this.sections = filteredSections;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,8 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
@ -49,6 +51,8 @@ public class QFrontendProcessMetaData
|
||||
|
||||
private List<QFrontendStepMetaData> frontendSteps;
|
||||
|
||||
private boolean hasPermission;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -58,7 +62,7 @@ public class QFrontendProcessMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendProcessMetaData(QProcessMetaData processMetaData, boolean includeSteps)
|
||||
public QFrontendProcessMetaData(AbstractActionInput actionInput, QProcessMetaData processMetaData, boolean includeSteps)
|
||||
{
|
||||
this.name = processMetaData.getName();
|
||||
this.label = processMetaData.getLabel();
|
||||
@ -84,6 +88,8 @@ public class QFrontendProcessMetaData
|
||||
{
|
||||
this.iconName = processMetaData.getIcon().getName();
|
||||
}
|
||||
|
||||
hasPermission = PermissionsHelper.hasProcessPermission(actionInput, name);
|
||||
}
|
||||
|
||||
|
||||
@ -163,4 +169,15 @@ public class QFrontendProcessMetaData
|
||||
return iconName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hasPermission
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getHasPermission()
|
||||
{
|
||||
return hasPermission;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.frontend;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
|
||||
|
||||
@ -41,6 +43,8 @@ public class QFrontendReportMetaData
|
||||
|
||||
private String iconName;
|
||||
|
||||
private boolean hasPermission;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -50,7 +54,7 @@ public class QFrontendReportMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendReportMetaData(QReportMetaData reportMetaData, boolean includeSteps)
|
||||
public QFrontendReportMetaData(AbstractActionInput actionInput, QReportMetaData reportMetaData, boolean includeSteps)
|
||||
{
|
||||
this.name = reportMetaData.getName();
|
||||
this.label = reportMetaData.getLabel();
|
||||
@ -60,6 +64,8 @@ public class QFrontendReportMetaData
|
||||
{
|
||||
this.iconName = reportMetaData.getIcon().getName();
|
||||
}
|
||||
|
||||
hasPermission = PermissionsHelper.hasReportPermission(actionInput, name);
|
||||
}
|
||||
|
||||
|
||||
@ -106,4 +112,15 @@ public class QFrontendReportMetaData
|
||||
return iconName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hasPermission
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getHasPermission()
|
||||
{
|
||||
return hasPermission;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,9 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
@ -55,10 +58,13 @@ public class QFrontendTableMetaData
|
||||
private Map<String, QFrontendFieldMetaData> fields;
|
||||
private List<QFieldSection> sections;
|
||||
|
||||
private List<String> widgets;
|
||||
|
||||
private Set<String> capabilities;
|
||||
|
||||
private boolean readPermission;
|
||||
private boolean insertPermission;
|
||||
private boolean editPermission;
|
||||
private boolean deletePermission;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -68,7 +74,7 @@ public class QFrontendTableMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendTableMetaData(QBackendMetaData backendForTable, QTableMetaData tableMetaData, boolean includeFields)
|
||||
public QFrontendTableMetaData(AbstractActionInput actionInput, QBackendMetaData backendForTable, QTableMetaData tableMetaData, boolean includeFields)
|
||||
{
|
||||
this.name = tableMetaData.getName();
|
||||
this.label = tableMetaData.getLabel();
|
||||
@ -92,6 +98,11 @@ public class QFrontendTableMetaData
|
||||
}
|
||||
|
||||
setCapabilities(backendForTable, tableMetaData);
|
||||
|
||||
readPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.READ);
|
||||
insertPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.INSERT);
|
||||
editPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.EDIT);
|
||||
deletePermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.DELETE);
|
||||
}
|
||||
|
||||
|
||||
@ -196,17 +207,6 @@ public class QFrontendTableMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for widgets
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<String> getWidgets()
|
||||
{
|
||||
return widgets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for capabilities
|
||||
**
|
||||
@ -216,4 +216,47 @@ public class QFrontendTableMetaData
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for readPermission
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getReadPermission()
|
||||
{
|
||||
return readPermission;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for insertPermission
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getInsertPermission()
|
||||
{
|
||||
return insertPermission;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for editPermission
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getEditPermission()
|
||||
{
|
||||
return editPermission;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for deletePermission
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getDeletePermission()
|
||||
{
|
||||
return deletePermission;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.frontend;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
|
||||
|
||||
@ -42,6 +44,8 @@ public class QFrontendWidgetMetaData
|
||||
private final Integer gridColumns;
|
||||
private final boolean isCard;
|
||||
|
||||
private boolean hasPermission;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -51,7 +55,7 @@ public class QFrontendWidgetMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendWidgetMetaData(QWidgetMetaDataInterface widgetMetaData)
|
||||
public QFrontendWidgetMetaData(AbstractActionInput actionInput, QWidgetMetaDataInterface widgetMetaData)
|
||||
{
|
||||
this.name = widgetMetaData.getName();
|
||||
this.label = widgetMetaData.getLabel();
|
||||
@ -59,6 +63,8 @@ public class QFrontendWidgetMetaData
|
||||
this.icon = widgetMetaData.getIcon();
|
||||
this.gridColumns = widgetMetaData.getGridColumns();
|
||||
this.isCard = widgetMetaData.getIsCard();
|
||||
|
||||
hasPermission = PermissionsHelper.hasWidgetPermission(actionInput, name);
|
||||
}
|
||||
|
||||
|
||||
@ -127,4 +133,15 @@ public class QFrontendWidgetMetaData
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hasPermission
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getHasPermission()
|
||||
{
|
||||
return hasPermission;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
@ -34,11 +36,13 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
** MetaData definition of an App - an entity that organizes tables & processes
|
||||
** and can be arranged hierarchically (e.g, apps can contain other apps).
|
||||
*******************************************************************************/
|
||||
public class QAppMetaData implements QAppChildMetaData
|
||||
public class QAppMetaData implements QAppChildMetaData, MetaDataWithPermissionRules
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
|
||||
private QPermissionRules permissionRules;
|
||||
|
||||
private List<QAppChildMetaData> children;
|
||||
|
||||
private String parentAppName;
|
||||
@ -377,4 +381,35 @@ public class QAppMetaData implements QAppChildMetaData
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QPermissionRules getPermissionRules()
|
||||
{
|
||||
return (this.permissionRules);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public void setPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QAppMetaData withPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import java.util.List;
|
||||
/*******************************************************************************
|
||||
** A section of apps/tables/processes - a logical grouping.
|
||||
*******************************************************************************/
|
||||
public class QAppSection
|
||||
public class QAppSection implements Cloneable
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
@ -65,6 +65,25 @@ public class QAppSection
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QAppSection clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
QAppSection clone = (QAppSection) super.clone();
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
@ -314,5 +333,4 @@ public class QAppSection
|
||||
this.icon = icon;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.model.metadata.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum DenyBehavior
|
||||
{
|
||||
DISABLED,
|
||||
HIDDEN
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.model.metadata.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface MetaDataWithName
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
String getName();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
void setName(String name);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label
|
||||
*******************************************************************************/
|
||||
String getLabel();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for label
|
||||
*******************************************************************************/
|
||||
void setLabel(String label);
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.model.metadata.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface MetaDataWithPermissionRules extends MetaDataWithName
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionRules
|
||||
*******************************************************************************/
|
||||
QPermissionRules getPermissionRules();
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionRules
|
||||
*******************************************************************************/
|
||||
void setPermissionRules(QPermissionRules permissionRules);
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.model.metadata.permissions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum PermissionLevel
|
||||
{
|
||||
NOT_PROTECTED,
|
||||
HAS_ACCESS_PERMISSION,
|
||||
READ_WRITE_PERMISSIONS,
|
||||
READ_INSERT_EDIT_DELETE_PERMISSIONS,
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.model.metadata.permissions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QPermissionRules implements Cloneable
|
||||
{
|
||||
private PermissionLevel level;
|
||||
private DenyBehavior denyBehavior;
|
||||
private String permissionBaseName;
|
||||
|
||||
private QCodeReference customPermissionChecker;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QPermissionRules defaultInstance()
|
||||
{
|
||||
return new QPermissionRules()
|
||||
.withLevel(PermissionLevel.NOT_PROTECTED)
|
||||
.withDenyBehavior(DenyBehavior.HIDDEN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for level
|
||||
*******************************************************************************/
|
||||
public PermissionLevel getLevel()
|
||||
{
|
||||
return (this.level);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for level
|
||||
*******************************************************************************/
|
||||
public void setLevel(PermissionLevel level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for level
|
||||
*******************************************************************************/
|
||||
public QPermissionRules withLevel(PermissionLevel level)
|
||||
{
|
||||
this.level = level;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for denyBehavior
|
||||
*******************************************************************************/
|
||||
public DenyBehavior getDenyBehavior()
|
||||
{
|
||||
return (this.denyBehavior);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for denyBehavior
|
||||
*******************************************************************************/
|
||||
public void setDenyBehavior(DenyBehavior denyBehavior)
|
||||
{
|
||||
this.denyBehavior = denyBehavior;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for denyBehavior
|
||||
*******************************************************************************/
|
||||
public QPermissionRules withDenyBehavior(DenyBehavior denyBehavior)
|
||||
{
|
||||
this.denyBehavior = denyBehavior;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionBaseName
|
||||
*******************************************************************************/
|
||||
public String getPermissionBaseName()
|
||||
{
|
||||
return (this.permissionBaseName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionBaseName
|
||||
*******************************************************************************/
|
||||
public void setPermissionBaseName(String permissionBaseName)
|
||||
{
|
||||
this.permissionBaseName = permissionBaseName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionBaseName
|
||||
*******************************************************************************/
|
||||
public QPermissionRules withPermissionBaseName(String permissionBaseName)
|
||||
{
|
||||
this.permissionBaseName = permissionBaseName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QPermissionRules clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
QPermissionRules clone = (QPermissionRules) super.clone();
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for customPermissionChecker
|
||||
*******************************************************************************/
|
||||
public QCodeReference getCustomPermissionChecker()
|
||||
{
|
||||
return (this.customPermissionChecker);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for customPermissionChecker
|
||||
*******************************************************************************/
|
||||
public void setCustomPermissionChecker(QCodeReference customPermissionChecker)
|
||||
{
|
||||
this.customPermissionChecker = customPermissionChecker;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for customPermissionChecker
|
||||
*******************************************************************************/
|
||||
public QPermissionRules withCustomPermissionChecker(QCodeReference customPermissionChecker)
|
||||
{
|
||||
this.customPermissionChecker = customPermissionChecker;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -32,6 +32,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaData;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
||||
|
||||
@ -40,13 +42,14 @@ import com.kingsrook.qqq.backend.core.processes.implementations.basepull.Basepul
|
||||
** Meta-Data to define a process in a QQQ instance.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QProcessMetaData implements QAppChildMetaData
|
||||
public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissionRules
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private String tableName;
|
||||
private boolean isHidden = false;
|
||||
private BasepullConfiguration basepullConfiguration;
|
||||
private QPermissionRules permissionRules;
|
||||
|
||||
private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in
|
||||
private Map<String, QStepMetaData> steps; // this is the full map of possible steps
|
||||
@ -509,4 +512,35 @@ public class QProcessMetaData implements QAppChildMetaData
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QPermissionRules getPermissionRules()
|
||||
{
|
||||
return (this.permissionRules);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public void setPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QProcessMetaData withPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,17 +27,21 @@ import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Meta-data definition of a report generated by QQQ
|
||||
*******************************************************************************/
|
||||
public class QReportMetaData implements QAppChildMetaData
|
||||
public class QReportMetaData implements QAppChildMetaData, MetaDataWithPermissionRules
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
|
||||
private QPermissionRules permissionRules;
|
||||
|
||||
private String processName;
|
||||
private List<QFieldMetaData> inputFields;
|
||||
|
||||
@ -372,4 +376,35 @@ public class QReportMetaData implements QAppChildMetaData
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QPermissionRules getPermissionRules()
|
||||
{
|
||||
return (this.permissionRules);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public void setPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QReportMetaData withPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.model.metadata.security;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define, for a field, a lock that controls if users can or cannot see the field.
|
||||
*******************************************************************************/
|
||||
public class FieldSecurityLock
|
||||
{
|
||||
private String securityKeyType;
|
||||
private Behavior defaultBehavior = Behavior.DENY;
|
||||
private List<Serializable> overrideValues;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FieldSecurityLock()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum Behavior
|
||||
{
|
||||
ALLOW,
|
||||
DENY
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for securityKeyType
|
||||
*******************************************************************************/
|
||||
public String getSecurityKeyType()
|
||||
{
|
||||
return (this.securityKeyType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for securityKeyType
|
||||
*******************************************************************************/
|
||||
public void setSecurityKeyType(String securityKeyType)
|
||||
{
|
||||
this.securityKeyType = securityKeyType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for securityKeyType
|
||||
*******************************************************************************/
|
||||
public FieldSecurityLock withSecurityKeyType(String securityKeyType)
|
||||
{
|
||||
this.securityKeyType = securityKeyType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for defaultBehavior
|
||||
*******************************************************************************/
|
||||
public Behavior getDefaultBehavior()
|
||||
{
|
||||
return (this.defaultBehavior);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for defaultBehavior
|
||||
*******************************************************************************/
|
||||
public void setDefaultBehavior(Behavior defaultBehavior)
|
||||
{
|
||||
this.defaultBehavior = defaultBehavior;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for defaultBehavior
|
||||
*******************************************************************************/
|
||||
public FieldSecurityLock withDefaultBehavior(Behavior defaultBehavior)
|
||||
{
|
||||
this.defaultBehavior = defaultBehavior;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for overrideValues
|
||||
*******************************************************************************/
|
||||
public List<Serializable> getOverrideValues()
|
||||
{
|
||||
return (this.overrideValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for overrideValues
|
||||
*******************************************************************************/
|
||||
public void setOverrideValues(List<Serializable> overrideValues)
|
||||
{
|
||||
this.overrideValues = overrideValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for overrideValues
|
||||
*******************************************************************************/
|
||||
public FieldSecurityLock withOverrideValues(List<Serializable> overrideValues)
|
||||
{
|
||||
this.overrideValues = overrideValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.model.metadata.security;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define a type of security key (e.g., a field associated with values), that
|
||||
** can be used to control access to records and/or fields
|
||||
*******************************************************************************/
|
||||
public class QSecurityKeyType
|
||||
{
|
||||
private String name;
|
||||
private String allAccessKeyName;
|
||||
private String possibleValueSourceName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QSecurityKeyType()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public QSecurityKeyType withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for allAccessKeyName
|
||||
*******************************************************************************/
|
||||
public String getAllAccessKeyName()
|
||||
{
|
||||
return (this.allAccessKeyName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for allAccessKeyName
|
||||
*******************************************************************************/
|
||||
public void setAllAccessKeyName(String allAccessKeyName)
|
||||
{
|
||||
this.allAccessKeyName = allAccessKeyName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for allAccessKeyName
|
||||
*******************************************************************************/
|
||||
public QSecurityKeyType withAllAccessKeyName(String allAccessKeyName)
|
||||
{
|
||||
this.allAccessKeyName = allAccessKeyName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for possibleValueSourceName
|
||||
*******************************************************************************/
|
||||
public String getPossibleValueSourceName()
|
||||
{
|
||||
return (this.possibleValueSourceName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for possibleValueSourceName
|
||||
*******************************************************************************/
|
||||
public void setPossibleValueSourceName(String possibleValueSourceName)
|
||||
{
|
||||
this.possibleValueSourceName = possibleValueSourceName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for possibleValueSourceName
|
||||
*******************************************************************************/
|
||||
public QSecurityKeyType withPossibleValueSourceName(String possibleValueSourceName)
|
||||
{
|
||||
this.possibleValueSourceName = possibleValueSourceName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.model.metadata.security;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define (for a table) a lock that applies to records in the table - e.g.,
|
||||
** a key type, and a field that has values for that key.
|
||||
*
|
||||
*******************************************************************************/
|
||||
public class RecordSecurityLock
|
||||
{
|
||||
private String securityKeyType;
|
||||
private String fieldName;
|
||||
private List<String> joinChain; // todo - add validation in validator!!
|
||||
private NullValueBehavior nullValueBehavior = NullValueBehavior.DENY;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RecordSecurityLock()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum NullValueBehavior
|
||||
{
|
||||
ALLOW,
|
||||
DENY
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for securityKeyType
|
||||
*******************************************************************************/
|
||||
public String getSecurityKeyType()
|
||||
{
|
||||
return (this.securityKeyType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for securityKeyType
|
||||
*******************************************************************************/
|
||||
public void setSecurityKeyType(String securityKeyType)
|
||||
{
|
||||
this.securityKeyType = securityKeyType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for securityKeyType
|
||||
*******************************************************************************/
|
||||
public RecordSecurityLock withSecurityKeyType(String securityKeyType)
|
||||
{
|
||||
this.securityKeyType = securityKeyType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldName
|
||||
*******************************************************************************/
|
||||
public String getFieldName()
|
||||
{
|
||||
return (this.fieldName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldName
|
||||
*******************************************************************************/
|
||||
public void setFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldName
|
||||
*******************************************************************************/
|
||||
public RecordSecurityLock withFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nullValueBehavior
|
||||
*******************************************************************************/
|
||||
public NullValueBehavior getNullValueBehavior()
|
||||
{
|
||||
return (this.nullValueBehavior);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nullValueBehavior
|
||||
*******************************************************************************/
|
||||
public void setNullValueBehavior(NullValueBehavior nullValueBehavior)
|
||||
{
|
||||
this.nullValueBehavior = nullValueBehavior;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nullValueBehavior
|
||||
*******************************************************************************/
|
||||
public RecordSecurityLock withNullValueBehavior(NullValueBehavior nullValueBehavior)
|
||||
{
|
||||
this.nullValueBehavior = nullValueBehavior;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for joinChain
|
||||
*******************************************************************************/
|
||||
public List<String> getJoinChain()
|
||||
{
|
||||
return (this.joinChain);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for joinChain
|
||||
*******************************************************************************/
|
||||
public void setJoinChain(List<String> joinChain)
|
||||
{
|
||||
this.joinChain = joinChain;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for joinChain
|
||||
*******************************************************************************/
|
||||
public RecordSecurityLock withJoinChain(List<String> joinChain)
|
||||
{
|
||||
this.joinChain = joinChain;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -41,6 +41,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf;
|
||||
|
||||
@ -49,7 +52,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf;
|
||||
** Meta-Data to define a table in a QQQ instance.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
public class QTableMetaData implements QAppChildMetaData, Serializable, MetaDataWithPermissionRules
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
@ -69,6 +72,9 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
private Map<String, QFieldMetaData> fields;
|
||||
private List<UniqueKey> uniqueKeys;
|
||||
|
||||
private List<RecordSecurityLock> recordSecurityLocks;
|
||||
private QPermissionRules permissionRules;
|
||||
|
||||
private QTableBackendDetails backendDetails;
|
||||
private QTableAutomationDetails automationDetails;
|
||||
|
||||
@ -1041,7 +1047,12 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test if a capability is enabled - checking both at the table level and
|
||||
** at the backend level.
|
||||
**
|
||||
** If backend says disabled, then disable - UNLESS - the table says enable.
|
||||
** If backend either doesn't specify, or says enable, return what the table says (if it says).
|
||||
** else, return the default (of enabled).
|
||||
*******************************************************************************/
|
||||
public boolean isCapabilityEnabled(QBackendMetaData backend, Capability capability)
|
||||
{
|
||||
@ -1078,4 +1089,82 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
|
||||
return (hasCapability);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordSecurityLocks
|
||||
*******************************************************************************/
|
||||
public List<RecordSecurityLock> getRecordSecurityLocks()
|
||||
{
|
||||
return (this.recordSecurityLocks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordSecurityLocks
|
||||
*******************************************************************************/
|
||||
public void setRecordSecurityLocks(List<RecordSecurityLock> recordSecurityLocks)
|
||||
{
|
||||
this.recordSecurityLocks = recordSecurityLocks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordSecurityLocks
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withRecordSecurityLocks(List<RecordSecurityLock> recordSecurityLocks)
|
||||
{
|
||||
this.recordSecurityLocks = recordSecurityLocks;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordSecurityLocks
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withRecordSecurityLock(RecordSecurityLock recordSecurityLock)
|
||||
{
|
||||
if(this.recordSecurityLocks == null)
|
||||
{
|
||||
this.recordSecurityLocks = new ArrayList<>();
|
||||
}
|
||||
this.recordSecurityLocks.add(recordSecurityLock);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QPermissionRules getPermissionRules()
|
||||
{
|
||||
return (this.permissionRules);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public void setPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionRules
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withPermissionRules(QPermissionRules permissionRules)
|
||||
{
|
||||
this.permissionRules = permissionRules;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,9 +23,19 @@ package com.kingsrook.qqq.backend.core.model.session;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MutableMap;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -37,6 +47,9 @@ public class QSession implements Serializable
|
||||
private QUser user;
|
||||
private String uuid;
|
||||
|
||||
private Map<String, List<Serializable>> securityKeyValues;
|
||||
private Set<String> permissions;
|
||||
|
||||
// implementation-specific custom values
|
||||
private Map<String, String> values;
|
||||
|
||||
@ -179,4 +192,264 @@ public class QSession implements Serializable
|
||||
{
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for securityKeyValues
|
||||
*******************************************************************************/
|
||||
public Map<String, List<Serializable>> getSecurityKeyValues()
|
||||
{
|
||||
return (this.securityKeyValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for securityKeyValues - the list under a given key - never null.
|
||||
*******************************************************************************/
|
||||
public List<Serializable> getSecurityKeyValues(String keyName)
|
||||
{
|
||||
if(securityKeyValues == null)
|
||||
{
|
||||
return (new ArrayList<>());
|
||||
}
|
||||
|
||||
return (Objects.requireNonNullElseGet(securityKeyValues.get(keyName), ArrayList::new));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for securityKeyValues - the list under a given key - as the expected tye - never null.
|
||||
*******************************************************************************/
|
||||
public List<Serializable> getSecurityKeyValues(String keyName, QFieldType type)
|
||||
{
|
||||
if(securityKeyValues == null)
|
||||
{
|
||||
return (new ArrayList<>());
|
||||
}
|
||||
|
||||
List<Serializable> rawValues = securityKeyValues.get(keyName);
|
||||
if(rawValues == null)
|
||||
{
|
||||
return (new ArrayList<>());
|
||||
}
|
||||
|
||||
List<Serializable> valuesAsType = new ArrayList<>();
|
||||
for(Serializable rawValue : rawValues)
|
||||
{
|
||||
valuesAsType.add(ValueUtils.getValueAsFieldType(type, rawValue));
|
||||
}
|
||||
return (valuesAsType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test if this session has a given value for a given key
|
||||
*******************************************************************************/
|
||||
public boolean hasSecurityKeyValue(String keyName, Serializable value)
|
||||
{
|
||||
if(securityKeyValues == null)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
if(!securityKeyValues.containsKey(keyName))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
List<Serializable> values = securityKeyValues.get(keyName);
|
||||
return (values != null && values.contains(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean hasSecurityKeyValue(String keyName, Serializable value, QFieldType fieldType)
|
||||
{
|
||||
if(securityKeyValues == null)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
if(!securityKeyValues.containsKey(keyName))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
List<Serializable> values = securityKeyValues.get(keyName);
|
||||
Serializable valueAsType = ValueUtils.getValueAsFieldType(fieldType, value);
|
||||
for(Serializable keyValue : values)
|
||||
{
|
||||
Serializable keyValueAsType = ValueUtils.getValueAsFieldType(fieldType, keyValue);
|
||||
if(keyValueAsType.equals(valueAsType))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for securityKeyValues
|
||||
*******************************************************************************/
|
||||
public void setSecurityKeyValues(Map<String, List<Serializable>> securityKeyValues)
|
||||
{
|
||||
this.securityKeyValues = new MutableMap<>(securityKeyValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for securityKeyValues - replaces the map.
|
||||
*******************************************************************************/
|
||||
public QSession withSecurityKeyValues(Map<String, List<Serializable>> securityKeyValues)
|
||||
{
|
||||
this.securityKeyValues = new MutableMap<>(securityKeyValues);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for securityKeyValues - add a list of values for 1 key
|
||||
*******************************************************************************/
|
||||
public QSession withSecurityKeyValues(String keyName, List<Serializable> values)
|
||||
{
|
||||
if(values == null)
|
||||
{
|
||||
return (this);
|
||||
}
|
||||
|
||||
if(securityKeyValues == null)
|
||||
{
|
||||
securityKeyValues = new HashMap<>();
|
||||
}
|
||||
|
||||
securityKeyValues.computeIfAbsent(keyName, (k) -> new ArrayList<>());
|
||||
|
||||
try
|
||||
{
|
||||
securityKeyValues.get(keyName).addAll(values);
|
||||
}
|
||||
catch(UnsupportedOperationException uoe)
|
||||
{
|
||||
securityKeyValues.put(keyName, new ArrayList<>(securityKeyValues.get(keyName)));
|
||||
securityKeyValues.get(keyName).addAll(values);
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for securityKeyValues - add 1 value for 1 key.
|
||||
*******************************************************************************/
|
||||
public QSession withSecurityKeyValue(String keyName, Serializable value)
|
||||
{
|
||||
return (withSecurityKeyValues(keyName, List.of(value)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Clear the map of security key values in the session.
|
||||
*******************************************************************************/
|
||||
public void clearSecurityKeyValues()
|
||||
{
|
||||
if(securityKeyValues != null)
|
||||
{
|
||||
securityKeyValues.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissions
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setPermissions(Set<String> permissions)
|
||||
{
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QSession withPermission(String permission)
|
||||
{
|
||||
if(this.permissions == null)
|
||||
{
|
||||
this.permissions = new HashSet<>();
|
||||
}
|
||||
this.permissions.add(permission);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QSession withPermissions(String... permissionNames)
|
||||
{
|
||||
if(this.permissions == null)
|
||||
{
|
||||
this.permissions = new HashSet<>();
|
||||
}
|
||||
|
||||
Collections.addAll(this.permissions, permissionNames);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QSession withPermissions(Collection<String> permissionNames)
|
||||
{
|
||||
if(this.permissions == null)
|
||||
{
|
||||
this.permissions = new HashSet<>();
|
||||
}
|
||||
|
||||
this.permissions.addAll(permissionNames);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean hasPermission(String permissionName)
|
||||
{
|
||||
return (permissions != null && permissions.contains(permissionName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissions
|
||||
*******************************************************************************/
|
||||
public Set<String> getPermissions()
|
||||
{
|
||||
return (this.permissions);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,4 +73,15 @@ public class QUser
|
||||
{
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return ("QUser{" + idReference + "," + fullName + "}");
|
||||
}
|
||||
}
|
||||
|
@ -44,4 +44,14 @@ public interface QAuthenticationModuleInterface
|
||||
**
|
||||
*******************************************************************************/
|
||||
boolean isSessionValid(QInstance instance, QSession session);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default boolean usesSessionIdCookie()
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,8 +28,11 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Base64;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import com.auth0.client.auth.AuthAPI;
|
||||
import com.auth0.exception.Auth0Exception;
|
||||
import com.auth0.json.auth.TokenHolder;
|
||||
@ -53,8 +56,10 @@ import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModu
|
||||
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
|
||||
import com.kingsrook.qqq.backend.core.state.SimpleStateKey;
|
||||
import com.kingsrook.qqq.backend.core.state.StateProviderInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
@ -70,11 +75,11 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static final int ID_TOKEN_VALIDATION_INTERVAL_SECONDS = 1800;
|
||||
|
||||
public static final String AUTH0_ID_TOKEN_KEY = "sessionId";
|
||||
public static final String BASIC_AUTH_KEY = "basicAuthString";
|
||||
public static final String AUTH0_ACCESS_TOKEN_KEY = "sessionId";
|
||||
public static final String BASIC_AUTH_KEY = "basicAuthString";
|
||||
|
||||
public static final String TOKEN_NOT_PROVIDED_ERROR = "Id Token was not provided";
|
||||
public static final String COULD_NOT_DECODE_ERROR = "Unable to decode id token";
|
||||
public static final String TOKEN_NOT_PROVIDED_ERROR = "Access Token was not provided";
|
||||
public static final String COULD_NOT_DECODE_ERROR = "Unable to decode access token";
|
||||
public static final String EXPIRED_TOKEN_ERROR = "Token has expired";
|
||||
public static final String INVALID_TOKEN_ERROR = "An invalid token was provided";
|
||||
|
||||
@ -102,8 +107,8 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
// decode the credentials from the header auth //
|
||||
/////////////////////////////////////////////////
|
||||
String base64Credentials = context.get(BASIC_AUTH_KEY).trim();
|
||||
String idToken = getIdTokenFromBase64BasicAuthCredentials(auth, base64Credentials);
|
||||
context.put(AUTH0_ID_TOKEN_KEY, idToken);
|
||||
String accessToken = getAccessTokenFromBase64BasicAuthCredentials(metaData, auth, base64Credentials);
|
||||
context.put(AUTH0_ACCESS_TOKEN_KEY, accessToken);
|
||||
}
|
||||
catch(Auth0Exception e)
|
||||
{
|
||||
@ -116,11 +121,11 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// get the jwt id token from the context object //
|
||||
//////////////////////////////////////////////////
|
||||
String idToken = context.get(AUTH0_ID_TOKEN_KEY);
|
||||
if(idToken == null)
|
||||
//////////////////////////////////////////////////////
|
||||
// get the jwt access token from the context object //
|
||||
//////////////////////////////////////////////////////
|
||||
String accessToken = context.get(AUTH0_ACCESS_TOKEN_KEY);
|
||||
if(accessToken == null)
|
||||
{
|
||||
LOG.warn(TOKEN_NOT_PROVIDED_ERROR);
|
||||
throw (new QAuthenticationException(TOKEN_NOT_PROVIDED_ERROR));
|
||||
@ -135,7 +140,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
// try to build session to see if still valid //
|
||||
// then call method to check more session validity //
|
||||
/////////////////////////////////////////////////////
|
||||
QSession qSession = buildQSessionFromToken(idToken);
|
||||
QSession qSession = buildQSessionFromToken(accessToken, qInstance);
|
||||
if(isSessionValid(qInstance, qSession))
|
||||
{
|
||||
return (qSession);
|
||||
@ -145,7 +150,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
// if we make it here it means we have never validated this token or its been a long //
|
||||
// enough duration so we need to re-verify the token //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
qSession = revalidateToken(qInstance, idToken);
|
||||
qSession = revalidateToken(qInstance, accessToken);
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// put now into state so we dont check until next interval passes //
|
||||
@ -193,34 +198,34 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getIdTokenFromBase64BasicAuthCredentials(AuthAPI auth, String base64Credentials) throws Auth0Exception
|
||||
private String getAccessTokenFromBase64BasicAuthCredentials(Auth0AuthenticationMetaData metaData, AuthAPI auth, String base64Credentials) throws Auth0Exception
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// look for a fresh idToken in the state provider for this set of credentials //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
SimpleStateKey<String> idTokenStateKey = new SimpleStateKey<>(base64Credentials + ":idToken");
|
||||
SimpleStateKey<String> timestampStateKey = new SimpleStateKey<>(base64Credentials + ":timestamp");
|
||||
StateProviderInterface stateProvider = getStateProvider();
|
||||
Optional<String> cachedIdToken = stateProvider.get(String.class, idTokenStateKey);
|
||||
Optional<Instant> cachedTimestamp = stateProvider.get(Instant.class, timestampStateKey);
|
||||
if(cachedIdToken.isPresent() && cachedTimestamp.isPresent())
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// look for a fresh accessToken in the state provider for this set of credentials //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
SimpleStateKey<String> accessTokenStateKey = new SimpleStateKey<>(base64Credentials + ":accessToken");
|
||||
SimpleStateKey<String> timestampStateKey = new SimpleStateKey<>(base64Credentials + ":timestamp");
|
||||
StateProviderInterface stateProvider = getStateProvider();
|
||||
Optional<String> cachedAccessToken = stateProvider.get(String.class, accessTokenStateKey);
|
||||
Optional<Instant> cachedTimestamp = stateProvider.get(Instant.class, timestampStateKey);
|
||||
if(cachedAccessToken.isPresent() && cachedTimestamp.isPresent())
|
||||
{
|
||||
if(cachedTimestamp.get().isAfter(Instant.now().minus(1, ChronoUnit.MINUTES)))
|
||||
{
|
||||
return cachedIdToken.get();
|
||||
return cachedAccessToken.get();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// not found in cache, make request to auth0 and cache the returned idToken //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// not found in cache, make request to auth0 and cache the returned accessToken //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
|
||||
String credentials = new String(credDecoded, StandardCharsets.UTF_8);
|
||||
|
||||
String idToken = getIdTokenFromAuth0(auth, credentials);
|
||||
stateProvider.put(idTokenStateKey, idToken);
|
||||
String accessToken = getAccessTokenFromAuth0(metaData, auth, credentials);
|
||||
stateProvider.put(accessTokenStateKey, accessToken);
|
||||
stateProvider.put(timestampStateKey, Instant.now());
|
||||
return (idToken);
|
||||
return (accessToken);
|
||||
}
|
||||
|
||||
|
||||
@ -228,16 +233,17 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String getIdTokenFromAuth0(AuthAPI auth, String credentials) throws Auth0Exception
|
||||
protected String getAccessTokenFromAuth0(Auth0AuthenticationMetaData metaData, AuthAPI auth, String credentials) throws Auth0Exception
|
||||
{
|
||||
/////////////////////////////////////
|
||||
// call auth0 with a login request //
|
||||
/////////////////////////////////////
|
||||
TokenHolder result = auth.login(credentials.split(":")[0], credentials.split(":")[1].toCharArray())
|
||||
.setScope("openid email nickname")
|
||||
.setAudience(metaData.getAudience())
|
||||
.execute();
|
||||
|
||||
return (result.getIdToken());
|
||||
return (result.getAccessToken());
|
||||
}
|
||||
|
||||
|
||||
@ -303,11 +309,11 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
** makes request to check if a token is still valid and build new qSession if it is
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QSession revalidateToken(QInstance qInstance, String idToken) throws JwkException
|
||||
private QSession revalidateToken(QInstance qInstance, String accessToken) throws JwkException
|
||||
{
|
||||
Auth0AuthenticationMetaData metaData = (Auth0AuthenticationMetaData) qInstance.getAuthentication();
|
||||
|
||||
DecodedJWT jwt = JWT.decode(idToken);
|
||||
DecodedJWT jwt = JWT.decode(accessToken);
|
||||
JwkProvider provider = new UrlJwkProvider(metaData.getBaseUrl());
|
||||
Jwk jwk = provider.get(jwt.getKeyId());
|
||||
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
|
||||
@ -318,9 +324,9 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
///////////////////////////////////
|
||||
// make call to verify the token //
|
||||
///////////////////////////////////
|
||||
verifier.verify(idToken);
|
||||
verifier.verify(accessToken);
|
||||
|
||||
return (buildQSessionFromToken(idToken));
|
||||
return (buildQSessionFromToken(accessToken, qInstance));
|
||||
}
|
||||
|
||||
|
||||
@ -329,52 +335,181 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
** extracts info from token creating a QSession
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QSession buildQSessionFromToken(String idToken) throws JwkException
|
||||
private QSession buildQSessionFromToken(String accessToken, QInstance qInstance) throws JwkException
|
||||
{
|
||||
////////////////////////////////////
|
||||
// decode and extract the payload //
|
||||
////////////////////////////////////
|
||||
DecodedJWT jwt = JWT.decode(idToken);
|
||||
DecodedJWT jwt = JWT.decode(accessToken);
|
||||
Base64.Decoder decoder = Base64.getUrlDecoder();
|
||||
String payloadString = new String(decoder.decode(jwt.getPayload()));
|
||||
JSONObject payload = new JSONObject(payloadString);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// create user object. look for multiple possible keys in the jwt payload where the name & email may be //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QUser qUser = new QUser();
|
||||
if(payload.has("name"))
|
||||
qUser.setFullName("Unknown");
|
||||
for(String key : List.of("name", "com.kingsrook.qqq.name"))
|
||||
{
|
||||
qUser.setFullName(payload.getString("name"));
|
||||
}
|
||||
else
|
||||
{
|
||||
qUser.setFullName("Unknown");
|
||||
}
|
||||
|
||||
if(payload.has("email"))
|
||||
{
|
||||
qUser.setIdReference(payload.getString("email"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(payload.has("sub"))
|
||||
if(payload.has(key))
|
||||
{
|
||||
qUser.setIdReference(payload.getString("sub"));
|
||||
qUser.setFullName(payload.getString(key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(String key : List.of("email", "com.kingsrook.qqq.email", "sub"))
|
||||
{
|
||||
if(payload.has(key))
|
||||
{
|
||||
qUser.setIdReference(payload.getString(key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// create session object - link to access token & user //
|
||||
/////////////////////////////////////////////////////////
|
||||
QSession qSession = new QSession();
|
||||
qSession.setIdReference(idToken);
|
||||
qSession.setIdReference(accessToken);
|
||||
qSession.setUser(qUser);
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// set permissions in the session from the JWT //
|
||||
/////////////////////////////////////////////////
|
||||
setPermissionsInSessionFromJwtPayload(payload, qSession);
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// set security keys in the session from the JWT //
|
||||
///////////////////////////////////////////////////
|
||||
setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession);
|
||||
|
||||
return (qSession);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
static void setPermissionsInSessionFromJwtPayload(JSONObject payload, QSession qSession)
|
||||
{
|
||||
HashSet<String> permissions = new HashSet<>();
|
||||
if(payload.has("permissions"))
|
||||
{
|
||||
try
|
||||
{
|
||||
JSONArray jwtPermissions = payload.getJSONArray("permissions");
|
||||
for(int i = 0; i < jwtPermissions.length(); i++)
|
||||
{
|
||||
permissions.add(jwtPermissions.optString(i));
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error getting permissions from JWT", e);
|
||||
}
|
||||
}
|
||||
qSession.setPermissions(permissions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
static void setSecurityKeysInSessionFromJwtPayload(QInstance qInstance, JSONObject payload, QSession qSession)
|
||||
{
|
||||
for(String payloadKey : List.of("com.kingsrook.qqq.app_metadata", "com.kingsrook.qqq.client_metadata"))
|
||||
{
|
||||
if(!payload.has(payloadKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
JSONObject appMetadata = payload.getJSONObject(payloadKey);
|
||||
Set<String> allowedSecurityKeyNames = qInstance.getAllowedSecurityKeyNames();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// for users, they will have a map of securityKeyValues (in their app_metadata) //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
JSONObject securityKeyValues = appMetadata.optJSONObject("securityKeyValues");
|
||||
if(securityKeyValues != null)
|
||||
{
|
||||
for(String keyName : securityKeyValues.keySet())
|
||||
{
|
||||
setSecurityKeyValuesFromToken(allowedSecurityKeyNames, qSession, keyName, securityKeyValues, keyName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// for system-logins, there will be keys prefixed by securityKeyValues: (under client_metadata) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(String appMetaDataKey : appMetadata.keySet())
|
||||
{
|
||||
if(appMetaDataKey.startsWith("securityKeyValues:"))
|
||||
{
|
||||
String securityKeyName = appMetaDataKey.replace("securityKeyValues:", "");
|
||||
setSecurityKeyValuesFromToken(allowedSecurityKeyNames, qSession, securityKeyName, appMetadata, appMetaDataKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error getting securityKey values from app_metadata from JWT", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void setSecurityKeyValuesFromToken(Set<String> allowedSecurityKeyNames, QSession qSession, String securityKeyName, JSONObject securityKeyValues, String jsonKey)
|
||||
{
|
||||
if(!allowedSecurityKeyNames.contains(securityKeyName))
|
||||
{
|
||||
QUser user = qSession.getUser();
|
||||
LOG.warn("Unrecognized security key name [" + securityKeyName + "] when creating session for user [" + user + "]. Allowed key names are: " + allowedSecurityKeyNames);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONArray valueArray = securityKeyValues.optJSONArray(jsonKey);
|
||||
if(valueArray != null)
|
||||
{
|
||||
// todo - types?
|
||||
for(int i = 0; i < valueArray.length(); i++)
|
||||
{
|
||||
Object optValue = valueArray.opt(i);
|
||||
if(optValue != null)
|
||||
{
|
||||
qSession.withSecurityKeyValue(securityKeyName, ValueUtils.getValueAsString(optValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String value = securityKeyValues.optString(jsonKey);
|
||||
if(value != null)
|
||||
{
|
||||
qSession.withSecurityKeyValue(securityKeyName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load an instance of the appropriate state provider
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static StateProviderInterface getStateProvider()
|
||||
private static StateProviderInterface getStateProvider()
|
||||
{
|
||||
// TODO - read this from somewhere in meta data eh?
|
||||
return (InMemoryStateProvider.getInstance());
|
||||
|
@ -51,8 +51,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.authentication.TableBasedAu
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.state.AbstractStateKey;
|
||||
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
|
||||
import com.kingsrook.qqq.backend.core.state.SimpleStateKey;
|
||||
import com.kingsrook.qqq.backend.core.state.StateProviderInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -87,6 +87,17 @@ public class TableBasedAuthenticationModule implements QAuthenticationModuleInte
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean usesSessionIdCookie()
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -203,7 +214,7 @@ public class TableBasedAuthenticationModule implements QAuthenticationModuleInte
|
||||
// put now into state so we dont check until next interval passes //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
StateProviderInterface spi = getStateProvider();
|
||||
SessionIdStateKey key = new SessionIdStateKey(qSession.getIdReference());
|
||||
SimpleStateKey<String> key = new SimpleStateKey<>(qSession.getIdReference());
|
||||
spi.put(key, Instant.now());
|
||||
|
||||
return (qSession);
|
||||
@ -251,7 +262,7 @@ public class TableBasedAuthenticationModule implements QAuthenticationModuleInte
|
||||
}
|
||||
|
||||
StateProviderInterface stateProvider = getStateProvider();
|
||||
SessionIdStateKey key = new SessionIdStateKey(session.getIdReference());
|
||||
SimpleStateKey<String> key = new SimpleStateKey<>(session.getIdReference());
|
||||
Optional<Instant> lastTimeCheckedOptional = stateProvider.get(Instant.class, key);
|
||||
if(lastTimeCheckedOptional.isPresent())
|
||||
{
|
||||
@ -392,81 +403,6 @@ public class TableBasedAuthenticationModule implements QAuthenticationModuleInte
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class SessionIdStateKey extends AbstractStateKey
|
||||
{
|
||||
private final String key;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor.
|
||||
**
|
||||
*******************************************************************************/
|
||||
SessionIdStateKey(String key)
|
||||
{
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return (this.key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Make the key give a unique string to identify itself.
|
||||
*
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getUniqueIdentifier()
|
||||
{
|
||||
return (this.key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if(this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SessionIdStateKey that = (SessionIdStateKey) o;
|
||||
return key.equals(that.key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return key.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -146,6 +146,7 @@ public class MemoryRecordStore
|
||||
}
|
||||
|
||||
BackendQueryFilterUtils.sortRecordList(input.getFilter(), records);
|
||||
records = BackendQueryFilterUtils.applySkipAndLimit(input, records);
|
||||
|
||||
return (records);
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ public class BackendQueryFilterUtils
|
||||
case IS_NOT_BLANK -> !testBlank(criterion, value);
|
||||
case CONTAINS -> testContains(criterion, fieldName, value);
|
||||
case NOT_CONTAINS -> !testContains(criterion, fieldName, value);
|
||||
case IS_NULL_OR_IN -> testBlank(criterion, value) || testIn(criterion, value);
|
||||
case STARTS_WITH -> testStartsWith(criterion, fieldName, value);
|
||||
case NOT_STARTS_WITH -> !testStartsWith(criterion, fieldName, value);
|
||||
case ENDS_WITH -> testEndsWith(criterion, fieldName, value);
|
||||
@ -438,13 +439,13 @@ public class BackendQueryFilterUtils
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(isGreaterThan(valueA, valueB) && orderBy.getIsAscending())
|
||||
else if(isGreaterThan(valueA, valueB))
|
||||
{
|
||||
return (-1);
|
||||
return (orderBy.getIsAscending() ? -1 : 1);
|
||||
}
|
||||
else
|
||||
else // Less Than
|
||||
{
|
||||
return (1);
|
||||
return (orderBy.getIsAscending() ? 1 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* 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.utils.collections;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.function.Supplier;
|
||||
import com.kingsrook.qqq.backend.core.utils.lambdas.VoidVoidMethod;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Object to wrap a List, so that in case a caller provided an immutable List,
|
||||
** you can safely perform mutating operations on it (in which case, it'll get
|
||||
** replaced by an actual mutable list).
|
||||
*******************************************************************************/
|
||||
public class MutableList<T> implements List<T>
|
||||
{
|
||||
private List<T> sourceList;
|
||||
private Class<? extends List<T>> mutableTypeIfNeeded;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MutableList(List<T> sourceList)
|
||||
{
|
||||
this(sourceList, (Class) ArrayList.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MutableList(List<T> sourceList, Class<? extends List<T>> mutableTypeIfNeeded)
|
||||
{
|
||||
this.sourceList = sourceList;
|
||||
this.mutableTypeIfNeeded = mutableTypeIfNeeded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void replaceSourceListWithMutableCopy()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<T> replacementList = mutableTypeIfNeeded.getConstructor().newInstance();
|
||||
replacementList.addAll(sourceList);
|
||||
sourceList = replacementList;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new IllegalStateException("The mutable type provided for this MutableList [" + mutableTypeIfNeeded.getName() + "] could not be instantiated."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private <T> T doMutableOperationForValue(Supplier<T> supplier)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (supplier.get());
|
||||
}
|
||||
catch(UnsupportedOperationException uoe)
|
||||
{
|
||||
replaceSourceListWithMutableCopy();
|
||||
return (supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void doMutableOperationForVoid(VoidVoidMethod method)
|
||||
{
|
||||
try
|
||||
{
|
||||
method.run();
|
||||
}
|
||||
catch(UnsupportedOperationException uoe)
|
||||
{
|
||||
replaceSourceListWithMutableCopy();
|
||||
method.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return (sourceList.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return (sourceList.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean contains(Object o)
|
||||
{
|
||||
return (sourceList.contains(o));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return (sourceList.iterator());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Object[] toArray()
|
||||
{
|
||||
return (sourceList.toArray());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public <T1> T1[] toArray(T1[] a)
|
||||
{
|
||||
return (sourceList.toArray(a));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean add(T t)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.add(t)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.remove(o)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c)
|
||||
{
|
||||
return (sourceList.containsAll(c));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> c)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.addAll(c)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> c)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.addAll(index, c)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.removeAll(c)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.retainAll(c)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
doMutableOperationForVoid(() -> sourceList.clear());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public T get(int index)
|
||||
{
|
||||
return (sourceList.get(index));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public T set(int index, T element)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.set(index, element)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void add(int index, T element)
|
||||
{
|
||||
doMutableOperationForVoid(() -> sourceList.add(index, element));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public T remove(int index)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceList.remove(index)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public int indexOf(Object o)
|
||||
{
|
||||
return (sourceList.indexOf(o));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public int lastIndexOf(Object o)
|
||||
{
|
||||
return (sourceList.lastIndexOf(o));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public ListIterator<T> listIterator()
|
||||
{
|
||||
return (sourceList.listIterator());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int index)
|
||||
{
|
||||
return (sourceList.listIterator(index));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<T> subList(int fromIndex, int toIndex)
|
||||
{
|
||||
return (sourceList.subList(fromIndex, toIndex));
|
||||
}
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.utils.collections;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import com.kingsrook.qqq.backend.core.utils.lambdas.VoidVoidMethod;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Object to wrap a Map, so that in case a caller provided an immutable Map,
|
||||
** you can safely perform mutating operations on it (in which case, it'll get
|
||||
** replaced by an actual mutable Map).
|
||||
*******************************************************************************/
|
||||
public class MutableMap<K, V> implements Map<K, V>
|
||||
{
|
||||
private Map<K, V> sourceMap;
|
||||
private Class<? extends Map<K, V>> mutableTypeIfNeeded;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MutableMap(Map<K, V> sourceMap)
|
||||
{
|
||||
this(sourceMap, (Class) HashMap.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MutableMap(Map<K, V> sourceMap, Class<? extends Map<K, V>> mutableTypeIfNeeded)
|
||||
{
|
||||
this.sourceMap = sourceMap;
|
||||
this.mutableTypeIfNeeded = mutableTypeIfNeeded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void replaceSourceMapWithMutableCopy()
|
||||
{
|
||||
try
|
||||
{
|
||||
Map<K, V> replacementMap = mutableTypeIfNeeded.getConstructor().newInstance();
|
||||
replacementMap.putAll(sourceMap);
|
||||
sourceMap = replacementMap;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new IllegalStateException("The mutable type provided for this MutableMap [" + mutableTypeIfNeeded.getName() + "] could not be instantiated."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private <T> T doMutableOperationForValue(Supplier<T> supplier)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (supplier.get());
|
||||
}
|
||||
catch(UnsupportedOperationException uoe)
|
||||
{
|
||||
replaceSourceMapWithMutableCopy();
|
||||
return (supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void doMutableOperationForVoid(VoidVoidMethod method)
|
||||
{
|
||||
try
|
||||
{
|
||||
method.run();
|
||||
}
|
||||
catch(UnsupportedOperationException uoe)
|
||||
{
|
||||
replaceSourceMapWithMutableCopy();
|
||||
method.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return (sourceMap.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return (sourceMap.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean containsKey(Object key)
|
||||
{
|
||||
return (sourceMap.containsKey(key));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean containsValue(Object value)
|
||||
{
|
||||
return (sourceMap.containsValue(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public V get(Object key)
|
||||
{
|
||||
return (sourceMap.get(key));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public V put(K key, V value)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceMap.put(key, value)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public V remove(Object key)
|
||||
{
|
||||
return (doMutableOperationForValue(() -> sourceMap.remove(key)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m)
|
||||
{
|
||||
doMutableOperationForVoid(() -> sourceMap.putAll(m));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
doMutableOperationForVoid(() -> sourceMap.clear());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Set<K> keySet()
|
||||
{
|
||||
return (sourceMap.keySet());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Collection<V> values()
|
||||
{
|
||||
return (sourceMap.values());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet()
|
||||
{
|
||||
return (sourceMap.entrySet());
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.utils.lambdas;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@FunctionalInterface
|
||||
public interface VoidVoidMethod
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
void run();
|
||||
|
||||
}
|
Reference in New Issue
Block a user