Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EDGPATRON-130. Enhance HTTP Endpoint Security with TLS and FIPS-140-2… #111

Merged
merged 14 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ buildMvn {
doDocker = {
buildJavaDocker {
publishMaster = 'yes'
healthChk = 'yes'
healthChkCmd = 'wget --no-verbose --tries=1 --spider http://localhost:8081/admin/health || exit 1'
//healthChk for /admin/health in MainVerticleTest.java
}
}
}
65 changes: 46 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,61 @@ Institutional users should be granted the following permissions in order to use

Configuration information is specified in two forms:
1. System Properties - General configuration
1. Properties File - Configuration specific to the desired secure store
2. Properties File - Configuration specific to the desired secure store

### System Properties

Property | Default | Description
----------------------------- | ----------- | -------------
`port` | `8081` | Server port to listen on
`okapi_url` | *required* | Where to find Okapi (URL)
`secure_store` | `Ephemeral` | Type of secure store to use. Valid: `Ephemeral`, `AwsSsm`, `Vault`
`secure_store_props` | `NA` | Path to a properties file specifying secure store configuration
`token_cache_ttl_ms` | `3600000` | How long to cache JWTs, in milliseconds (ms)
`null_token_cache_ttl_ms` | `30000` | How long to cache login failures (null JWTs), in milliseconds (ms)
`token_cache_capacity` | `100` | Max token cache size
`patron_id_cache_ttl_ms` | `3600000` | How long to cache patron ID mappings in milliseconds (ms)
`null_patron_id_cache_ttl_ms` | `30000` | How long to cache patron lookup failures in milliseconds (ms)
`patron_id_cache_capacity` | `1000` | Max token cache size
`log_level` | `INFO` | Log4j Log Level
`request_timeout_ms` | `30000` | Request Timeout
| Property | Default | Description |
|-------------------------------|---------------------|----------------------------------------------------------------------------|
| `port` | `8081` | Server port to listen on |
| `okapi_url` | *required* | Where to find Okapi (URL) |
| `secure_store` | `Ephemeral` | Type of secure store to use. Valid: `Ephemeral`, `AwsSsm`, `Vault` |
| `secure_store_props` | `NA` | Path to a properties file specifying secure store configuration |
| `token_cache_ttl_ms` | `3600000` | How long to cache JWTs, in milliseconds (ms) |
| `null_token_cache_ttl_ms` | `30000` | How long to cache login failures (null JWTs), in milliseconds (ms) |
| `token_cache_capacity` | `100` | Max token cache size |
| `patron_id_cache_ttl_ms` | `3600000` | How long to cache patron ID mappings in milliseconds (ms) |
| `null_patron_id_cache_ttl_ms` | `30000` | How long to cache patron lookup failures in milliseconds (ms) |
| `patron_id_cache_capacity` | `1000` | Max token cache size |
| `log_level` | `INFO` | Log4j Log Level |
| `request_timeout_ms` | `30000` | Request Timeout |

### Env variables for TLS configuration for Http server

To configure Transport Layer Security (TLS) for the HTTP server in an edge module, the following configuration parameters should be used.
Parameters marked as Required are required only in case when TLS for the server should be enabled.

| Property | Default | Description |
|-----------------------------------------------------|------------------|---------------------------------------------------------------------------------------------|
| `SPRING_SSL_BUNDLE_JKS_WEBSERVER_KEYSTORE_TYPE` | `NA` | (Required). Set the type of the keystore. Common types include `JKS`, `PKCS12`, and `BCFKS` |
| `SPRING_SSL_BUNDLE_JKS_WEBSERVER_KEYSTORE_LOCATION` | `NA` | (Required). Set the location of the keystore file in the local file system |
| `SPRING_SSL_BUNDLE_JKS_WEBSERVER_KEYSTORE_PASSWORD` | `NA` | (Required). Set the password for the keystore |
| `SPRING_SSL_BUNDLE_JKS_WEBSERVER_KEY_ALIAS` | `NA` | Set the alias of the key within the keystore. |
| `SPRING_SSL_BUNDLE_JKS_WEBSERVER_KEY_PASSWORD` | `NA` | Optional param that points to a password of `KEY_ALIAS` if it protected |

### Env variables for TLS configuration for Web Client

To configure Transport Layer Security (TLS) for Web clients in the edge module, you can use the following configuration parameters.
Truststore parameters for configuring Web clients are optional even when `FOLIO_CLIENT_TLS_ENABLED = true`.
If truststore parameters need to be populated, `FOLIO_CLIENT_TLS_TRUSTSTORETYPE`, `FOLIO_CLIENT_TLS_TRUSTSTOREPATH` and `FOLIO_CLIENT_TLS_TRUSTSTOREPASSWORD` are required.

| Property | Default | Description |
|-----------------------------------------|-------------------|----------------------------------------------------------------------------------|
| `FOLIO_CLIENT_TLS_ENABLED` | `false` | Set whether SSL/TLS is enabled for Vertx Http Server |
| `FOLIO_CLIENT_TLS_TRUSTSTORETYPE` | `NA` | Set the type of the keystore. Common types include `JKS`, `PKCS12`, and `BCFKS` |
| `FOLIO_CLIENT_TLS_TRUSTSTOREPATH` | `NA` | Set the location of the keystore file in the local file system |
| `FOLIO_CLIENT_TLS_TRUSTSTOREPASSWORD` | `NA` | Set the password for the keystore |


