Support api-key in query-string for backend-api;

This commit is contained in:
2023-06-29 11:15:24 -05:00
parent b75fd29a57
commit 3ae938ac6e
7 changed files with 169 additions and 32 deletions

View File

@ -24,6 +24,8 @@ package com.kingsrook.qqq.backend.module.api.actions;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
@ -644,6 +646,20 @@ public class BaseAPIActionUtil
request.setHeader("Authorization", "Bearer " + getOAuth2Token());
break;
case API_KEY_QUERY_PARAM:
try
{
String uri = request.getURI().toString();
uri += (uri.contains("?") ? "&" : "?");
uri += backendMetaData.getApiKeyQueryParamName() + "=" + backendMetaData.getApiKey();
request.setURI(new URI(uri));
}
catch(URISyntaxException e)
{
throw (new QException("Error setting authorization query parameter", e));
}
break;
default:
throw new IllegalArgumentException("Unexpected authorization type: " + backendMetaData.getAuthorizationType());
}
@ -1051,37 +1067,53 @@ public class BaseAPIActionUtil
*******************************************************************************/
protected void logOutboundApiCall(HttpRequestBase request, QHttpResponse response)
{
QTableMetaData table = QContext.getQInstance().getTable(OutboundAPILog.TABLE_NAME);
if(table == null)
try
{
return;
}
QTableMetaData table = QContext.getQInstance().getTable(OutboundAPILog.TABLE_NAME);
if(table == null)
{
return;
}
String requestBody = null;
if(request instanceof HttpEntityEnclosingRequest entityRequest)
String requestBody = null;
if(request instanceof HttpEntityEnclosingRequest entityRequest)
{
try
{
requestBody = StringUtils.join("\n", IOUtils.readLines(entityRequest.getEntity().getContent()));
}
catch(Exception e)
{
// leave it null...
}
}
////////////////////////////////////
// mask api keys in query strings //
////////////////////////////////////
String url = request.getURI().toString();
if(backendMetaData.getAuthorizationType().equals(AuthorizationType.API_KEY_QUERY_PARAM))
{
url = url.replaceFirst(backendMetaData.getApiKey(), "******");
}
InsertInput insertInput = new InsertInput();
insertInput.setTableName(table.getName());
insertInput.setRecords(List.of(new OutboundAPILog()
.withMethod(request.getMethod())
.withUrl(url)
.withTimestamp(Instant.now())
.withRequestBody(requestBody)
.withStatusCode(response.getStatusCode())
.withResponseBody(response.getContent())
.toQRecord()
));
new InsertAction().executeAsync(insertInput);
}
catch(Exception e)
{
try
{
requestBody = StringUtils.join("\n", IOUtils.readLines(entityRequest.getEntity().getContent()));
}
catch(Exception e)
{
// leave it null...
}
LOG.warn("Error logging outbound api call", e);
}
InsertInput insertInput = new InsertInput();
insertInput.setTableName(table.getName());
insertInput.setRecords(List.of(new OutboundAPILog()
.withMethod(request.getMethod())
.withUrl(request.getURI().toString()) // todo - does this have the query string?
.withTimestamp(Instant.now())
.withRequestBody(requestBody)
.withStatusCode(response.getStatusCode())
.withResponseBody(response.getContent())
.toQRecord()
));
new InsertAction().executeAsync(insertInput);
}

View File

@ -31,5 +31,5 @@ public enum AuthorizationType
BASIC_AUTH_API_KEY,
BASIC_AUTH_USERNAME_PASSWORD,
OAUTH2,
API_KEY_QUERY_PARAM,
}

View File

