Skip to content

Commit

Permalink
transform responses in IdCacheLoader
Browse files Browse the repository at this point in the history
Signed-off-by: Cai Yufei (INST/ECS1) <yufei.cai@bosch-si.com>
  • Loading branch information
yufei-cai committed Mar 12, 2018
1 parent 7fb935e commit 4dda0a6
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 11 deletions.
Expand Up @@ -12,10 +12,12 @@
package org.eclipse.ditto.services.authorization.util.cache;

import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

import org.eclipse.ditto.model.enforcers.Enforcer;
import org.eclipse.ditto.model.policies.ResourceKey;
import org.eclipse.ditto.model.things.AccessControlList;
import org.eclipse.ditto.services.authorization.util.cache.entry.Entry;
import org.eclipse.ditto.services.authorization.util.config.CacheConfigReader;
import org.eclipse.ditto.utils.jsr305.annotations.AllParametersAndReturnValuesAreNonnullByDefault;
Expand All @@ -31,8 +33,8 @@
@AllParametersAndReturnValuesAreNonnullByDefault
public final class AuthorizationCache {

private final AsyncLoadingCache<ResourceKey, Entry<ResourceKey>> idCache;
private final AsyncLoadingCache<ResourceKey, Entry<Enforcer>> enforcerCache;
private final AsyncLoadingCache<ResourceKey, Entry<ResourceKey>> idCache;

/**
* Creates a cache from configuration.
Expand All @@ -42,10 +44,21 @@ public final class AuthorizationCache {
public AuthorizationCache(final CacheConfigReader cacheConfigReader) {
// TODO: compute entity region map in root actor
final Supplier<Map<String, ActorRef>> entityRegionMap = () -> { throw new NotImplementedError(); };
final IdCacheLoader idCacheLoader = new IdCacheLoader(cacheConfigReader.getAskTimeout(), entityRegionMap.get());
final EnforcerCacheLoader enforcerCacheLoader = new EnforcerCacheLoader();
idCache = cacheConfigReader.getIdCacheConfigReader().toCaffeine().buildAsync(idCacheLoader);
enforcerCache = cacheConfigReader.getEnforcerCacheConfigReader().toCaffeine().buildAsync(enforcerCacheLoader);

final IdCacheLoader idCacheLoader =
new IdCacheLoader(cacheConfigReader.getAskTimeout(), entityRegionMap.get(), this);
idCache = cacheConfigReader.getIdCacheConfigReader().toCaffeine().buildAsync(idCacheLoader);

}

void updateAcl(final ResourceKey resourceKey, final long revision, final AccessControlList acl) {
final Enforcer enforcerFromAcl = null; // TODO: implement
final Entry<Enforcer> entry = Entry.of(revision, enforcerFromAcl);
enforcerCache.put(resourceKey, CompletableFuture.completedFuture(entry));

throw new NotImplementedError();
}

// TODO: DO NOT save policy id relation into the ID cache because it is always the identity relation.
Expand Down
Expand Up @@ -18,16 +18,22 @@
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;

import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.policies.ResourceKey;
import org.eclipse.ditto.model.things.AccessControlList;
import org.eclipse.ditto.model.things.Thing;
import org.eclipse.ditto.model.things.ThingRevision;
import org.eclipse.ditto.services.authorization.util.cache.entry.Entry;
import org.eclipse.ditto.services.models.things.commands.sudo.SudoRetrieveThing;
import org.eclipse.ditto.services.models.things.commands.sudo.SudoRetrieveThingResponse;
import org.eclipse.ditto.signals.commands.policies.PolicyCommand;
import org.eclipse.ditto.signals.commands.things.ThingCommand;
import org.eclipse.ditto.signals.commands.things.exceptions.ThingNotAccessibleException;
import org.eclipse.ditto.utils.jsr305.annotations.AllParametersAndReturnValuesAreNonnullByDefault;

import akka.actor.ActorRef;
Expand All @@ -43,8 +49,12 @@ public class IdCacheLoader extends AbstractAskCacheLoader<ResourceKey> {
private final Map<String, ActorRef> entityRegionMap;
private final Map<String, Function<String, Object>> commandMap;
private final Map<String, Function<Object, Entry<ResourceKey>>> transformerMap;
private final AuthorizationCache authorizationCache;

IdCacheLoader(final Duration askTimeout, final Map<String, ActorRef> entityRegionMap,
final AuthorizationCache authorizationCache) {
this.authorizationCache = authorizationCache;

IdCacheLoader(final Duration askTimeout, final Map<String, ActorRef> entityRegionMap) {
this.askTimeout = askTimeout;
this.entityRegionMap = entityRegionMap;
this.commandMap = Collections.unmodifiableMap(buildCommandMap());
Expand All @@ -71,24 +81,64 @@ protected Entry<ResourceKey> transformResponse(final String resourceType, final
return checkNotNull(transformerMap.get(resourceType), resourceType).apply(response);
}

// TODO document as extension point
/**
* Map resource type to the command used to retrieve the ID of the authorization data for the entity.
* Subclasses may override this method to handle additional resource types a la "cake pattern".
*
* @return A mutable map from resource types to authorization retrieval commands.
*/
protected HashMap<String, Function<String, Object>> buildCommandMap() {
final HashMap<String, Function<String, Object>> hashMap = new HashMap<>();
hashMap.put(ThingCommand.RESOURCE_TYPE, IdCacheLoader::sudoRetrieveThing);
return hashMap;
}

// TODO document as extension point
/**
* Map resource type to the transformation applied to responses. Subclasses may override this method to handle
* additional resource types a la "cake pattern".
*
* @return A mutable map containing response transformations.
*/
protected HashMap<String, Function<Object, Entry<ResourceKey>>> buildTransformerMap() {
final HashMap<String, Function<Object, Entry<ResourceKey>>> hashMap = new HashMap<>();
// TODO transform responses.
hashMap.put(ThingCommand.RESOURCE_TYPE, this::handleSudoRetrieveThingResponse);
return hashMap;
}

private static SudoRetrieveThing sudoRetrieveThing(final String thingId) {
return SudoRetrieveThing.of(thingId,
JsonFieldSelector.newInstance(Thing.JsonFields.ID.getPointer(),
Thing.JsonFields.ACL.getPointer(),
Thing.JsonFields.POLICY_ID.getPointer()), DittoHeaders.empty());
final JsonFieldSelector jsonFieldSelector = JsonFieldSelector.newInstance(
Thing.JsonFields.ID.getPointer(),
Thing.JsonFields.REVISION.getPointer(),
Thing.JsonFields.ACL.getPointer(),
Thing.JsonFields.POLICY_ID.getPointer());
return SudoRetrieveThing.withOriginalSchemaVersion(thingId, jsonFieldSelector, DittoHeaders.empty());
}

private Entry<ResourceKey> handleSudoRetrieveThingResponse(final Object response) {
if (response instanceof SudoRetrieveThingResponse) {
final SudoRetrieveThingResponse sudoRetrieveThingResponse = (SudoRetrieveThingResponse) response;
final Thing thing = sudoRetrieveThingResponse.getThing();
final String thingId = thing.getId().orElseThrow(badResponse("no ThingId"));
final long revision = thing.getRevision().map(ThingRevision::toLong)
.orElseThrow(badResponse("no revision"));
if (thing.getAccessControlList().isPresent()) {
final ResourceKey resourceKey = ResourceKey.newInstance(ThingCommand.RESOURCE_TYPE, thingId);
final AccessControlList acl = thing.getAccessControlList().get();
authorizationCache.updateAcl(resourceKey, revision, acl);
return Entry.of(revision, resourceKey);
} else {
final String policyId = thing.getPolicyId().orElseThrow(badResponse("no PolicyId or ACL"));
final ResourceKey resourceKey = ResourceKey.newInstance(PolicyCommand.RESOURCE_TYPE, policyId);
return Entry.of(revision, resourceKey);
}
} else if (response instanceof ThingNotAccessibleException) {
return Entry.nonexistent();
} else {
throw new IllegalStateException("expect SudoRetrieveThingResponse, got: " + response);
}
}

private static Supplier<RuntimeException> badResponse(final String message) {
return () -> new IllegalStateException("Bad SudoRetrieveThingResponse: " + message);
}
}

0 comments on commit 4dda0a6

Please sign in to comment.