## Patron Mapping

In order to map external patron IDs to those used within FOLIO, the `externalSystemId` field in the user metadata is used. The mapping flow works like this:

1. A request arrives containing an external system's patron ID
1. The patron ID cache is consulted. If a mapping has been cached, skip to #5.
1. A request is made to mod-users, querying for the user having the provided `externalSystemId`
1. The external ID -> internal/FOLIO ID mapping is cached for a configurable amount of time.
1. The internal/FOLIO ID is used when calling mod-patron
2. The patron ID cache is consulted. If a mapping has been cached, skip to #5.
3. A request is made to mod-users, querying for the user having the provided `externalSystemId`
4. The external ID -> internal/FOLIO ID mapping is cached for a configurable amount of time.
5. The internal/FOLIO ID is used when calling mod-patron

## Additional information

Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

<!-- the main class -->
<exec.mainClass>org.folio.edge.patron.MainVerticle</exec.mainClass>
<vertx.version>4.5.5</vertx.version>
<vertx.version>4.5.7</vertx.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -100,7 +100,7 @@
<dependency>
<groupId>org.folio</groupId>
<artifactId>edge-common</artifactId>
<version>4.5.1</version>
<version>4.7.0</version>
julianladisch marked this conversation as resolved.
Show resolved Hide resolved
</dependency>

<!-- Only needed for AwsParamStore -->
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/folio/edge/patron/MainVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.edge.core.EdgeVerticleHttp;
import org.folio.edge.core.utils.OkapiClientFactory;
import org.folio.edge.core.utils.OkapiClientFactoryInitializer;
import org.folio.edge.patron.cache.PatronIdCache;
import org.folio.edge.patron.utils.PatronOkapiClientFactory;

import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
Expand Down Expand Up @@ -44,8 +45,7 @@ public MainVerticle() {

@Override
public Router defineRoutes() {
PatronOkapiClientFactory ocf = new PatronOkapiClientFactory(vertx, config().getString(org.folio.edge.core.Constants.SYS_OKAPI_URL),
config().getInteger(org.folio.edge.core.Constants.SYS_REQUEST_TIMEOUT_MS));
OkapiClientFactory ocf = OkapiClientFactoryInitializer.createInstance(vertx, config());
PatronHandler patronHandler = new PatronHandler(secureStore, ocf);

Router router = Router.router(vertx);
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/folio/edge/patron/PatronHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,20 @@
import org.folio.edge.core.Handler;
import org.folio.edge.core.security.SecureStore;
import org.folio.edge.core.utils.OkapiClient;
import org.folio.edge.core.utils.OkapiClientFactory;
import org.folio.edge.patron.model.error.Error;
import org.folio.edge.patron.model.error.ErrorMessage;
import org.folio.edge.patron.model.error.Errors;
import org.folio.edge.patron.utils.PatronIdHelper;
import org.folio.edge.patron.utils.PatronOkapiClient;
import org.folio.edge.patron.utils.PatronOkapiClientFactory;

public class PatronHandler extends Handler {

public static final String WRONG_INTEGER_PARAM_MESSAGE = "'%s' parameter is incorrect."
+ " parameter value {%s} is not valid: must be an integer, greater than or equal to 0";
private static final Logger logger = LogManager.getLogger(Handler.class);

public PatronHandler(SecureStore secureStore, PatronOkapiClientFactory ocf) {
public PatronHandler(SecureStore secureStore, OkapiClientFactory ocf) {
super(secureStore, ocf);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import org.folio.edge.patron.model.Hold;
import org.joda.time.DateTime;
Expand All @@ -26,10 +25,6 @@ public PatronOkapiClient(OkapiClient client, String alternateTenantId) {
super(client, alternateTenantId);
}

protected PatronOkapiClient(Vertx vertx, String okapiURL, String tenantId, String alternateTenantId, int timeout) {
super(vertx, okapiURL, tenantId, alternateTenantId, timeout);
}

private void getPatron(String extPatronId, Handler<HttpResponse<Buffer>> responseHandler,
Handler<Throwable> exceptionHandler) {
get(
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.edge.core.utils.OkapiClientFactory;
import org.folio.edge.core.utils.test.TestUtils;
import org.folio.edge.patron.model.Account;
import org.folio.edge.patron.model.Hold;
Expand Down Expand Up @@ -53,8 +54,8 @@ public void setUp(TestContext context) throws Exception {
mockOkapi.start()
.onComplete(context.asyncAssertSuccess());

client = new PatronOkapiClientFactory(Vertx.vertx(), "http://localhost:" + okapiPort, reqTimeout)
.getPatronOkapiClient(tenant);
client = new PatronOkapiClient(new OkapiClientFactory(Vertx.vertx(),
"http://localhost:" + okapiPort, reqTimeout).getOkapiClient(tenant), "alternateTenantId");
}

@After
Expand Down