@ -43,6 +43,7 @@ public class APIBackendMetaData extends QBackendMetaData
private String clientSecret;
private String username;
private String password;
private String apiKeyQueryParamName;
private AuthorizationType authorizationType;
private String contentType; // todo enum?
@ -460,6 +461,16 @@ public class APIBackendMetaData extends QBackendMetaData
public void performValidation(QInstanceValidator qInstanceValidator)
{
qInstanceValidator.assertCondition(StringUtils.hasContent(baseUrl), "Missing baseUrl for API backend: " + getName());
if(AuthorizationType.API_KEY_QUERY_PARAM.equals(authorizationType))
{
qInstanceValidator.assertCondition(StringUtils.hasContent(apiKey), "Missing apiKey for API backend: " + getName() + " (required when using AuthorizationType=API_KEY_QUERY_PARAM))");
qInstanceValidator.assertCondition(StringUtils.hasContent(apiKeyQueryParamName), "Missing apiKeyQueryParamName for API backend: " + getName() + " (required when using AuthorizationType=API_KEY_QUERY_PARAM))");
}
else
{
qInstanceValidator.assertCondition(!StringUtils.hasContent(apiKeyQueryParamName), "Unexpected apiKeyQueryParamName for API backend: " + getName() + " (only allowed when using AuthorizationType=API_KEY_QUERY_PARAM))");
}
}
@ -473,4 +484,35 @@ public class APIBackendMetaData extends QBackendMetaData
return (false);
}
/*******************************************************************************
** Getter for apiKeyQueryParamName
*******************************************************************************/
public String getApiKeyQueryParamName()
{
return (this.apiKeyQueryParamName);
}
/*******************************************************************************
** Setter for apiKeyQueryParamName
*******************************************************************************/
public void setApiKeyQueryParamName(String apiKeyQueryParamName)
{
this.apiKeyQueryParamName = apiKeyQueryParamName;
}
/*******************************************************************************
** Fluent setter for apiKeyQueryParamName
*******************************************************************************/
public APIBackendMetaData withApiKeyQueryParamName(String apiKeyQueryParamName)
{
this.apiKeyQueryParamName = apiKeyQueryParamName;
return (this);
}
}

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.module.api.model.metadata;
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
import com.kingsrook.qqq.backend.module.api.BaseTest;
import com.kingsrook.qqq.backend.module.api.model.AuthorizationType;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -39,7 +40,7 @@ class APIBackendMetaDataTest extends BaseTest
**
*******************************************************************************/
@Test
void test()
void testMissingBaseUrl()
{
APIBackendMetaData apiBackendMetaData = new APIBackendMetaData()
.withName("test");
@ -49,4 +50,45 @@ class APIBackendMetaDataTest extends BaseTest
assertThat(qInstanceValidator.getErrors()).anyMatch(e -> e.contains("Missing baseUrl"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAuthorizationApiKeyQueryParam()
{
APIBackendMetaData apiBackendMetaData = new APIBackendMetaData()
.withAuthorizationType(AuthorizationType.API_KEY_QUERY_PARAM)
.withBaseUrl("http://localhost:8000/")
.withName("test");
QInstanceValidator qInstanceValidator = new QInstanceValidator();
apiBackendMetaData.performValidation(qInstanceValidator);
assertEquals(2, qInstanceValidator.getErrors().size());
assertThat(qInstanceValidator.getErrors()).anyMatch(e -> e.contains("Missing apiKey for API backend"));
assertThat(qInstanceValidator.getErrors()).anyMatch(e -> e.contains("Missing apiKeyQueryParamName for API backend"));
apiBackendMetaData = new APIBackendMetaData()
.withAuthorizationType(AuthorizationType.API_KEY_QUERY_PARAM)
.withApiKey("ABC-123")
.withApiKeyQueryParamName("key")
.withBaseUrl("http://localhost:8000/")
.withName("test");
qInstanceValidator = new QInstanceValidator();
apiBackendMetaData.performValidation(qInstanceValidator);
assertEquals(0, qInstanceValidator.getErrors().size());
apiBackendMetaData = new APIBackendMetaData()
.withAuthorizationType(AuthorizationType.API_KEY_HEADER)
.withApiKey("ABC-123")
.withApiKeyQueryParamName("key")
.withBaseUrl("http://localhost:8000/")
.withName("test");
qInstanceValidator = new QInstanceValidator();
apiBackendMetaData.performValidation(qInstanceValidator);
assertEquals(1, qInstanceValidator.getErrors().size());
assertThat(qInstanceValidator.getErrors()).anyMatch(e -> e.contains("Unexpected apiKeyQueryParamName for API backend"));
}
}