mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-17 20:50: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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,10 +22,8 @@
|
||||
package com.kingsrook.qqq.backend.javalin;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -98,6 +96,8 @@ import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.routeproviders.ProcessBasedRouterPayload;
|
||||
import com.kingsrook.qqq.middleware.javalin.routeproviders.authentication.SimpleRouteAuthenticator;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
@ -126,7 +126,7 @@ public class TestUtils
|
||||
public static final String SCREEN_0 = "screen0";
|
||||
public static final String SCREEN_1 = "screen1";
|
||||
|
||||
public static final String STATIC_SITE_PATH = Paths.get("").toAbsolutePath() + "/static-site";
|
||||
public static final String STATIC_SITE_PATH = "static-site";
|
||||
|
||||
|
||||
|
||||
@ -134,12 +134,9 @@ public class TestUtils
|
||||
** Prime a test database (e.g., h2, in-memory)
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void primeTestDatabase() throws Exception
|
||||
{
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
|
||||
try(Connection connection = connectionManager.getConnection(TestUtils.defineDefaultH2Backend()))
|
||||
try(Connection connection = ConnectionManager.getConnection(TestUtils.defineDefaultH2Backend()))
|
||||
{
|
||||
InputStream primeTestDatabaseSqlStream = TestUtils.class.getResourceAsStream("/prime-test-database.sql");
|
||||
assertNotNull(primeTestDatabaseSqlStream);
|
||||
@ -161,8 +158,7 @@ public class TestUtils
|
||||
*******************************************************************************/
|
||||
public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
||||
{
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
try(Connection connection = connectionManager.getConnection(defineDefaultH2Backend()))
|
||||
try(Connection connection = ConnectionManager.getConnection(defineDefaultH2Backend()))
|
||||
{
|
||||
QueryManager.executeStatement(connection, sql, resultSetProcessor);
|
||||
}
|
||||
@ -195,17 +191,24 @@ public class TestUtils
|
||||
defineWidgets(qInstance);
|
||||
|
||||
List<JavalinRouteProviderMetaData> routeProviders = new ArrayList<>();
|
||||
if(new File(STATIC_SITE_PATH).exists())
|
||||
{
|
||||
routeProviders.add(new JavalinRouteProviderMetaData()
|
||||
.withHostedPath("/statically-served")
|
||||
.withFileSystemPath(STATIC_SITE_PATH));
|
||||
}
|
||||
routeProviders.add(new JavalinRouteProviderMetaData()
|
||||
.withHostedPath("/statically-served")
|
||||
.withFileSystemPath(STATIC_SITE_PATH));
|
||||
|
||||
routeProviders.add(new JavalinRouteProviderMetaData()
|
||||
.withHostedPath("/protected-statically-served")
|
||||
.withFileSystemPath(STATIC_SITE_PATH)
|
||||
.withRouteAuthenticator(new QCodeReference(SimpleRouteAuthenticator.class)));
|
||||
|
||||
routeProviders.add(new JavalinRouteProviderMetaData()
|
||||
.withHostedPath("/served-by-process/<pagePath>")
|
||||
.withProcessName("routerProcess"));
|
||||
|
||||
routeProviders.add(new JavalinRouteProviderMetaData()
|
||||
.withHostedPath("/protected-served-by-process/<pagePath>")
|
||||
.withProcessName("routerProcess")
|
||||
.withRouteAuthenticator(new QCodeReference(SimpleRouteAuthenticator.class)));
|
||||
|
||||
qInstance.withSupplementalMetaData(new QJavalinMetaData().withRouteProviders(routeProviders));
|
||||
|
||||
qInstance.addBackend(defineMemoryBackend());
|
||||
@ -237,8 +240,10 @@ public class TestUtils
|
||||
.withName("step")
|
||||
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||
{
|
||||
String path = runBackendStepInput.getValueString("path");
|
||||
runBackendStepOutput.addValue("response", "So you've asked for: " + path);
|
||||
ProcessBasedRouterPayload processPayload = runBackendStepInput.getProcessPayload(ProcessBasedRouterPayload.class);
|
||||
String path = processPayload.getPath();
|
||||
processPayload.setResponseString("So you've asked for: " + path);
|
||||
runBackendStepOutput.setProcessPayload(processPayload);
|
||||
}))
|
||||
));
|
||||
}
|
||||
@ -793,7 +798,7 @@ public class TestUtils
|
||||
{
|
||||
return (new RenderWidgetOutput(new RawHTML("title",
|
||||
QContext.getQSession().getValue(QSession.VALUE_KEY_USER_TIMEZONE_OFFSET_MINUTES)
|
||||
+ "|" + QContext.getQSession().getValue(QSession.VALUE_KEY_USER_TIMEZONE)
|
||||
+ "|" + QContext.getQSession().getValue(QSession.VALUE_KEY_USER_TIMEZONE)
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -22,23 +22,18 @@
|
||||
package com.kingsrook.qqq.middleware.javalin;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.AbstractQQQApplication;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.TestUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
|
||||
import io.javalin.http.HttpStatus;
|
||||
import kong.unirest.HttpResponse;
|
||||
import kong.unirest.Unirest;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@ -55,17 +50,6 @@ class QApplicationJavalinServerTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
void beforeEach() throws IOException
|
||||
{
|
||||
FileUtils.writeStringToFile(new File(TestUtils.STATIC_SITE_PATH + "/foo.html"), "Foo? Bar!", Charset.defaultCharset());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -74,8 +58,6 @@ class QApplicationJavalinServerTest
|
||||
{
|
||||
javalinServer.stop();
|
||||
TestApplication.callCount = 0;
|
||||
|
||||
FileUtils.deleteDirectory(new File(TestUtils.STATIC_SITE_PATH));
|
||||
}
|
||||
|
||||
|
||||
@ -212,6 +194,35 @@ class QApplicationJavalinServerTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAuthenticatedStaticRouter() throws Exception
|
||||
{
|
||||
javalinServer = new QApplicationJavalinServer(getQqqApplication())
|
||||
.withServeFrontendMaterialDashboard(false)
|
||||
.withPort(PORT);
|
||||
javalinServer.start();
|
||||
|
||||
Unirest.config().setDefaultResponseEncoding("UTF-8")
|
||||
.followRedirects(false);
|
||||
|
||||
HttpResponse<String> response = Unirest.get("http://localhost:" + PORT + "/protected-statically-served/foo.html")
|
||||
.header("Authorization", "Bearer Deny")
|
||||
.asString();
|
||||
|
||||
assertEquals(HttpStatus.FOUND.getCode(), response.getStatus());
|
||||
assertThat(response.getHeaders().getFirst("Location")).contains("createMockSession");
|
||||
|
||||
response = Unirest.get("http://localhost:" + PORT + "/protected-statically-served/foo.html")
|
||||
.asString();
|
||||
assertEquals(HttpStatus.OK.getCode(), response.getStatus());
|
||||
assertEquals("Foo? Bar!", response.getBody());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -230,6 +241,35 @@ class QApplicationJavalinServerTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAuthenticatedProcessRouter() throws Exception
|
||||
{
|
||||
javalinServer = new QApplicationJavalinServer(getQqqApplication())
|
||||
.withServeFrontendMaterialDashboard(false)
|
||||
.withPort(PORT);
|
||||
javalinServer.start();
|
||||
|
||||
Unirest.config().setDefaultResponseEncoding("UTF-8")
|
||||
.followRedirects(false);
|
||||
|
||||
HttpResponse<String> response = Unirest.get("http://localhost:" + PORT + "/protected-served-by-process/foo.html")
|
||||
.header("Authorization", "Bearer Deny")
|
||||
.asString();
|
||||
|
||||
assertEquals(HttpStatus.FOUND.getCode(), response.getStatus());
|
||||
assertThat(response.getHeaders().getFirst("Location")).contains("createMockSession");
|
||||
|
||||
response = Unirest.get("http://localhost:" + PORT + "/protected-statically-served/foo.html")
|
||||
.asString();
|
||||
assertEquals(200, response.getStatus());
|
||||
assertEquals("So you've asked for: /served-by-process/foo.html", response.getBody());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
|
@ -0,0 +1 @@
|
||||
Foo? Bar!
|
Reference in New Issue
Block a user