diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java index 0ff5c246..cf050a18 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QSession.java @@ -42,7 +42,7 @@ import com.kingsrook.qqq.backend.core.utils.collections.MutableMap; /******************************************************************************* ** *******************************************************************************/ -public class QSession implements Serializable +public class QSession implements Serializable, Cloneable { private String idReference; private QUser user; @@ -68,6 +68,58 @@ public class QSession implements Serializable + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public QSession clone() throws CloneNotSupportedException + { + QSession clone = (QSession) super.clone(); + + if(user != null) + { + clone.user = user.clone(); + } + + if(permissions != null) + { + clone.permissions = new HashSet<>(); + clone.permissions.addAll(permissions); + } + + if(securityKeyValues != null) + { + clone.securityKeyValues = new HashMap<>(); + for(Map.Entry> entry : securityKeyValues.entrySet()) + { + List cloneValues = entry.getValue() == null ? null : new ArrayList<>(entry.getValue()); + clone.securityKeyValues.put(entry.getKey(), cloneValues); + } + } + + if(backendVariants != null) + { + clone.backendVariants = new HashMap<>(); + clone.backendVariants.putAll(backendVariants); + } + + if(values != null) + { + clone.values = new HashMap<>(); + clone.values.putAll(values); + } + + if(valuesForFrontend != null) + { + clone.valuesForFrontend = new HashMap<>(); + clone.valuesForFrontend.putAll(valuesForFrontend); + } + + return (clone); + } + + + /******************************************************************************* ** Default constructor, puts a uuid in the session ** diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QUser.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QUser.java index 1ff995a0..1adcc3a4 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QUser.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/session/QUser.java @@ -25,13 +25,24 @@ package com.kingsrook.qqq.backend.core.model.session; /******************************************************************************* ** *******************************************************************************/ -public class QUser +public class QUser implements Cloneable { private String idReference; private String fullName; + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public QUser clone() throws CloneNotSupportedException + { + return (QUser) super.clone(); + } + + + /******************************************************************************* ** Getter for idReference ** diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleCustomizerInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleCustomizerInterface.java index 01cb9a7a..021b8d4c 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleCustomizerInterface.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleCustomizerInterface.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.modules.authentication; import java.io.Serializable; import java.util.Map; +import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.session.QSession; @@ -62,4 +63,14 @@ public interface QAuthenticationModuleCustomizerInterface ////////// } + /******************************************************************************* + ** + *******************************************************************************/ + default void customizeAutomatedSessionForUser(QInstance qInstance, QSession automatedSessionForUser, Serializable userId) throws QAuthenticationException + { + ////////// + // noop // + ////////// + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java index 6cd21ab6..28db59d6 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/QAuthenticationModuleInterface.java @@ -22,12 +22,15 @@ package com.kingsrook.qqq.backend.core.modules.authentication; +import java.io.Serializable; import java.util.Map; +import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.AccessTokenException; import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.session.QSession; +import com.kingsrook.qqq.backend.core.utils.ValueUtils; import org.apache.commons.lang.NotImplementedException; @@ -49,6 +52,27 @@ public interface QAuthenticationModuleInterface boolean isSessionValid(QInstance instance, QSession session); + /******************************************************************************* + ** + *******************************************************************************/ + default QSession createAutomatedSessionForUser(QInstance qInstance, Serializable userId) throws QAuthenticationException + { + try + { + QSession clone = QContext.getQSession().clone(); + if(clone.getUser() != null) + { + clone.getUser().setIdReference(ValueUtils.getValueAsString(userId)); + } + return clone; + } + catch(CloneNotSupportedException e) + { + throw (new QAuthenticationException("Cloning session failed", e)); + } + } + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java index 5cd096df..03b8b540 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/authentication/implementations/Auth0AuthenticationModule.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.backend.core.modules.authentication.implementations; +import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.security.interfaces.RSAPublicKey; import java.time.Duration; @@ -622,7 +623,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface // set security keys in the session from the JWT // /////////////////////////////////////////////////// setSecurityKeysInSessionFromJwtPayload(qInstance, payload, qSession); - + ////////////////////////////////////////////////////////////// // allow customizer to do custom things here, if so desired // ////////////////////////////////////////////////////////////// @@ -1108,4 +1109,20 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface return (null); } } + + + + /******************************************************************************* + ** e.g., if a scheduled job needs to run as a user (say, a report)... + *******************************************************************************/ + @Override + public QSession createAutomatedSessionForUser(QInstance qInstance, Serializable userId) throws QAuthenticationException + { + QSession automatedSessionForUser = QAuthenticationModuleInterface.super.createAutomatedSessionForUser(qInstance, userId); + if(getCustomizer() != null) + { + getCustomizer().customizeAutomatedSessionForUser(qInstance, automatedSessionForUser, userId); + } + return (automatedSessionForUser); + } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/savedreports/RunScheduledReportExecuteStep.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/savedreports/RunScheduledReportExecuteStep.java index 038f6c04..8ad4b673 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/savedreports/RunScheduledReportExecuteStep.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/savedreports/RunScheduledReportExecuteStep.java @@ -26,6 +26,7 @@ import java.util.List; import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallbackFactory; import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction; +import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException; import com.kingsrook.qqq.backend.core.logging.QLogger; @@ -34,7 +35,11 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutp import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.savedreports.ScheduledReport; +import com.kingsrook.qqq.backend.core.model.session.QSession; +import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleDispatcher; +import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.JsonUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils; @@ -69,6 +74,11 @@ public class RunScheduledReportExecuteStep implements BackendStep ScheduledReport scheduledReport = new ScheduledReport(records.get(0)); scheduledReportId = scheduledReport.getId(); + //////////////////////////////////////////////////////////////////////////////////// + // get the schedule's user - as that will drive the security key we need to apply // + //////////////////////////////////////////////////////////////////////////////////// + updateSessionForUser(scheduledReport.getUserId()); + ///////////////////////////////////////////// // run the process that renders the report // ///////////////////////////////////////////// @@ -110,4 +120,34 @@ public class RunScheduledReportExecuteStep implements BackendStep } } + + + /******************************************************************************* + ** + *******************************************************************************/ + private void updateSessionForUser(String userId) throws QException + { + try + { + QInstance qInstance = QContext.getQInstance(); + QAuthenticationModuleDispatcher qAuthenticationModuleDispatcher = new QAuthenticationModuleDispatcher(); + QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(qInstance.getAuthentication()); + + /////////////////////////////////////// + // create automated-session for user // + /////////////////////////////////////// + QSession session = authenticationModule.createAutomatedSessionForUser(qInstance, userId); + + ///////////////////////////////////////////// + // set that session in the current context // + ///////////////////////////////////////////// + QContext.setQSession(session); + } + catch(Exception e) + { + LOG.warn("Error setting up user session for running scheduled report", e, logPair("userId", userId)); + throw (new QException("Error setting up user session for running scheduled report", e)); + } + } + }