Skip to content

Commit

Permalink
feat(java-sdk): add ABAC support to OpenFgaClient
Browse files Browse the repository at this point in the history
  • Loading branch information
booniepepper committed Dec 6, 2023
1 parent ced4d3d commit a5d8eef
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 86 deletions.
8 changes: 8 additions & 0 deletions config/clients/java/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,18 @@
"destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ClientReadResponse.java",
"templateType": "SupportingFiles"
},
"client-ClientRelationshipCondition.java.mustache" : {
"destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ClientRelationshipCondition.java",
"templateType": "SupportingFiles"
},
"client-ClientTupleKey.java.mustache" : {
"destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ClientTupleKey.java",
"templateType": "SupportingFiles"
},
"client-ClientTupleKeyWithCondition.java.mustache" : {
"destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ClientTupleKeyWithCondition.java",
"templateType": "SupportingFiles"
},
"client-ClientWriteAssertionsResponse.java.mustache" : {
"destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ClientWriteAssertionsResponse.java",
"templateType": "SupportingFiles"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
{{>licenseInfo}}
package {{invokerPackage}};

import dev.openfga.sdk.api.model.CheckRequestTupleKey;
import java.util.List;

public class ClientCheckRequest {
private String user;
private String relation;
private String _object;
private List<ClientTupleKey> contextualTuples;
private List<ClientTupleKeyWithCondition> contextualTuples;
public CheckRequestTupleKey asCheckRequestTupleKey() {
return new CheckRequestTupleKey().user(user).relation(relation)._object(_object);
}

public ClientCheckRequest _object(String _object) {
this._object = _object;
Expand Down Expand Up @@ -48,12 +53,12 @@ public class ClientCheckRequest {
return user;
}

public ClientCheckRequest contextualTuples(List<ClientTupleKey> contextualTuples) {
public ClientCheckRequest contextualTuples(List<ClientTupleKeyWithCondition> contextualTuples) {
this.contextualTuples = contextualTuples;
return this;
}

public List<ClientTupleKey> getContextualTuples() {
public List<ClientTupleKeyWithCondition> getContextualTuples() {
return contextualTuples;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class ClientListObjectsRequest {
private String user;
private String relation;
private String type;
private List<ClientTupleKey> contextualTupleKeys;
private List<ClientTupleKeyWithCondition> contextualTupleKeys;
public ClientListObjectsRequest user(String user) {
this.user = user;
Expand Down Expand Up @@ -44,12 +44,12 @@ public class ClientListObjectsRequest {
return type;
}

public ClientListObjectsRequest contextualTupleKeys(List<ClientTupleKey> contextualTupleKeys) {
public ClientListObjectsRequest contextualTupleKeys(List<ClientTupleKeyWithCondition> contextualTupleKeys) {
this.contextualTupleKeys = contextualTupleKeys;
return this;
}

public List<ClientTupleKey> getContextualTupleKeys() {
public List<ClientTupleKeyWithCondition> getContextualTupleKeys() {
return contextualTupleKeys;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{{>licenseInfo}}
package {{invokerPackage}};

import {{modelPackage}}.RelationshipCondition;

public class ClientRelationshipCondition {
private String name;
private Object context;
public ClientRelationshipCondition name(String name) {
this.name = name;
return this;
}

public String getName() {
return name;
}

public ClientRelationshipCondition context(Object context) {
this.context = context;
return this;
}

public Object getContext() {
return context;
}

public RelationshipCondition asRelationshipCondition() {
return new RelationshipCondition().name(name).context(context);
}
}
25 changes: 25 additions & 0 deletions config/clients/java/template/client-ClientTupleKey.java.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ public class ClientTupleKey {
private String relation;
private String _object;
public TupleKeyWithoutCondition asTupleKeyWithoutCondition() {
return new TupleKeyWithoutCondition().user(user).relation(relation)._object(_object);
}

public static WriteRequestDeletes asWriteRequestDeletes(Collection<ClientTupleKey> tupleKeys) {
return new WriteRequestDeletes()
.tupleKeys(tupleKeys.stream()
.map(ClientTupleKey::asTupleKeyWithoutCondition)
.collect(Collectors.toList()));
}

public ClientTupleKey _object(String _object) {
this._object = _object;
return this;
Expand Down Expand Up @@ -44,4 +55,18 @@ public class ClientTupleKey {
public String getUser() {
return user;
}

/**
* Adds a condition to the tuple key.
* @param condition a {@link ClientRelationshipCondition}
* @return a new {@link ClientTupleKeyWithCondition} with this {@link ClientTupleKey}'s
* user, relation, and object, and the passed condition.
*/
public ClientTupleKeyWithCondition condition(ClientRelationshipCondition condition) {
return new ClientTupleKeyWithCondition()
.user(user)
.relation(relation)
._object(_object)
.condition(condition);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{{>licenseInfo}}
package {{invokerPackage}};

import {{modelPackage}}.ContextualTupleKeys;
import {{modelPackage}}.TupleKey;
import {{modelPackage}}.WriteRequestWrites;
import java.util.Collection;
import java.util.stream.Collectors;

public class ClientTupleKeyWithCondition extends ClientTupleKey {
private ClientRelationshipCondition condition;
public ClientTupleKeyWithCondition condition(ClientRelationshipCondition condition) {
this.condition = condition;
return this;
}

public ClientRelationshipCondition getCondition() {
return condition;
}

public TupleKey asTupleKey() {
return new TupleKey()
.user(getUser())
.relation(getRelation())
._object(getObject())
.condition(condition.asRelationshipCondition());
}

public static ContextualTupleKeys asContextualTupleKeys(Collection<ClientTupleKeyWithCondition> tupleKeys) {
return new ContextualTupleKeys()
.tupleKeys(tupleKeys.stream()
.map(ClientTupleKeyWithCondition::asTupleKey)
.collect(Collectors.toList()));
}

public static WriteRequestWrites asWriteRequestWrites(Collection<ClientTupleKeyWithCondition> tupleKeys) {
return new WriteRequestWrites()
.tupleKeys(tupleKeys.stream()
.map(ClientTupleKeyWithCondition::asTupleKey)
.collect(Collectors.toList()));
}

/* Overrides for correct typing */

@Override
public ClientTupleKeyWithCondition user(String user) {
super.user(user);
return this;
}

@Override
public ClientTupleKeyWithCondition relation(String relation) {
super.relation(relation);
return this;
}

@Override
public ClientTupleKeyWithCondition _object(String _object) {
super._object(_object);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ package {{invokerPackage}};
import java.util.List;

public class ClientWriteRequest {
private List<ClientTupleKey> writes;
private List<ClientTupleKeyWithCondition> writes;
private List<ClientTupleKey> deletes;
public static ClientWriteRequest ofWrites(List<ClientTupleKey> writes) {
public static ClientWriteRequest ofWrites(List<ClientTupleKeyWithCondition> writes) {
return new ClientWriteRequest().writes(writes);
}

public ClientWriteRequest writes(List<ClientTupleKey> writes) {
public ClientWriteRequest writes(List<ClientTupleKeyWithCondition> writes) {
this.writes = writes;
return this;
}

public List<ClientTupleKey> getWrites() {
public List<ClientTupleKeyWithCondition> getWrites() {
return writes;
}

Expand Down
57 changes: 10 additions & 47 deletions config/clients/java/template/client-OpenFgaClient.java.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -280,24 +280,12 @@ public class OpenFgaClient {
var writeTuples = request.getWrites();
if (writeTuples != null && !writeTuples.isEmpty()) {
body.writes(new WriteRequestWrites()
.tupleKeys(writeTuples.stream()
.map(writeTuple -> new TupleKey()
.user(writeTuple.getUser())
.relation(writeTuple.getRelation())
._object(writeTuple.getObject()))
.collect(Collectors.toList())));
body.writes(ClientTupleKeyWithCondition.asWriteRequestWrites(writeTuples));
}

var deleteTuples = request.getDeletes();
if (deleteTuples != null && !deleteTuples.isEmpty()) {
body.deletes(new WriteRequestDeletes()
.tupleKeys(deleteTuples.stream()
.map(deleteTuple -> new TupleKeyWithoutCondition()
.user(deleteTuple.getUser())
.relation(deleteTuple.getRelation())
._object(deleteTuple.getObject()))
.collect(Collectors.toList())));
body.deletes(ClientTupleKey.asWriteRequestDeletes(deleteTuples));
}

if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) {
Expand Down Expand Up @@ -357,20 +345,14 @@ public class OpenFgaClient {
*
* @throws FgaInvalidParameterException When the Store ID is null, empty, or whitespace
*/
public CompletableFuture<ClientWriteResponse> writeTuples(List<ClientTupleKey> tupleKeys)
public CompletableFuture<ClientWriteResponse> writeTuples(List<ClientTupleKeyWithCondition> tupleKeys)
throws FgaInvalidParameterException {
configuration.assertValid();
String storeId = configuration.getStoreIdChecked();
var body = new WriteRequest();
body.writes(new WriteRequestWrites()
.tupleKeys(tupleKeys.stream()
.map(writeTuple -> new TupleKey()
.user(writeTuple.getUser())
.relation(writeTuple.getRelation())
._object(writeTuple.getObject()))
.collect(Collectors.toList())));
body.writes(ClientTupleKeyWithCondition.asWriteRequestWrites(tupleKeys));
String authorizationModelId = configuration.getAuthorizationModelId();
if (!isNullOrWhitespace(authorizationModelId)) {
Expand All @@ -392,13 +374,7 @@ public class OpenFgaClient {
var body = new WriteRequest();
body.deletes(new WriteRequestDeletes()
.tupleKeys(tupleKeys.stream()
.map(deleteTuple -> new TupleKeyWithoutCondition()
.user(deleteTuple.getUser())
.relation(deleteTuple.getRelation())
._object(deleteTuple.getObject()))
.collect(Collectors.toList())));
body.deletes(ClientTupleKey.asWriteRequestDeletes(tupleKeys));
String authorizationModelId = configuration.getAuthorizationModelId();
if (!isNullOrWhitespace(authorizationModelId)) {
Expand Down Expand Up @@ -435,20 +411,11 @@ public class OpenFgaClient {
CheckRequest body = new CheckRequest();
if (request != null) {
body.tupleKey(new CheckRequestTupleKey()
.user(request.getUser())
.relation(request.getRelation())
._object(request.getObject()));
body.tupleKey(request.asCheckRequestTupleKey());
var contextualTuples = request.getContextualTuples();
if (contextualTuples != null && !contextualTuples.isEmpty()) {
body.contextualTuples(new ContextualTupleKeys()
.tupleKeys(contextualTuples.stream()
.map(contextualTuple -> new TupleKey()
._object(contextualTuple.getObject())
.relation(contextualTuple.getRelation())
.user(contextualTuple.getUser()))
.collect(Collectors.toList())));
body.contextualTuples(ClientTupleKeyWithCondition.asContextualTupleKeys(contextualTuples));
}
}

Expand Down Expand Up @@ -559,13 +526,9 @@ public class OpenFgaClient {
if (request != null) {
body.user(request.getUser()).relation(request.getRelation()).type(request.getType());
if (request.getContextualTupleKeys() != null) {
body.contextualTuples(new ContextualTupleKeys()
.tupleKeys(request.getContextualTupleKeys().stream()
.map(contextualTuple -> new TupleKey()
._object(contextualTuple.getObject())
.relation(contextualTuple.getRelation())
.user(contextualTuple.getUser()))
.collect(Collectors.toList())));
var contextualTuples = request.getContextualTupleKeys();
var bodyContextualTuples = ClientTupleKeyWithCondition.asContextualTupleKeys(contextualTuples);
body.contextualTuples(bodyContextualTuples);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {{configPackage}}.*;
import dev.openfga.errors.ApiException;
import java.net.http.HttpClient;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Expand All @@ -23,7 +24,8 @@ public class OpenFgaClientIntegrationTest {
private static final String DEFAULT_DOC = "document:2021-budget";
public static final ClientTupleKey DEFAULT_TUPLE_KEY =
new ClientTupleKey().user(DEFAULT_USER).relation("reader")._object(DEFAULT_DOC);
public static final List<ClientTupleKey> DEFAULT_TUPLE_KEYS = List.of(DEFAULT_TUPLE_KEY);
public static final ClientRelationshipCondition DEFAULT_CONDITION =
new ClientRelationshipCondition().name("condition").context(Map.of("some", "context"));
public static final ClientAssertion DEFAULT_ASSERTION =
new ClientAssertion().user(DEFAULT_USER).relation("reader")._object(DEFAULT_DOC).expectation(true);
Expand Down Expand Up @@ -186,8 +188,10 @@ public class OpenFgaClientIntegrationTest {
String authModelId = writeAuthModel(storeId);
fga.setAuthorizationModelId(authModelId);
ClientWriteRequest writeRequest = new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY));
ClientReadRequest readRequest = new ClientReadRequest().user(DEFAULT_USER)._object(DEFAULT_DOC);
ClientWriteRequest writeRequest =
new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY.condition(DEFAULT_CONDITION)));
ClientReadRequest readRequest =
new ClientReadRequest().user(DEFAULT_USER)._object(DEFAULT_DOC);
// When
fga.write(writeRequest).get();
Expand Down Expand Up @@ -231,7 +235,8 @@ public class OpenFgaClientIntegrationTest {
fga.setStoreId(storeId);
String authModelId = writeAuthModel(storeId);
fga.setAuthorizationModelId(authModelId);
ClientWriteRequest writeRequest = new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY));
ClientWriteRequest writeRequest =
new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY.condition(DEFAULT_CONDITION)));
ClientExpandRequest expandRequest =
new ClientExpandRequest()._object(DEFAULT_DOC).relation("reader");
Expand Down Expand Up @@ -261,7 +266,8 @@ public class OpenFgaClientIntegrationTest {
fga.setStoreId(storeId);
String authModelId = writeAuthModel(storeId);
fga.setAuthorizationModelId(authModelId);
ClientWriteRequest writeRequest = new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY));
ClientWriteRequest writeRequest =
new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY.condition(DEFAULT_CONDITION)));
ClientListObjectsRequest listObjectsRequest =
new ClientListObjectsRequest().user(DEFAULT_USER).relation("reader").type("document");
Expand Down
Loading

0 comments on commit a5d8eef

Please sign in to comment.