-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ef72f07
commit 106b976
Showing
11 changed files
with
459 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Spring Boot OpenFGA Example | ||
|
||
An example Spring Boot application that demonstrates using OpenFGA Spring Boot Starter. | ||
|
||
## Requirements | ||
|
||
- Java 17 | ||
- Docker | ||
|
||
## Configuration | ||
|
||
This example is configured to connect to the OpenFGA server running on port 4000. | ||
To use a different FGA server, update `src/main/resources/application.yaml` accordingly. | ||
|
||
## Usage | ||
|
||
### Start OpenFGA server | ||
|
||
```bash | ||
docker pull openfga/openfga:latest | ||
docker run --rm -e OPENFGA_HTTP_ADDR=0.0.0.0:4000 -p 4000:4000 -p 8081:8081 -p 3000:3000 openfga/openfga run | ||
``` | ||
|
||
### Start the example application: | ||
|
||
```bash | ||
./gradlew bootRun | ||
``` | ||
|
||
This will start the application on port 8080. As part of the application startup, some data is loaded: | ||
|
||
- Two documents, with IDs `1` and `2` | ||
- A simple FGA authorization model, along with an authorization tuple that grants user `anne` viewer access to document `1`. | ||
|
||
### Make requests | ||
|
||
Execute a GET request for document 1, for which user `anne` has viewer access: | ||
|
||
```bash | ||
curl http://localhost:8080/documents/1 | ||
``` | ||
|
||
You should receive a 200 response with the document: | ||
|
||
```json | ||
{ | ||
"id": "1", | ||
"content": "this is document 1 content" | ||
} | ||
``` | ||
|
||
Execute a request for document 2, for which user `anne` does **not** have viewer access to: | ||
|
||
```bash | ||
curl http://localhost:8080/documents/2 | ||
``` | ||
|
||
You should receive a 403 response, as user `anne` does not have the required relation to document 2. | ||
|
||
You can also create a document, for which user `anne` will be granted the owner relation for the document: | ||
|
||
```bash | ||
curl -d '{"id": "10", "content": "new document content"}' -H 'Content-Type: application/json' http://localhost:8080/documents | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
examples/servlet/src/main/java/dev/openfga/example/config/LoadDocuments.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package dev.openfga.example.config; | ||
|
||
import dev.openfga.example.model.Document; | ||
import dev.openfga.example.service.DocumentRepository; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.boot.CommandLineRunner; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
/** | ||
* Preloads database with two documents. | ||
*/ | ||
@Configuration | ||
public class LoadDocuments { | ||
private static final Logger log = LoggerFactory.getLogger(LoadDocuments.class); | ||
|
||
@Bean | ||
CommandLineRunner initDatabase(DocumentRepository repository) { | ||
|
||
return args -> { | ||
log.info("Preloading " + repository.save(new Document("1", "this is document 1 content"))); | ||
log.info("Preloading " + repository.save(new Document("2", "this is document 2 content"))); | ||
}; | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
examples/servlet/src/main/java/dev/openfga/example/config/LoadFgaData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package dev.openfga.example.config; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import dev.openfga.sdk.api.client.OpenFgaClient; | ||
import dev.openfga.sdk.api.client.model.ClientReadRequest; | ||
import dev.openfga.sdk.api.client.model.ClientTupleKey; | ||
import dev.openfga.sdk.api.client.model.ClientWriteRequest; | ||
import dev.openfga.sdk.api.configuration.ClientWriteOptions; | ||
import dev.openfga.sdk.api.model.CreateStoreRequest; | ||
import dev.openfga.sdk.api.model.WriteAuthorizationModelRequest; | ||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.List; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.CommandLineRunner; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.io.ClassPathResource; | ||
|
||
/** | ||
* Creates an FGA store, writes a simple authorization model, and adds a single tuple of form: | ||
* user: user:anne | ||
* relation: viewer | ||
* object: document:1 | ||
* | ||
* This is for sample purposes only; would not be necessary in a real application. | ||
*/ | ||
@Configuration | ||
public class LoadFgaData implements CommandLineRunner { | ||
|
||
Logger logger = LoggerFactory.getLogger(LoadFgaData.class); | ||
|
||
@Autowired | ||
private OpenFgaClient openFgaClient; | ||
|
||
@Autowired | ||
private ObjectMapper objectMapper; | ||
|
||
@Override | ||
public void run(String... args) throws Exception { | ||
loadFgaData(); | ||
} | ||
|
||
private void loadFgaData() throws Exception { | ||
// CreateStore | ||
logger.debug("Creating Test Store"); | ||
var store = openFgaClient | ||
.createStore(new CreateStoreRequest().name("Demo Store")) | ||
.get(); | ||
logger.debug("Test Store ID: " + store.getId()); | ||
|
||
// Set the store id | ||
openFgaClient.setStoreId(store.getId()); | ||
|
||
// ListStores after Create | ||
logger.debug("Listing Stores"); | ||
var stores = openFgaClient.listStores().get(); | ||
logger.debug("Stores Count: " + stores.getStores().size()); | ||
|
||
// GetStore | ||
logger.debug("Getting Current Store"); | ||
var currentStore = openFgaClient.getStore().get(); | ||
logger.debug("Current Store Name: " + currentStore.getName()); | ||
|
||
var authModelJson = loadResource(); | ||
var authorizationModel = openFgaClient | ||
.writeAuthorizationModel(objectMapper.readValue(authModelJson, WriteAuthorizationModelRequest.class)) | ||
.get(); | ||
logger.debug("Authorization Model ID " + authorizationModel.getAuthorizationModelId()); | ||
|
||
// Set the model ID | ||
openFgaClient.setAuthorizationModelId(authorizationModel.getAuthorizationModelId()); | ||
|
||
// Write | ||
logger.debug("Writing Tuples"); | ||
openFgaClient | ||
.write( | ||
new ClientWriteRequest() | ||
.writes(List.of(new ClientTupleKey() | ||
.user("user:anne") | ||
.relation("viewer") | ||
._object("document:1"))), | ||
new ClientWriteOptions() | ||
.disableTransactions(true) | ||
.authorizationModelId(authorizationModel.getAuthorizationModelId())) | ||
.get(); | ||
logger.debug("Done Writing Tuples"); | ||
|
||
// Read | ||
logger.debug("Reading Tuples"); | ||
var readTuples = openFgaClient.read(new ClientReadRequest()).get(); | ||
logger.debug("Read Tuples" + objectMapper.writeValueAsString(readTuples)); | ||
} | ||
|
||
private String loadResource() { | ||
try { | ||
return new ClassPathResource("example-auth-model.json").getContentAsString(StandardCharsets.UTF_8); | ||
} catch (IOException ioe) { | ||
throw new RuntimeException("Unable to load resource: " + "example-auth-model.json", ioe); | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
examples/servlet/src/main/java/dev/openfga/example/config/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package dev.openfga.example.config; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
|
||
@Configuration | ||
@EnableMethodSecurity | ||
public class SecurityConfig { | ||
|
||
@Bean | ||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
return http | ||
// For simple demonstration purposes, do not require requests to be authenticated | ||
.authorizeHttpRequests(customizer -> customizer.anyRequest().permitAll()) | ||
.csrf(CsrfConfigurer::disable) | ||
.build(); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
examples/servlet/src/main/java/dev/openfga/example/controllers/DocumentController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package dev.openfga.example.controllers; | ||
|
||
import dev.openfga.example.model.Document; | ||
import dev.openfga.example.service.DocumentService; | ||
import dev.openfga.sdk.api.model.Tuple; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@RestController | ||
public class DocumentController { | ||
|
||
private final DocumentService documentService; | ||
|
||
public DocumentController(DocumentService documentService) { | ||
this.documentService = documentService; | ||
} | ||
|
||
@GetMapping("/documents/{id}") | ||
public Optional<Document> getDocument(@PathVariable String id) { | ||
return documentService.getDocumentWithPreAuthorize(id); | ||
} | ||
|
||
@PostMapping("/documents") | ||
public Document createDocument(@RequestBody Document document) { | ||
return documentService.createDoc(document); | ||
} | ||
|
||
/** | ||
* For convenience only; lists the authorization tuples. | ||
* | ||
* @return authorization tuples | ||
*/ | ||
@GetMapping("/tuples") | ||
public List<Tuple> getTuples() throws Exception { | ||
return documentService.getTuples().getTuples(); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
examples/servlet/src/main/java/dev/openfga/example/model/Document.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package dev.openfga.example.model; | ||
|
||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.Id; | ||
|
||
@Entity | ||
public class Document { | ||
|
||
private @Id String id; | ||
|
||
private String content; | ||
|
||
public Document() {} | ||
|
||
public Document(String id, String content) { | ||
this.id = id; | ||
this.content = content; | ||
} | ||
|
||
public String getId() { | ||
return id; | ||
} | ||
|
||
public void setId(String id) { | ||
this.id = id; | ||
} | ||
|
||
public String getContent() { | ||
return content; | ||
} | ||
|
||
public void setContent(String content) { | ||
this.content = content; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Document{" + "id=" + this.id + ", content='" + this.content + "'}"; | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
examples/servlet/src/main/java/dev/openfga/example/service/DocumentRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package dev.openfga.example.service; | ||
|
||
import dev.openfga.example.model.Document; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface DocumentRepository extends JpaRepository<Document, String> {} |
60 changes: 60 additions & 0 deletions
60
examples/servlet/src/main/java/dev/openfga/example/service/DocumentService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package dev.openfga.example.service; | ||
|
||
import dev.openfga.example.model.Document; | ||
import dev.openfga.sdk.api.client.OpenFgaClient; | ||
import dev.openfga.sdk.api.client.model.ClientReadRequest; | ||
import dev.openfga.sdk.api.client.model.ClientReadResponse; | ||
import dev.openfga.sdk.api.client.model.ClientTupleKey; | ||
import dev.openfga.sdk.api.client.model.ClientWriteRequest; | ||
import dev.openfga.sdk.errors.FgaInvalidParameterException; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.concurrent.ExecutionException; | ||
import org.springframework.security.access.prepost.PreAuthorize; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class DocumentService { | ||
|
||
private final OpenFgaClient fgaClient; | ||
private final DocumentRepository repository; | ||
|
||
public DocumentService(OpenFgaClient fgaClient, DocumentRepository repository) { | ||
this.fgaClient = fgaClient; | ||
this.repository = repository; | ||
} | ||
|
||
/** | ||
* Ensure user has permission in PreAuthorize | ||
*/ | ||
@PreAuthorize("@fga.check('document', #id, 'can_read', 'user', 'anne')") | ||
public Optional<Document> getDocumentWithPreAuthorize(String id) { | ||
return repository.findById(id); | ||
} | ||
|
||
public ClientReadResponse getTuples() throws Exception { | ||
return fgaClient.read(new ClientReadRequest()).get(); | ||
} | ||
|
||
/** | ||
* Demonstrates a simple example of using the injected fgaClient to write authorization data to FGA. | ||
*/ | ||
public Document createDoc(Document document) { | ||
|
||
// write to fga | ||
ClientWriteRequest writeRequest = new ClientWriteRequest() | ||
.writes(List.of(new ClientTupleKey() | ||
.user("user:anne") | ||
.relation("owner") | ||
._object(String.format("document:%s", document.getId())))); | ||
|
||
try { | ||
fgaClient.write(writeRequest).get(); | ||
} catch (InterruptedException | ExecutionException | FgaInvalidParameterException e) { | ||
throw new RuntimeException("Error writing to FGA", e); | ||
} | ||
|
||
// create doc | ||
return repository.save(document); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
logging: | ||
level: | ||
org: | ||
# springframework: DEBUG | ||
example: TRACE | ||
|
||
########## FGA CONFIG ######### | ||
|
||
### NO AUTH - for example purposes only!! ### | ||
openfga: | ||
api-url: http://localhost:4000 |
Oops, something went wrong.