Skip to content
Open
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
11 changes: 0 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,6 @@ The content store type is selected with `contentgrid.appserver.content-store.typ
| `contentgrid.appserver.content.s3.connection-pool-size` | Maximum number of idle HTTP connections to keep in the pool. | `0` | No |
| `contentgrid.appserver.content.s3.connection-pool-keep-alive-seconds` | How long idle connections are kept alive (in seconds). | `1` | No |

### Content encryption

When enabled, content is transparently encrypted before being written to the content store.

| Property | Description | Default | Required |
|---|---|---|---|
| `contentgrid.appserver.content.encryption.enabled` | Enables transparent content encryption. | `false` | No |
| `contentgrid.appserver.content.encryption.bootstrap-tables` | Controls DEK table lifecycle on startup. `NONE` does nothing, `CREATE` creates the table, `CREATE_DROP` creates on start and drops on stop. | `NONE` | No |
| `contentgrid.appserver.content.encryption.wrapper.algorithms` | List of key wrapping algorithms to use. Supported value: `NONE` (unencrypted symmetric key). | `NONE` | No |
| `contentgrid.appserver.content.encryption.engine.algorithms` | List of Content encryption algorithms to use. Supported values: `AES128_CTR`, `AES192_CTR`, `AES256_CTR`, `ALFRESCO`. | `AES128_CTR` | No |

### Query engine

| Property | Description | Default | Required |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.contentgrid.appserver.application.model.Application;
import com.contentgrid.appserver.application.model.Constraint;
import com.contentgrid.appserver.application.model.json.model.ApplicationSettings;
import com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionEngineAlgorithm;
import com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionKeyWrapperAlgorithm;
import com.contentgrid.appserver.application.model.Entity.ConfigurableEntityTranslations;
import com.contentgrid.appserver.application.model.Entity.EntityTranslations;
import com.contentgrid.appserver.application.model.attributes.Attribute.AttributeTranslations;
Expand Down Expand Up @@ -51,6 +54,7 @@
import com.contentgrid.appserver.application.model.json.exceptions.UnknownFilterTypeException;
import com.contentgrid.appserver.application.model.json.exceptions.UnknownFlagException;
import com.contentgrid.appserver.application.model.json.model.AllowedValuesConstraint;
import com.contentgrid.appserver.application.model.json.model.ContentEncryptionSettings;
import com.contentgrid.appserver.application.model.json.model.ApplicationSchema;
import com.contentgrid.appserver.application.model.json.model.Attribute;
import com.contentgrid.appserver.application.model.json.model.AttributeConstraint;
Expand Down Expand Up @@ -138,11 +142,19 @@
relations.add(relation);
}
}
List<com.contentgrid.appserver.application.model.settings.ApplicationSettings> applicationSettings = new ArrayList<>();
if (schema.getApplicationSettings() != null) {
for (ApplicationSettings settings : schema.getApplicationSettings()) {
com.contentgrid.appserver.application.model.settings.ApplicationSettings convertedSettings = fromJsonApplicationSettings(settings);
applicationSettings.add(convertedSettings);
}
}
return Application.builder()
.name(ApplicationName.of(
schema.getApplicationName()))
.entities(entities)
.relations(relations)
.applicationSettings(applicationSettings)
.build();
}

Expand All @@ -156,6 +168,29 @@
}
}

private com.contentgrid.appserver.application.model.settings.ApplicationSettings fromJsonApplicationSettings(ApplicationSettings json) {
return switch (json) {
case ContentEncryptionSettings contentEncryptionSettings -> fromJsonContentEncryption(
contentEncryptionSettings);
};
}

private com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionSettings fromJsonContentEncryption(
ContentEncryptionSettings json) {
var builder = com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionSettings.builder();
if (json.getEncryptionEngineAlgorithms() != null) {
json.getEncryptionEngineAlgorithms().stream()
.map(ContentEncryptionEngineAlgorithm::valueOf)
.forEach(builder::encryptionEngineAlgorithm);
}
if (json.getKeyWrapperAlgorithms() != null) {
json.getKeyWrapperAlgorithms().stream()
.map(ContentEncryptionKeyWrapperAlgorithm::valueOf)
.forEach(builder::keyWrapperAlgorithm);
}
return builder.build();
}

private com.contentgrid.appserver.application.model.Entity fromJsonEntity(
Entity jsonEntity) throws InvalidJsonException {
com.contentgrid.appserver.application.model.attributes.SimpleAttribute primaryKey = fromJsonSimpleAttribute(
Expand Down Expand Up @@ -219,7 +254,7 @@
return ATTRIBUTE_TRANSLATIONS.mapInto(jsonAttr, com.contentgrid.appserver.application.model.attributes.SimpleAttribute.builder())
.name(AttributeName.of(jsonAttr.getName()))
.column(ColumnName.of(jsonAttr.getColumnName()))
.type(Type.valueOf(jsonAttr.getDataType().toUpperCase()))

Check warning on line 257 in contentgrid-appserver-application-model-json/src/main/java/com/contentgrid/appserver/application/model/json/DefaultApplicationSchemaConverter.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define the locale to be used in this String operation.

See more on https://sonarcloud.io/project/issues?id=xenit-eu_contentgrid-appserver&issues=AZ7v2NQzxHPsQcQXolwo&open=AZ7v2NQzxHPsQcQXolwo&pullRequest=316
.flags(fromJsonAttributeFlags(jsonAttr.getFlags()))
.constraints(constraints)
.build();
Expand Down Expand Up @@ -461,13 +496,41 @@
var relations = app.getRelations().stream()
.map(this::toJsonRelation)
.toList();
var applicationSettings = app.getApplicationSettings().stream()
.map(this::toJsonApplicationSettings)
.toList();
var schema = new ApplicationSchema();
schema.setApplicationName(app.getName().getValue());
schema.setEntities(entities);
schema.setRelations(relations);
schema.setApplicationSettings(applicationSettings);
return schema;
}

private ApplicationSettings toJsonApplicationSettings(
com.contentgrid.appserver.application.model.settings.ApplicationSettings applicationSettings) {
return switch (applicationSettings) {
case com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionSettings contentEncryptionSettings -> toJsonContentEncryptionSettings(contentEncryptionSettings);
default -> throw new IllegalArgumentException("Unknown application settings: " + applicationSettings.getClass().getName());
};
}

private ContentEncryptionSettings toJsonContentEncryptionSettings(
com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionSettings settings) {
var json = new ContentEncryptionSettings();
if (!settings.getEncryptionEngineAlgorithms().isEmpty()) {
json.setEncryptionEngineAlgorithms(settings.getEncryptionEngineAlgorithms().stream()
.map(ContentEncryptionEngineAlgorithm::name)
.toList());
}
if (!settings.getKeyWrapperAlgorithms().isEmpty()) {
json.setKeyWrapperAlgorithms(settings.getKeyWrapperAlgorithms().stream()
.map(ContentEncryptionKeyWrapperAlgorithm::name)
.toList());
}
return json;
}

private Entity toJsonEntity(com.contentgrid.appserver.application.model.Entity entity) {
var jsonEntity = new Entity();
jsonEntity.setName(entity.getName().getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"$schema", "applicationName", "version", "entities", "relations"})
@JsonPropertyOrder({"$schema", "applicationName", "version", "entities", "relations", "applicationSettings"})
public class ApplicationSchema {
@NonNull
private String applicationName;
private final String version = "1.0.0";
private List<Entity> entities;
private List<Relation> relations;
private List<ApplicationSettings> applicationSettings;

@JsonProperty("$schema")
private final String schema = "https://contentgrid.cloud/schemas/application-schema.json";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.contentgrid.appserver.application.model.json.model;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = ContentEncryptionSettings.class, name = "contentEncryption"),
})
public sealed interface ApplicationSettings permits ContentEncryptionSettings {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.contentgrid.appserver.application.model.json.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public final class ContentEncryptionSettings implements ApplicationSettings {

private List<String> encryptionEngineAlgorithms;
private List<String> keyWrapperAlgorithms;
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,47 @@
}
]
}
},
"applicationSettings": {
"type": "array",
"description": "A list of application settings with application-specific configuration",
"items": {
"anyOf": [
{
"type": "object",
"description": "Content encryption configuration for this application.",
"properties": {
"type": {
"type": "string",
"const": "contentEncryption",
"description": "The type of application settings"
},
"encryptionEngineAlgorithms": {
"type": "array",
"description": "The content encryption engine algorithms to use. The first algorithm will be used for encryption, the additional algorithms will only be used for decryption. Defaults to [ AES128_CTR ].",
"items": {
"type": "string",
"enum": ["AES128_CTR", "AES192_CTR", "AES256_CTR", "ALFRESCO"]
},
"uniqueItems": true
},
"keyWrapperAlgorithms": {
"type": "array",
"description": "The key wrapper algorithms to use. Defaults to [ NONE ].",
"items": {
"type": "string",
"enum": ["NONE"]
},
"uniqueItems": true
}
},
"required": [
"type"
],
"additionalProperties": false
}
]
}
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

import com.contentgrid.appserver.application.model.Application;
import com.contentgrid.appserver.application.model.Constraint;
import com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionSettings;
import com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionEngineAlgorithm;
import com.contentgrid.appserver.application.model.settings.encryption.ContentEncryptionKeyWrapperAlgorithm;
import com.contentgrid.appserver.application.model.Entity;
import com.contentgrid.appserver.application.model.attributes.SimpleAttribute;
import com.contentgrid.appserver.application.model.attributes.SimpleAttribute.Type;
Expand All @@ -33,6 +36,7 @@
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;

class DefaultApplicationSchemaConverterTest {
Expand All @@ -43,6 +47,7 @@ void testConvertSampleApplicationJson() throws Exception {
Application app = new DefaultApplicationSchemaConverter().convert(is);
assertNotNull(app);
assertEquals("HR application", app.getName().getValue());
assertTrue(app.getApplicationSettings().isEmpty());
assertFalse(app.getEntities().isEmpty());
assertFalse(app.getRelations().isEmpty());
// Optionally, add more assertions for entities, attributes, and relations
Expand Down Expand Up @@ -78,6 +83,59 @@ void testInvalidJson() {
});
}

@Test
void testConvertContentEncryptionEnabled() throws Exception {
var json = """
{
"$schema": "https://contentgrid.cloud/schemas/application-schema.json",
"applicationName": "test",
"version": "1.0.0",
"entities": [],
"relations": [],
"applicationSettings": [ {
"type": "contentEncryption",
"encryptionEngineAlgorithms": ["AES256_CTR"],
"keyWrapperAlgorithms": ["NONE"]
} ]
}
""";
var app = new DefaultApplicationSchemaConverter().convert(new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)));
var settings = app.getApplicationSettings(ContentEncryptionSettings.class);
assertTrue(settings.isPresent());
assertEquals(Set.of(ContentEncryptionEngineAlgorithm.AES256_CTR), settings.get().getEncryptionEngineAlgorithms());
assertEquals(Set.of(ContentEncryptionKeyWrapperAlgorithm.NONE), settings.get().getKeyWrapperAlgorithms());
}

@Test
void testSerializeContentEncryptionEnabled() {
var app = Application.builder()
.name(ApplicationName.of("test"))
.applicationSetting(ContentEncryptionSettings.builder()
.encryptionEngineAlgorithm(ContentEncryptionEngineAlgorithm.AES256_CTR)
.keyWrapperAlgorithm(ContentEncryptionKeyWrapperAlgorithm.NONE)
.build())
.build();

var out = new ByteArrayOutputStream();
new DefaultApplicationSchemaConverter().toJson(app, out);
var jsonOutput = out.toString(StandardCharsets.UTF_8);

assertThat(jsonOutput, sameJSONAs("""
{
"$schema": "https://contentgrid.cloud/schemas/application-schema.json",
"applicationName": "test",
"version": "1.0.0",
"entities": [],
"relations": [],
"applicationSettings": [ {
"type": "contentEncryption",
"encryptionEngineAlgorithms": ["AES256_CTR"],
"keyWrapperAlgorithms": ["NONE"]
} ]
}
"""));
}

@Test
void testUnknownFlag() {
var jsonWithUnknownFlag = """
Expand Down Expand Up @@ -218,7 +276,8 @@ void testTargetOneToOneSerialization() {
},
"targetReference":"source_ref"
}
]
],
"applicationSettings": []
}
"""));
}
Expand Down Expand Up @@ -333,7 +392,8 @@ void testManyToOneSerialization() {
},
"sourceReference":"target_ref"
}
]
],
"applicationSettings": []
}
"""));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -759,5 +759,6 @@
},
"targetReference": "secret_information_id"
}
]
],
"applicationSettings": []
}
Loading