Skip to content

Commit

Permalink
moved added "default-namespace" functionality to EntityCreationConfig
Browse files Browse the repository at this point in the history
* as a part of resolving a conflict in concerige which has been removed
* had to move the default namespace appliance to the model classes CreateThing and CreatePolicy - doing that in the persistence actors is too late
* solved by using a system property added to DittoSystemProperties
* added new POST /policies endpoint utilizing "CreatePolicy"
* added new PolicyNotCreatableException
* harmonized creation methods in PolicyId and ThingId

Signed-off-by: Thomas Jaeckle <thomas.jaeckle@bosch.io>
  • Loading branch information
thjaeckle committed May 9, 2022
1 parent 89d6841 commit 57040d1
Show file tree
Hide file tree
Showing 22 changed files with 283 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public final class DittoSystemProperties {
*/
public static final String DITTO_LIMITS_MESSAGES_MAX_SIZE_BYTES = "ditto.limits.messages.max-size";

/**
* System property name of the property defining the default namespace to use when creating new entities.
*/
public static final String DITTO_ENTITY_CREATION_DEFAULT_NAMESPACE = "ditto.entity-creation.default-namespace";

private DittoSystemProperties() {
throw new AssertionError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,13 @@
*/
package org.eclipse.ditto.connectivity.service;

import org.eclipse.ditto.base.api.common.Shutdown;
import org.eclipse.ditto.base.api.common.purge.PurgeEntities;
import org.eclipse.ditto.base.api.devops.signals.commands.ExecutePiggybackCommand;
import org.eclipse.ditto.base.api.persistence.cleanup.CleanupPersistence;
import org.eclipse.ditto.base.model.namespaces.signals.commands.PurgeNamespace;
import org.eclipse.ditto.connectivity.api.messaging.monitoring.logs.AddConnectionLogEntry;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.OpenConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnection;
import org.eclipse.ditto.base.api.common.Shutdown;
import org.eclipse.ditto.base.api.common.purge.PurgeEntities;
import org.eclipse.ditto.base.api.devops.signals.commands.ExecutePiggybackCommand;
import org.eclipse.ditto.base.api.persistence.cleanup.CleanupPersistence;
import org.eclipse.ditto.base.model.namespaces.signals.commands.PurgeNamespace;
import org.eclipse.ditto.base.service.cluster.ModifySplitBrainResolver;
import org.eclipse.ditto.connectivity.api.messaging.monitoring.logs.AddConnectionLogEntry;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.OpenConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnection;
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
Expand Down
6 changes: 6 additions & 0 deletions edge/api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@
<artifactId>ditto-internal-utils-cluster</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.ditto</groupId>
<artifactId>ditto-utils-jsr305</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.eclipse.ditto.policies.model.signals.commands.actions.TopLevelPolicyActionCommand;
import org.eclipse.ditto.policies.model.signals.commands.exceptions.PolicyActionFailedException;
import org.eclipse.ditto.policies.model.signals.commands.exceptions.PolicyIdNotExplicitlySettableException;
import org.eclipse.ditto.policies.model.signals.commands.modify.CreatePolicy;
import org.eclipse.ditto.policies.model.signals.commands.modify.DeletePolicy;
import org.eclipse.ditto.policies.model.signals.commands.modify.ModifyPolicy;
import org.eclipse.ditto.policies.model.signals.commands.query.RetrievePolicy;
Expand Down Expand Up @@ -96,13 +97,37 @@ public PoliciesRoute(final RouteBaseProperties routeBaseProperties,
public Route buildPoliciesRoute(final RequestContext ctx, final DittoHeaders dittoHeaders,
final AuthenticationResult authenticationResult) {
return rawPathPrefix(PathMatchers.slash().concat(PATH_POLICIES), () ->
rawPathPrefix(PathMatchers.slash().concat(PathMatchers.segment()), policyId ->
// /policies/<policyId>
policyRoute(ctx, dittoHeaders, PolicyId.of(policyId), authenticationResult)
concat(
policies(ctx, dittoHeaders),
rawPathPrefix(PathMatchers.slash().concat(PathMatchers.segment()), policyId ->
// /policies/<policyId>
policyRoute(ctx, dittoHeaders, PolicyId.of(policyId), authenticationResult)
)
)
);
}

/*
* Describes {@code /policies} route.
*
* @return {@code /policies} route.
*/
private Route policies(final RequestContext ctx, final DittoHeaders dittoHeaders) {
return pathEndOrSingleSlash(() ->
// POST /policies
post(() -> buildPostPoliciesRoute(ctx, dittoHeaders))
);
}

private Route buildPostPoliciesRoute(final RequestContext ctx, final DittoHeaders dittoHeaders) {
return ensureMediaTypeJsonWithFallbacksThenExtractDataBytes(ctx, dittoHeaders,
payloadSource ->
handlePerRequest(ctx, dittoHeaders, payloadSource, policyJson ->
CreatePolicy.of(createPolicyForPost(policyJson), dittoHeaders)
)
);
}

private Route policyRoute(final RequestContext ctx, final DittoHeaders dittoHeaders, final PolicyId policyId,
final AuthenticationResult authenticationResult) {
return concat(
Expand Down Expand Up @@ -143,6 +168,18 @@ private Route policyId(final RequestContext ctx, final DittoHeaders dittoHeaders
);
}

private static Policy createPolicyForPost(final String jsonString) {
final JsonObject policyJsonObject = wrapJsonRuntimeException(() -> JsonFactory.newObject(jsonString));
if (policyJsonObject.contains(Policy.JsonFields.ID.getPointer())) {
throw PolicyIdNotExplicitlySettableException.forPostMethod().build();
}

return PoliciesModelFactory.newPolicy(policyJsonObject)
.toBuilder()
.setId(PolicyId.generateRandom())
.build();
}

private static JsonObject createPolicyJsonObjectForPut(final String jsonString, final PolicyId policyId) {
final JsonObject policyJsonObject = wrapJsonRuntimeException(() -> JsonFactory.newObject(jsonString));
policyJsonObject.getValue(Policy.JsonFields.ID.getPointer())
Expand Down Expand Up @@ -246,8 +283,8 @@ static Route extractJwt(final DittoHeaders dittoHeaders,
final String actionName,
final Function<JsonWebToken, Route> inner) {

if (authResult instanceof JwtAuthenticationResult) {
final Optional<JsonWebToken> jwtOptional = ((JwtAuthenticationResult) authResult).getJwt();
if (authResult instanceof JwtAuthenticationResult jwtAuthenticationResult) {
final Optional<JsonWebToken> jwtOptional = jwtAuthenticationResult.getJwt();
if (jwtOptional.isPresent()) {
return inner.apply(jwtOptional.get());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.eclipse.ditto.internal.utils.metrics.instruments.gauge.Gauge;
import org.eclipse.ditto.internal.utils.pubsub.DittoProtocolSub;
import org.eclipse.ditto.internal.utils.search.SubscriptionManager;
import org.eclipse.ditto.protocol.HeaderTranslator;

import com.typesafe.config.Config;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@
import org.eclipse.ditto.base.api.devops.signals.commands.ExecutePiggybackCommand;
import org.eclipse.ditto.base.api.persistence.cleanup.CleanupPersistence;
import org.eclipse.ditto.base.model.namespaces.signals.commands.PurgeNamespace;
import org.eclipse.ditto.connectivity.api.messaging.monitoring.logs.AddConnectionLogEntry;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.OpenConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnection;
import org.eclipse.ditto.base.model.namespaces.signals.commands.PurgeNamespace;
import org.eclipse.ditto.base.service.cluster.ModifySplitBrainResolver;
import org.eclipse.ditto.connectivity.api.messaging.monitoring.logs.AddConnectionLogEntry;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.OpenConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnection;
import org.eclipse.ditto.gateway.service.endpoints.routes.whoami.Whoami;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# restrict entity creation
ditto.entity-creation {
# specifies the default namespace to use when e.g. creating things/policies via POST (without specifying a namespace)
default-namespace = "org.eclipse.ditto"
default-namespace = ${?DITTO_DEFAULT_NAMESPACE}

# this default entry allows every authenticated "auth-subject" to create any "resource-type" in any "namespace":
grant = [{}]
# same as "grant", but rejecting requests which already passed "grant"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ private static List<Pattern> compile(final List<String> patterns) {
}

/**
* Returns an instance of {@code DefaultCreationRestrictionConfig} based on the settings of the specified Config.
* Returns an instance of {@code CreationRestrictionConfig} based on the settings of the specified Config.
*
* @param config is supposed to provide the settings of the restriction config.
* @return the instance.
* @throws org.eclipse.ditto.internal.utils.config.DittoConfigError if {@code config} is invalid.
*/
public static DefaultCreationRestrictionConfig of(final Config config) {
public static CreationRestrictionConfig of(final Config config) {
return new DefaultCreationRestrictionConfig(ConfigWithFallback.newInstance(config,
CreationRestrictionConfig.CreationRestrictionConfigValues.values()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.annotation.concurrent.Immutable;

Expand All @@ -31,16 +30,18 @@ public class DefaultEntityCreationConfig implements EntityCreationConfig {

private static final String CONFIG_PATH = "entity-creation";

private final String defaultNamespace;
private final List<CreationRestrictionConfig> grant;
private final List<CreationRestrictionConfig> revoke;

private DefaultEntityCreationConfig(final ScopedConfig config) {
defaultNamespace = config.getString(ConfigValue.DEFAULT_NAMESPACE.getConfigPath());
grant = config.getConfigList(ConfigValue.GRANT.getConfigPath()).stream()
.map(DefaultCreationRestrictionConfig::of)
.collect(Collectors.toUnmodifiableList());
.toList();
revoke = config.getConfigList(ConfigValue.REVOKE.getConfigPath()).stream()
.map(DefaultCreationRestrictionConfig::of)
.collect(Collectors.toUnmodifiableList());
.toList();
}

/**
Expand All @@ -55,6 +56,11 @@ public static DefaultEntityCreationConfig of(final Config config) {
ConfigWithFallback.newInstance(config, CONFIG_PATH, EntityCreationConfig.ConfigValue.values()));
}

@Override
public String getDefaultNamespace() {
return defaultNamespace;
}

@Override
public List<CreationRestrictionConfig> getGrant() {
return grant;
Expand All @@ -66,27 +72,28 @@ public List<CreationRestrictionConfig> getRevoke() {
}

@Override
public boolean equals(Object o) {
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DefaultEntityCreationConfig that = (DefaultEntityCreationConfig) o;
return grant.equals(that.grant) && revoke.equals(that.revoke);
final DefaultEntityCreationConfig that = (DefaultEntityCreationConfig) o;
return defaultNamespace.equals(that.defaultNamespace) && grant.equals(that.grant) && revoke.equals(that.revoke);
}

@Override
public int hashCode() {
return Objects.hash(grant, revoke);
return Objects.hash(defaultNamespace, grant, revoke);
}

@Override
public String toString() {
return getClass().getSimpleName() + " [" +
"grant=" + grant +
"defaultNamespace=" + defaultNamespace +
", grant=" + grant +
", revoke=" + revoke +
']';
"]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
@Immutable
public interface EntityCreationConfig {

/**
* Returns the default namespace which is used for creating entities, when no namespace is provided.
*
* @return the default namespace.
*/
String getDefaultNamespace();

/**
* Returns the list of creation config entries which would allow the creation.
*
Expand All @@ -48,10 +55,17 @@ public interface EntityCreationConfig {
* {@code EntityCreationConfig}.
*/
enum ConfigValue implements KnownConfigValue {

/**
* The default namespace to use for creating entities without specified namespace.
*/
DEFAULT_NAMESPACE("default-namespace", "org.eclipse.ditto"),

/**
* The list of creation config entries which would allow the creation.
*/
GRANT("grant", List.of(Map.of())),

/**
* The list of creation config entries which would reject the creation.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.base.model.common.DittoSystemProperties;
import org.eclipse.ditto.base.model.entity.id.AbstractNamespacedEntityId;
import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
import org.eclipse.ditto.base.model.entity.id.NamespacedEntityIdInvalidException;
Expand All @@ -29,6 +30,12 @@
@TypedEntityId(type = "policy")
public final class PolicyId extends AbstractNamespacedEntityId {

private static final String DEFAULT_NAMESPACE;

static {
DEFAULT_NAMESPACE = System.getProperty(DittoSystemProperties.DITTO_ENTITY_CREATION_DEFAULT_NAMESPACE, "");
}

private PolicyId(final String namespace, final String policyName, final boolean shouldValidate) {
super(PolicyConstants.ENTITY_TYPE, namespace, policyName, shouldValidate);
}
Expand Down Expand Up @@ -74,11 +81,34 @@ public static PolicyId of(final String namespace, final String policyName) {
*
* @param namespace the namespace of the policy.
* @return The generated unique policy ID.
* @throws PolicyIdInvalidException if for the given {@code namespace} a PolicyId cannot be derived.
*/
public static PolicyId inNamespaceWithRandomName(final String namespace) {
return of(namespace, UUID.randomUUID().toString());
}

/**
* Returns an instance of this class with default namespace placeholder.
*
* @param name the name of the policy.
* @return the created ID.
* @throws PolicyIdInvalidException if for the given {@code name} a PolicyId cannot be derived.
* @since 3.0.0
*/
public static PolicyId inDefaultNamespace(final String name) {
return wrapInPolicyIdInvalidException(() -> new PolicyId(DEFAULT_NAMESPACE, name, true));
}

/**
* Generates a new policy ID with the default namespace placeholder and a unique name.
*
* @return the generated policy ID.
* @since 3.0.0
*/
public static PolicyId generateRandom() {
return wrapInPolicyIdInvalidException(() -> new PolicyId(DEFAULT_NAMESPACE, UUID.randomUUID().toString(), true));
}

private static <T> T wrapInPolicyIdInvalidException(final Supplier<T> supplier) {
try {
return supplier.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private static PolicyCommandToModifyExceptionRegistry createInstance() {
.dittoHeaders(command.getDittoHeaders())
.build());
mappingStrategies.put(CreatePolicy.TYPE,
command -> PolicyNotAccessibleException.newBuilder(command.getEntityId())
command -> PolicyNotCreatableException.newBuilder(command.getEntityId())
.dittoHeaders(command.getDittoHeaders())
.build());
mappingStrategies.put(ModifyPolicy.TYPE,
Expand Down
Loading

0 comments on commit 57040d1

Please sign in to comment.