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

feat(java-sdk): ABAC (attribute based access control) support #239

Merged
merged 7 commits into from
Dec 8, 2023
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Main config
OPENFGA_DOCKER_TAG = latest
OPENFGA_DOCKER_TAG = v1.4.0-rc1
OPEN_API_URL = https://raw.githubusercontent.com/openfga/api/main/docs/openapiv2/apidocs.swagger.json
OPENAPI_GENERATOR_CLI_DOCKER_TAG = v6.4.0
NODE_DOCKER_TAG = 18-alpine
Expand Down
12 changes: 12 additions & 0 deletions config/clients/java/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
"allowUnicodeIdentifiers": true,
"caseInsensitiveResponseHeaders": true,
"files": {
"auth-model.json" : {
"destinationFilename": "src/test-integration/resources/auth-model.json",
"templateType": "SupportingFiles"
},
"build.gradle.mustache" : {
"destinationFilename": "build.gradle",
"templateType": "SupportingFiles"
Expand Down Expand Up @@ -123,10 +127,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-ClientTupleKeyWithoutCondition.java.mustache" : {
"destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ClientTupleKeyWithoutCondition.java",
"templateType": "SupportingFiles"
},
"client-ClientWriteAssertionsResponse.java.mustache" : {
"destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ClientWriteAssertionsResponse.java",
"templateType": "SupportingFiles"
Expand Down
18 changes: 18 additions & 0 deletions config/clients/java/template/.github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 2
updates:
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "monthly"
groups:
dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
dependencies:
patterns:
- "*"
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,40 @@ import static org.junit.jupiter.api.Assertions.*;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import {{invokerPackage}}.*;
import {{modelPackage}}.*;
import {{configPackage}}.*;
import dev.openfga.errors.ApiException;
import {{modelPackage}}.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.net.http.HttpClient;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;

@TestInstance(Lifecycle.PER_CLASS)
public class OpenFgaApiIntegrationTest {
private static final ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
private static final String DEFAULT_AUTH_MODEL =
"{\"schema_version\":\"1.1\",\"type_definitions\":[{\"type\":\"user\"},{\"type\":\"document\",\"relations\":{\"reader\":{\"this\":{}},\"writer\":{\"this\":{}},\"owner\":{\"this\":{}}},\"metadata\":{\"relations\":{\"reader\":{\"directly_related_user_types\":[{\"type\":\"user\"}]},\"writer\":{\"directly_related_user_types\":[{\"type\":\"user\"}]},\"owner\":{\"directly_related_user_types\":[{\"type\":\"user\"}]}}}}]}";
private static final String DEFAULT_USER = "user:81684243-9356-4421-8fbf-a4f8d36aa31b";
private static final String DEFAULT_DOC = "document:2021-budget";
public static final TupleKey DEFAULT_TUPLE_KEY =
private static final TupleKey DEFAULT_TUPLE_KEY =
new TupleKey().user(DEFAULT_USER).relation("reader")._object(DEFAULT_DOC);
public static final List<TupleKey> DEFAULT_TUPLE_KEYS = List.of(DEFAULT_TUPLE_KEY);
private static final List<TupleKey> DEFAULT_TUPLE_KEYS = List.of(DEFAULT_TUPLE_KEY);
private String authModelJson;

private OpenFgaApi api;

@BeforeAll
public void loadAuthModelJson() throws IOException {
authModelJson = Files.readString(Paths.get("src", "test-integration", "resources", "auth-model.json"));
}

@BeforeEach
public void initializeApi() throws Exception {
System.setProperty("HttpRequestAttempt.debug-logging", "enable");

Configuration apiConfig = new Configuration().apiUrl("http://localhost:8080");
api = new OpenFgaApi(apiConfig);
}
Expand Down Expand Up @@ -112,7 +123,7 @@ public class OpenFgaApiIntegrationTest {
assertEquals(authModelId, authModel.getId());
String typeDefsJson = mapper.writeValueAsString(authModel.getTypeDefinitions());
assertEquals(
"[{\"type\":\"user\",\"relations\":{},\"metadata\":null},{\"type\":\"document\",\"relations\":{\"owner\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"reader\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"writer\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null}},\"metadata\":{\"relations\":{\"owner\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null}]},\"reader\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null}]},\"writer\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null}]}}}}]",
"[{\"type\":\"user\",\"relations\":{},\"metadata\":null},{\"type\":\"document\",\"relations\":{\"owner\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"reader\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"writer\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null}},\"metadata\":{\"relations\":{\"conditional_reader\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"name_starts_with_a\"}]},\"owner\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"\"}]},\"reader\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"\"}]},\"writer\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"\"}]}}}}]",
typeDefsJson);
}

Expand All @@ -136,7 +147,7 @@ public class OpenFgaApiIntegrationTest {
String typeDefsJson = mapper.writeValueAsString(authModel.getTypeDefinitions());

assertEquals(
"[{\"type\":\"user\",\"relations\":{},\"metadata\":null},{\"type\":\"document\",\"relations\":{\"owner\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"reader\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"writer\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null}},\"metadata\":{\"relations\":{\"owner\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null}]},\"reader\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null}]},\"writer\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null}]}}}}]",
"[{\"type\":\"user\",\"relations\":{},\"metadata\":null},{\"type\":\"document\",\"relations\":{\"owner\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"reader\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null},\"writer\":{\"this\":{},\"computedUserset\":null,\"tupleToUserset\":null,\"union\":null,\"intersection\":null,\"difference\":null}},\"metadata\":{\"relations\":{\"conditional_reader\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"name_starts_with_a\"}]},\"owner\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"\"}]},\"reader\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"\"}]},\"writer\":{\"directly_related_user_types\":[{\"type\":\"user\",\"relation\":null,\"wildcard\":null,\"condition\":\"\"}]}}}}]",
typeDefsJson);
} catch (JsonProcessingException ex) {
assertNull(ex);
Expand All @@ -149,8 +160,7 @@ public class OpenFgaApiIntegrationTest {
// Given
String storeName = thisTestName();
String storeId = createStore(storeName);
WriteAuthorizationModelRequest request =
mapper.readValue(DEFAULT_AUTH_MODEL, WriteAuthorizationModelRequest.class);
WriteAuthorizationModelRequest request = mapper.readValue(authModelJson, WriteAuthorizationModelRequest.class);

// When
WriteAuthorizationModelResponse response =
Expand All @@ -168,9 +178,9 @@ public class OpenFgaApiIntegrationTest {
String storeName = thisTestName();
String storeId = createStore(storeName);
String _authModelId = writeAuthModel(storeId);
WriteRequest writeRequest = new WriteRequest().writes(new TupleKeys().tupleKeys(List.of(DEFAULT_TUPLE_KEY)));
WriteRequest writeRequest = new WriteRequest().writes(new WriteRequestWrites().tupleKeys(List.of(DEFAULT_TUPLE_KEY)));
ReadRequest readRequest =
new ReadRequest().tupleKey(new TupleKey().user(DEFAULT_USER)._object(DEFAULT_DOC));
new ReadRequest().tupleKey(new ReadRequestTupleKey().user(DEFAULT_USER)._object(DEFAULT_DOC));

// When
api.write(storeId, writeRequest).get();
Expand All @@ -189,9 +199,9 @@ public class OpenFgaApiIntegrationTest {
String storeName = thisTestName();
String storeId = createStore(storeName);
String _authModelId = writeAuthModel(storeId);
WriteRequest writeRequest = new WriteRequest().writes(new TupleKeys().tupleKeys(DEFAULT_TUPLE_KEYS));
WriteRequest writeRequest = new WriteRequest().writes(new WriteRequestWrites().tupleKeys(DEFAULT_TUPLE_KEYS));
CheckRequest checkRequest = new CheckRequest()
.tupleKey(new TupleKey().user(DEFAULT_USER).relation("reader")._object(DEFAULT_DOC));
.tupleKey(new CheckRequestTupleKey().user(DEFAULT_USER).relation("reader")._object(DEFAULT_DOC));

// When
api.write(storeId, writeRequest).get();
Expand All @@ -207,9 +217,9 @@ public class OpenFgaApiIntegrationTest {
String storeName = thisTestName();
String storeId = createStore(storeName);
String _authModelId = writeAuthModel(storeId);
WriteRequest writeRequest = new WriteRequest().writes(new TupleKeys().tupleKeys(DEFAULT_TUPLE_KEYS));
WriteRequest writeRequest = new WriteRequest().writes(new WriteRequestWrites().tupleKeys(DEFAULT_TUPLE_KEYS));
ExpandRequest expandRequest =
new ExpandRequest().tupleKey(new TupleKey()._object(DEFAULT_DOC).relation("reader"));
new ExpandRequest().tupleKey(new ExpandRequestTupleKey()._object(DEFAULT_DOC).relation("reader"));

// When
api.write(storeId, writeRequest).get();
Expand All @@ -229,7 +239,7 @@ public class OpenFgaApiIntegrationTest {
String storeName = thisTestName();
String storeId = createStore(storeName);
String _authModelId = writeAuthModel(storeId);
WriteRequest writeRequest = new WriteRequest().writes(new TupleKeys().tupleKeys(DEFAULT_TUPLE_KEYS));
WriteRequest writeRequest = new WriteRequest().writes(new WriteRequestWrites().tupleKeys(DEFAULT_TUPLE_KEYS));
ListObjectsRequest listObjectsRequest =
new ListObjectsRequest().user(DEFAULT_USER).relation("reader").type("document");

Expand All @@ -249,7 +259,7 @@ public class OpenFgaApiIntegrationTest {
String storeName = thisTestName();
String storeId = createStore(storeName);
String _authModelId = writeAuthModel(storeId);
WriteRequest writeRequest = new WriteRequest().writes(new TupleKeys().tupleKeys(DEFAULT_TUPLE_KEYS));
WriteRequest writeRequest = new WriteRequest().writes(new WriteRequestWrites().tupleKeys(DEFAULT_TUPLE_KEYS));

// When
api.write(storeId, writeRequest).get();
Expand All @@ -261,7 +271,7 @@ public class OpenFgaApiIntegrationTest {
String firstTupleKeyJson =
mapper.writeValueAsString(response.getChanges().get(0).getTupleKey());
assertEquals(
"{\"object\":\"document:2021-budget\",\"relation\":\"reader\",\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\"}",
"{\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\",\"relation\":\"reader\",\"object\":\"document:2021-budget\",\"condition\":null}",
firstTupleKeyJson);
}

Expand All @@ -272,7 +282,12 @@ public class OpenFgaApiIntegrationTest {
String storeId = createStore(storeName);
String authModelId = writeAuthModel(storeId);
WriteAssertionsRequest writeRequest = new WriteAssertionsRequest()
.assertions(List.of(new Assertion().tupleKey(DEFAULT_TUPLE_KEY).expectation(true)));
.assertions(List.of(new Assertion()
.tupleKey(new CheckRequestTupleKey()
.user(DEFAULT_USER)
.relation("reader")
._object(DEFAULT_DOC))
.expectation(true)));

// When
api.writeAssertions(storeId, authModelId, writeRequest).get();
Expand All @@ -282,7 +297,7 @@ public class OpenFgaApiIntegrationTest {
// Then
String responseJson = mapper.writeValueAsString(response.getAssertions());
assertEquals(
"[{\"tuple_key\":{\"object\":\"document:2021-budget\",\"relation\":\"reader\",\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\"},\"expectation\":true}]",
"[{\"tuple_key\":{\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\",\"relation\":\"reader\",\"object\":\"document:2021-budget\"},\"expectation\":true}]",
responseJson);
}

Expand All @@ -303,10 +318,8 @@ public class OpenFgaApiIntegrationTest {
* @return The created Authorization Model ID
*/
private String writeAuthModel(String storeId) throws Exception {
WriteAuthorizationModelRequest request =
mapper.readValue(DEFAULT_AUTH_MODEL, WriteAuthorizationModelRequest.class);
WriteAuthorizationModelResponse response =
api.writeAuthorizationModel(storeId, request).get().getData();
var request = mapper.readValue(authModelJson, WriteAuthorizationModelRequest.class);
var response = api.writeAuthorizationModel(storeId, request).get().getData();
return response.getAuthorizationModelId();
}

Expand Down
Loading
Loading