mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-21 06:28:44 +00:00
Working version of authentication for static & dynamic (process) route providers
This commit is contained in:
@ -77,8 +77,8 @@ public class QApplicationJavalinServer
|
||||
private boolean serveLegacyUnversionedMiddlewareAPI = true;
|
||||
private List<AbstractMiddlewareVersion> middlewareVersionList = List.of(new MiddlewareVersionV1());
|
||||
private List<QJavalinRouteProviderInterface> additionalRouteProviders = null;
|
||||
private Consumer<Javalin> javalinConfigurationCustomizer = null;
|
||||
private QJavalinMetaData javalinMetaData = null;
|
||||
private Consumer<Javalin> javalinConfigurationCustomizer = null;
|
||||
private QJavalinMetaData javalinMetaData = null;
|
||||
|
||||
private long lastQInstanceHotSwapMillis;
|
||||
private long millisBetweenHotSwaps = 2500;
|
||||
@ -197,6 +197,15 @@ public class QApplicationJavalinServer
|
||||
}
|
||||
});
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// also pass the javalin service into any additionalRouteProviders, //
|
||||
// in case they need additional setup, e.g., before/after handlers. //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
for(QJavalinRouteProviderInterface routeProvider : CollectionUtils.nonNullList(additionalRouteProviders))
|
||||
{
|
||||
routeProvider.acceptJavalinService(service);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// per system property, set the server to hot-swap the q instance before all routes //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -228,7 +237,7 @@ public class QApplicationJavalinServer
|
||||
/***************************************************************************
|
||||
** initial tests with the SimpleFileSystemDirectoryRouter would sometimes
|
||||
** have a Content-Type:text/html;charset=null !
|
||||
** which doesn't seem every valid (and at least it broke our unit test).
|
||||
** which doesn't seem ever valid (and at least it broke our unit test).
|
||||
** so, if w see charset=null in contentType, replace it with the system
|
||||
** default, which may not be 100% right, but has to be better than "null"...
|
||||
***************************************************************************/
|
||||
@ -242,7 +251,6 @@ public class QApplicationJavalinServer
|
||||
contentType = contentType.replace("charset=null", "charset=" + Charset.defaultCharset().name());
|
||||
context.res().setContentType(contentType);
|
||||
}
|
||||
System.out.println();
|
||||
});
|
||||
}
|
||||
|
||||
@ -630,6 +638,7 @@ public class QApplicationJavalinServer
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for javalinMetaData
|
||||
*******************************************************************************/
|
||||
@ -659,5 +668,4 @@ public class QApplicationJavalinServer
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.middleware.javalin;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.apibuilder.EndpointGroup;
|
||||
import io.javalin.config.JavalinConfig;
|
||||
|
||||
@ -53,7 +54,9 @@ public interface QJavalinRouteProviderInterface
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
** when the javalin service is being configured as part of its boot up,
|
||||
** accept the javalinConfig object, to perform whatever setup you need,
|
||||
** such as setting up routes.
|
||||
***************************************************************************/
|
||||
default void acceptJavalinConfig(JavalinConfig config)
|
||||
{
|
||||
@ -61,4 +64,17 @@ public interface QJavalinRouteProviderInterface
|
||||
// noop at default //
|
||||
/////////////////////
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** when the javalin service is being configured as part of its boot up,
|
||||
** accept the Javalin service object, to perform whatever setup you need,
|
||||
** such as setting up before/after handlers.
|
||||
***************************************************************************/
|
||||
default void acceptJavalinService(Javalin service)
|
||||
{
|
||||
/////////////////////
|
||||
// noop at default //
|
||||
/////////////////////
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.middleware.javalin.metadata;
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QMetaDataObject;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -38,6 +39,8 @@ public class JavalinRouteProviderMetaData implements QMetaDataObject
|
||||
|
||||
private List<String> methods;
|
||||
|
||||
private QCodeReference routeAuthenticator;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -172,4 +175,35 @@ public class JavalinRouteProviderMetaData implements QMetaDataObject
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public QCodeReference getRouteAuthenticator()
|
||||
{
|
||||
return (this.routeAuthenticator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public void setRouteAuthenticator(QCodeReference routeAuthenticator)
|
||||
{
|
||||
this.routeAuthenticator = routeAuthenticator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public JavalinRouteProviderMetaData withRouteAuthenticator(QCodeReference routeAuthenticator)
|
||||
{
|
||||
this.routeAuthenticator = routeAuthenticator;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,22 +27,31 @@ import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.StorageAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
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.actions.tables.storage.StorageInput;
|
||||
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.session.QSystemUserSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.QJavalinRouteProviderInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.routeproviders.authentication.RouteAuthenticatorInterface;
|
||||
import io.javalin.apibuilder.ApiBuilder;
|
||||
import io.javalin.apibuilder.EndpointGroup;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.HttpStatus;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -55,7 +64,10 @@ public class ProcessBasedRouter implements QJavalinRouteProviderInterface
|
||||
private final String hostedPath;
|
||||
private final String processName;
|
||||
private final List<String> methods;
|
||||
private QInstance qInstance;
|
||||
|
||||
private QCodeReference routeAuthenticator;
|
||||
|
||||
private QInstance qInstance;
|
||||
|
||||
|
||||
|
||||
@ -76,6 +88,7 @@ public class ProcessBasedRouter implements QJavalinRouteProviderInterface
|
||||
public ProcessBasedRouter(JavalinRouteProviderMetaData routeProvider)
|
||||
{
|
||||
this(routeProvider.getHostedPath(), routeProvider.getProcessName(), routeProvider.getMethods());
|
||||
setRouteAuthenticator(routeProvider.getRouteAuthenticator());
|
||||
}
|
||||
|
||||
|
||||
@ -145,41 +158,36 @@ public class ProcessBasedRouter implements QJavalinRouteProviderInterface
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.setProcessName(processName);
|
||||
|
||||
try
|
||||
QContext.init(qInstance, new QSystemUserSession());
|
||||
|
||||
boolean isAuthenticated = false;
|
||||
if(routeAuthenticator == null)
|
||||
{
|
||||
QJavalinImplementation.setupSession(context, input);
|
||||
isAuthenticated = true;
|
||||
}
|
||||
catch(Exception e)
|
||||
else
|
||||
{
|
||||
context.header("WWW-Authenticate", "Basic realm=\"Access to this QQQ site\"");
|
||||
context.status(HttpStatus.UNAUTHORIZED);
|
||||
try
|
||||
{
|
||||
RouteAuthenticatorInterface routeAuthenticator = QCodeLoader.getAdHoc(RouteAuthenticatorInterface.class, this.routeAuthenticator);
|
||||
isAuthenticated = routeAuthenticator.authenticateRequest(context);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
context.skipRemainingHandlers();
|
||||
QJavalinImplementation.handleException(context, e);
|
||||
}
|
||||
}
|
||||
|
||||
if(!isAuthenticated)
|
||||
{
|
||||
LOG.info("Request is not authenticated, so returning before running process", logPair("processName", processName), logPair("path", context.path()));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
boolean authorized = false;
|
||||
String authorization = context.header("Authorization");
|
||||
if(authorization != null && authorization.matches("^Basic .+"))
|
||||
{
|
||||
String base64Authorization = authorization.substring("Basic ".length());
|
||||
String decoded = new String(Base64.getDecoder().decode(base64Authorization), StandardCharsets.UTF_8);
|
||||
String[] parts = decoded.split(":", 2);
|
||||
|
||||
QAuthenticationModuleDispatcher qAuthenticationModuleDispatcher = new QAuthenticationModuleDispatcher();
|
||||
QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(qInstance.getAuthentication());
|
||||
}
|
||||
|
||||
if(!authorized)
|
||||
{
|
||||
}
|
||||
|
||||
// todo - not always system-user session!!
|
||||
QContext.init(this.qInstance, new QSystemUserSession());
|
||||
*/
|
||||
|
||||
try
|
||||
{
|
||||
LOG.info("Running [" + processName + "] to serve [" + context.path() + "]...");
|
||||
LOG.info("Running process to serve route", logPair("processName", processName), logPair("path", context.path()));
|
||||
|
||||
/////////////////////
|
||||
// run the process //
|
||||
@ -190,16 +198,10 @@ public class ProcessBasedRouter implements QJavalinRouteProviderInterface
|
||||
input.addValue("pathParams", new HashMap<>(context.pathParamMap()));
|
||||
input.addValue("queryParams", new HashMap<>(context.queryParamMap()));
|
||||
input.addValue("formParams", new HashMap<>(context.formParamMap()));
|
||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||
input.addValue("cookies", new HashMap<>(context.cookieMap()));
|
||||
input.addValue("requestHeaders", new HashMap<>(context.headerMap()));
|
||||
|
||||
/////////////////
|
||||
// status code //
|
||||
/////////////////
|
||||
Integer statusCode = runProcessOutput.getValueInteger("statusCode");
|
||||
if(statusCode != null)
|
||||
{
|
||||
context.status(statusCode);
|
||||
}
|
||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||
|
||||
/////////////////
|
||||
// headers map //
|
||||
@ -217,26 +219,46 @@ public class ProcessBasedRouter implements QJavalinRouteProviderInterface
|
||||
// maybe via the callback object??? input.setCallback(new QProcessCallback() {});
|
||||
// context.resultInputStream();
|
||||
|
||||
///////////////////
|
||||
// response body //
|
||||
///////////////////
|
||||
Serializable response = runProcessOutput.getValue("response");
|
||||
if(response instanceof String s)
|
||||
//////////////
|
||||
// response //
|
||||
//////////////
|
||||
Integer statusCode = runProcessOutput.getValueInteger("statusCode");
|
||||
String redirectURL = runProcessOutput.getValueString("redirectURL");
|
||||
String responseString = runProcessOutput.getValueString("responseString");
|
||||
byte[] responseBytes = runProcessOutput.getValueByteArray("responseBytes");
|
||||
StorageInput responseStorageInput = (StorageInput) runProcessOutput.getValue("responseStorageInput");
|
||||
|
||||
if(StringUtils.hasContent(redirectURL))
|
||||
{
|
||||
context.result(s);
|
||||
context.redirect(redirectURL, statusCode == null ? HttpStatus.FOUND : HttpStatus.forStatus(statusCode));
|
||||
return;
|
||||
}
|
||||
else if(response instanceof byte[] ba)
|
||||
|
||||
if(statusCode != null)
|
||||
{
|
||||
context.result(ba);
|
||||
context.status(statusCode);
|
||||
}
|
||||
else if(response instanceof InputStream is)
|
||||
|
||||
if(StringUtils.hasContent(responseString))
|
||||
{
|
||||
context.result(is);
|
||||
context.result(responseString);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if(responseBytes != null && responseBytes.length > 0)
|
||||
{
|
||||
context.result(ValueUtils.getValueAsString(response));
|
||||
context.result(responseBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if(responseStorageInput != null)
|
||||
{
|
||||
InputStream inputStream = new StorageAction().getInputStream(responseStorageInput);
|
||||
context.result(inputStream);
|
||||
return;
|
||||
}
|
||||
|
||||
throw (new QException("No response value was set in the process output state."));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@ -248,4 +270,35 @@ public class ProcessBasedRouter implements QJavalinRouteProviderInterface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public QCodeReference getRouteAuthenticator()
|
||||
{
|
||||
return (this.routeAuthenticator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public void setRouteAuthenticator(QCodeReference routeAuthenticator)
|
||||
{
|
||||
this.routeAuthenticator = routeAuthenticator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouter withRouteAuthenticator(QCodeReference routeAuthenticator)
|
||||
{
|
||||
this.routeAuthenticator = routeAuthenticator;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,412 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.middleware.javalin.routeproviders;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessState;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.QProcessPayload;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** process payload shared the processes which are used as process-based-router
|
||||
** processes. e.g., the fields here are those written to and read by
|
||||
** ProcessBasedRouter.
|
||||
*******************************************************************************/
|
||||
public class ProcessBasedRouterPayload extends QProcessPayload
|
||||
{
|
||||
private String path;
|
||||
private String method;
|
||||
private Map<String, String> pathParams;
|
||||
private Map<String, String> queryParams;
|
||||
private Map<String, String> formParams;
|
||||
private Map<String, String> cookies;
|
||||
|
||||
private Integer statusCode;
|
||||
private String redirectURL;
|
||||
private Map<String, String> responseHeaders;
|
||||
private String responseString;
|
||||
private byte[] responseBytes;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload(ProcessState processState)
|
||||
{
|
||||
this.populateFromProcessState(processState);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for path
|
||||
*******************************************************************************/
|
||||
public String getPath()
|
||||
{
|
||||
return (this.path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for path
|
||||
*******************************************************************************/
|
||||
public void setPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for path
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for method
|
||||
*******************************************************************************/
|
||||
public String getMethod()
|
||||
{
|
||||
return (this.method);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for method
|
||||
*******************************************************************************/
|
||||
public void setMethod(String method)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for method
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withMethod(String method)
|
||||
{
|
||||
this.method = method;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for pathParams
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getPathParams()
|
||||
{
|
||||
return (this.pathParams);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for pathParams
|
||||
*******************************************************************************/
|
||||
public void setPathParams(Map<String, String> pathParams)
|
||||
{
|
||||
this.pathParams = pathParams;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for pathParams
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withPathParams(Map<String, String> pathParams)
|
||||
{
|
||||
this.pathParams = pathParams;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryParams
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getQueryParams()
|
||||
{
|
||||
return (this.queryParams);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryParams
|
||||
*******************************************************************************/
|
||||
public void setQueryParams(Map<String, String> queryParams)
|
||||
{
|
||||
this.queryParams = queryParams;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryParams
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withQueryParams(Map<String, String> queryParams)
|
||||
{
|
||||
this.queryParams = queryParams;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for formParams
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getFormParams()
|
||||
{
|
||||
return (this.formParams);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for formParams
|
||||
*******************************************************************************/
|
||||
public void setFormParams(Map<String, String> formParams)
|
||||
{
|
||||
this.formParams = formParams;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for formParams
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withFormParams(Map<String, String> formParams)
|
||||
{
|
||||
this.formParams = formParams;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for cookies
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getCookies()
|
||||
{
|
||||
return (this.cookies);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for cookies
|
||||
*******************************************************************************/
|
||||
public void setCookies(Map<String, String> cookies)
|
||||
{
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for cookies
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withCookies(Map<String, String> cookies)
|
||||
{
|
||||
this.cookies = cookies;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statusCode
|
||||
*******************************************************************************/
|
||||
public Integer getStatusCode()
|
||||
{
|
||||
return (this.statusCode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statusCode
|
||||
*******************************************************************************/
|
||||
public void setStatusCode(Integer statusCode)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statusCode
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withStatusCode(Integer statusCode)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for responseHeaders
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getResponseHeaders()
|
||||
{
|
||||
return (this.responseHeaders);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for responseHeaders
|
||||
*******************************************************************************/
|
||||
public void setResponseHeaders(Map<String, String> responseHeaders)
|
||||
{
|
||||
this.responseHeaders = responseHeaders;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for responseHeaders
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withResponseHeaders(Map<String, String> responseHeaders)
|
||||
{
|
||||
this.responseHeaders = responseHeaders;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for responseString
|
||||
*******************************************************************************/
|
||||
public String getResponseString()
|
||||
{
|
||||
return (this.responseString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for responseString
|
||||
*******************************************************************************/
|
||||
public void setResponseString(String responseString)
|
||||
{
|
||||
this.responseString = responseString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for responseString
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withResponseString(String responseString)
|
||||
{
|
||||
this.responseString = responseString;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for responseBytes
|
||||
*******************************************************************************/
|
||||
public byte[] getResponseBytes()
|
||||
{
|
||||
return (this.responseBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for responseBytes
|
||||
*******************************************************************************/
|
||||
public void setResponseBytes(byte[] responseBytes)
|
||||
{
|
||||
this.responseBytes = responseBytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for responseBytes
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withResponseBytes(byte[] responseBytes)
|
||||
{
|
||||
this.responseBytes = responseBytes;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for redirectURL
|
||||
*******************************************************************************/
|
||||
public String getRedirectURL()
|
||||
{
|
||||
return (this.redirectURL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for redirectURL
|
||||
*******************************************************************************/
|
||||
public void setRedirectURL(String redirectURL)
|
||||
{
|
||||
this.redirectURL = redirectURL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for redirectURL
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouterPayload withRedirectURL(String redirectURL)
|
||||
{
|
||||
this.redirectURL = redirectURL;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -22,22 +22,40 @@
|
||||
package com.kingsrook.qqq.middleware.javalin.routeproviders;
|
||||
|
||||
|
||||
import java.net.URL;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
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.session.QSystemUserSession;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.middleware.javalin.QJavalinRouteProviderInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.routeproviders.authentication.RouteAuthenticatorInterface;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.config.JavalinConfig;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.staticfiles.Location;
|
||||
import io.javalin.http.staticfiles.StaticFileConfig;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** javalin route provider that hosts a path in the http server via a path on
|
||||
** the file system
|
||||
*******************************************************************************/
|
||||
public class SimpleFileSystemDirectoryRouter implements QJavalinRouteProviderInterface
|
||||
{
|
||||
private final String hostedPath;
|
||||
private final String fileSystemPath;
|
||||
private QInstance qInstance;
|
||||
private static final QLogger LOG = QLogger.getLogger(SimpleFileSystemDirectoryRouter.class);
|
||||
|
||||
private final String hostedPath;
|
||||
private final String fileSystemPath;
|
||||
|
||||
private QCodeReference routeAuthenticator;
|
||||
|
||||
private QInstance qInstance;
|
||||
|
||||
|
||||
|
||||
@ -59,6 +77,7 @@ public class SimpleFileSystemDirectoryRouter implements QJavalinRouteProviderInt
|
||||
public SimpleFileSystemDirectoryRouter(JavalinRouteProviderMetaData routeProvider)
|
||||
{
|
||||
this(routeProvider.getHostedPath(), routeProvider.getFileSystemPath());
|
||||
setRouteAuthenticator(routeProvider.getRouteAuthenticator());
|
||||
}
|
||||
|
||||
|
||||
@ -74,17 +93,132 @@ public class SimpleFileSystemDirectoryRouter implements QJavalinRouteProviderInt
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void handleJavalinStaticFileConfig(StaticFileConfig staticFileConfig)
|
||||
{
|
||||
URL resource = getClass().getClassLoader().getResource(fileSystemPath);
|
||||
if(resource == null)
|
||||
{
|
||||
String message = "Could not find file system path: " + fileSystemPath;
|
||||
if(fileSystemPath.startsWith("/") && getClass().getClassLoader().getResource(fileSystemPath.replaceFirst("^/+", "")) != null)
|
||||
{
|
||||
message += ". For non-absolute paths, do not prefix with a leading slash.";
|
||||
}
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
|
||||
if(!hostedPath.startsWith("/"))
|
||||
{
|
||||
LOG.warn("hostedPath [" + hostedPath + "] should probably start with a leading slash...");
|
||||
}
|
||||
|
||||
staticFileConfig.directory = resource.getFile();
|
||||
staticFileConfig.hostedPath = hostedPath;
|
||||
staticFileConfig.location = Location.EXTERNAL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void before(Context context) throws QException
|
||||
{
|
||||
LOG.debug("In before handler for simpleFileSystemRouter", logPair("hostedPath", hostedPath));
|
||||
QContext.init(qInstance, new QSystemUserSession());
|
||||
|
||||
if(routeAuthenticator != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
RouteAuthenticatorInterface routeAuthenticator = QCodeLoader.getAdHoc(RouteAuthenticatorInterface.class, this.routeAuthenticator);
|
||||
boolean isAuthenticated = routeAuthenticator.authenticateRequest(context);
|
||||
if(!isAuthenticated)
|
||||
{
|
||||
LOG.info("Static file request is not authenticated, so telling javalin to skip remaining handlers", logPair("path", context.path()));
|
||||
context.skipRemainingHandlers();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
context.skipRemainingHandlers();
|
||||
QJavalinImplementation.handleException(context, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void after(Context context)
|
||||
{
|
||||
LOG.debug("In after handler for simpleFileSystemRouter", logPair("hostedPath", hostedPath));
|
||||
QContext.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void acceptJavalinConfig(JavalinConfig config)
|
||||
{
|
||||
config.staticFiles.add((StaticFileConfig userConfig) ->
|
||||
{
|
||||
userConfig.hostedPath = hostedPath;
|
||||
userConfig.directory = fileSystemPath;
|
||||
userConfig.location = Location.EXTERNAL;
|
||||
});
|
||||
config.staticFiles.add(this::handleJavalinStaticFileConfig);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void acceptJavalinService(Javalin service)
|
||||
{
|
||||
String javalinPath = hostedPath;
|
||||
if(!javalinPath.endsWith("/"))
|
||||
{
|
||||
javalinPath += "/";
|
||||
}
|
||||
javalinPath += "<subPath>";
|
||||
|
||||
service.before(javalinPath, this::before);
|
||||
service.before(javalinPath, this::after);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public QCodeReference getRouteAuthenticator()
|
||||
{
|
||||
return (this.routeAuthenticator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public void setRouteAuthenticator(QCodeReference routeAuthenticator)
|
||||
{
|
||||
this.routeAuthenticator = routeAuthenticator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for routeAuthenticator
|
||||
*******************************************************************************/
|
||||
public SimpleFileSystemDirectoryRouter withRouteAuthenticator(QCodeReference routeAuthenticator)
|
||||
{
|
||||
this.routeAuthenticator = routeAuthenticator;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.middleware.javalin.routeproviders.authentication;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** interface used by QJavalinRouteProviderInterface subclasses, to interact with
|
||||
** QQQ Authentication modules, to provide authentication to custom javalin routes.
|
||||
*******************************************************************************/
|
||||
public interface RouteAuthenticatorInterface
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
** where authentication for a route occurs, before the route is served.
|
||||
**
|
||||
** @return true if request is authenticated; else false
|
||||
***************************************************************************/
|
||||
boolean authenticateRequest(Context context) throws QException;
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.middleware.javalin.routeproviders.authentication;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
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.javalin.QJavalinImplementation;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** simple implementation of a route authenticator. Assumes that unauthenticated
|
||||
** requests should redirect to a login page. Note though, maybe that should be
|
||||
** more intelligent, like, only redirect requets for a .html file, but not
|
||||
** requests for include files like images or .js/.css?
|
||||
*******************************************************************************/
|
||||
public class SimpleRouteAuthenticator implements RouteAuthenticatorInterface
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(SimpleRouteAuthenticator.class);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public boolean authenticateRequest(Context context) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
QSession qSession = QJavalinImplementation.setupSession(context, null);
|
||||
LOG.debug("Session has been activated", "uuid=" + qSession.getUuid());
|
||||
return (true);
|
||||
}
|
||||
catch(QAuthenticationException e)
|
||||
{
|
||||
QAuthenticationModuleDispatcher qAuthenticationModuleDispatcher = new QAuthenticationModuleDispatcher();
|
||||
QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(QContext.getQInstance().getAuthentication());
|
||||
|
||||
String redirectURL = authenticationModule.getLoginRedirectUrl(context.fullUrl());
|
||||
|
||||
context.redirect(redirectURL);
|
||||
LOG.debug("Redirecting request, due to required session missing");
|
||||
return (false);
|
||||
}
|
||||
catch(QModuleDispatchException e)
|
||||
{
|
||||
throw (new QException("Error authenticating request", e));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user