diff --git a/.gitignore b/.gitignore index cd5d4b5..6605884 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# IntelliJ settings +.idea + # vscode settings .vscode @@ -28,4 +31,4 @@ npm-debug.log* !.yarn/sdks !.yarn/versions yarn-debug.log* -yarn-error.log* \ No newline at end of file +yarn-error.log* diff --git a/docs/02-tutorials/01-intro/04-authentication.mdx b/docs/02-tutorials/01-intro/04-authentication.mdx index 537b0b6..1199949 100644 --- a/docs/02-tutorials/01-intro/04-authentication.mdx +++ b/docs/02-tutorials/01-intro/04-authentication.mdx @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; Now that we have exposed a [Privacy Portal](https://github.com/blindnet-io/devkit-quickstart/blob/main/tutorials/intro/web/1-expose-request-interface-event-listener.html) and configured the **Application**, we need to authenticate users so that these two components can communicate. -## Create a server to generates tokens +## Create a server that generates tokens Tokens are generated on the server, using the `key` we got while [creating an application group](./dashboard#step-2-create-an-application-group). @@ -51,7 +51,7 @@ git clone https://github.com/blindnet-io/devkit-quickstart cd devkit-quickstart/tutorials/intro/server/java ``` -In the `src/index.js` file, set the `appId` and `key` variables with the Application's `ID` and `key` you obtained in the step [2. Create a devkit application](./02-dashboard.mdx). +In the `src/main/java/io/blindnet/quickstart/Main.java` file, set the `appId` and `key` variables with the Application's `ID` and `key` you obtained in the step [2. Create a devkit application](./02-dashboard.mdx). ```java String appId = ""; @@ -140,4 +140,4 @@ In the real world, you would need to verify the users' identity before issuing t Now we can build a privacy request in the Privacy Portal submit it processing. Great! The HTML file with our work so far can be found [here](https://github.com/blindnet-io/devkit-quickstart/blob/main/tutorials/intro/web/2-add-authentication.html). -::: \ No newline at end of file +::: diff --git a/docs/02-tutorials/04-connect-storage/index.mdx b/docs/02-tutorials/04-connect-storage/index.mdx index ddcff07..d570c3d 100644 --- a/docs/02-tutorials/04-connect-storage/index.mdx +++ b/docs/02-tutorials/04-connect-storage/index.mdx @@ -13,12 +13,134 @@ To take full advantage of the automation functionalities offered by blindnet dev If you do, user's data will be automatically obtained from your storage and delivered to the user in case of the `ACCESS` request, or automatically deleted should a `DELETE` request get accepted. Configuring a connector might save you a lot of time when there is a request to be processed. -> :construction_worker: writing in progress +The [Connector Manager](/docs/storage/connector-manager) is here to transform and forward requests from the +[Privacy Computation Engine](/docs/computation) to [Storage Connectors](/docs/storage/connectors) that interact +with your actual storage, be it a database, an object storage service, or anything else. -A [Data Access Component](/docs/storage/data-access) is here to transform requests from the [Privacy Computation Engine](/docs/computation) to efficiently execute automated operations on your data storage. +Blindnet provides [Global Connectors](/docs/storage/global-connectors) for some common services and databases +(and third-party contributions are always welcome!) but you might prefer to use a custom connector, for example if +your storage implementation is specific to your project. -For that, we'll set up a dedicated demo storage service in Java, and connect it to the Capture Component using the [Java Storage Connector library](https://github.com/blindnet-io/storage-connector-java). +For that, we'll set up a dedicated demo storage service in Java, and write a custom connector using the +[Java Storage Connector library](https://github.com/blindnet-io/storage-connector-java). - +## Setup -Code for this demo stage service can be found in the `example` directory in the librarie's repository: [github.com/blindnet-io/storage-connector-java > `example`](https://github.com/blindnet-io/storage-connector-java/tree/develop/example). +Source code for this demo storage service can be found in the `example` directory in the library repository: +[github.com/blindnet-io/storage-connector-java > `example`](https://github.com/blindnet-io/storage-connector-java/tree/develop/example). + +The following steps require **git**, a recent (17+) **Java Development Kit** and **Maven.** + +Clone this repository: + +```bash +git clone https://github.com/blindnet-io/storage-connector-java +``` + +Run `mvn package` from the root of the repo to build both the library and the demo. + +The demo can be run easily using `java`: + +```bash +cd example +java -jar target/storage-connector-java-example-1.0.0-SNAPSHOT.jar +``` + +Let's see how it works. + +## Backend storage + +This demo project uses the [H2 in-memory database](https://h2database.com) and the Java [Jdbi](https://jdbi.org) library +for easier access. + +The `User` class maps to our database entries. The code there is in no way Blindnet-specific: you can use any storage +solution to write a Custom Connector. +Our users are made of a unique email address, first and last names, and an optional byte array representing a file. + +```java +public record User(String firstName, String lastName, String email, byte[] proof) { + public String fullName() { + return firstName() + " " + lastName(); + } + + public interface Dao { + @SqlUpdate("CREATE TABLE users (first_name varchar, last_name varchar, email varchar, proof varbinary)") + void createTable(); + + @SqlUpdate("INSERT INTO users (first_name, last_name, email, proof) VALUES (?, ?, ?, ?)") + void insert(String firstName, String lastName, String email, byte[] proof); + + @SqlQuery("SELECT * FROM users WHERE email=?") + User findByEmail(String email); + } +} +``` + +The `Database` class contains the code that establishes the connection to the database, and also holds the data access +object for our User model. On startup, we insert some dummy data for John Doe. + +```java +public class Database { + public static User.Dao users; + + static void init() throws IOException { + Jdbi jdbi = Jdbi.create("jdbc:h2:mem:blindnet;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false"); + jdbi.installPlugin(new SqlObjectPlugin()); + + jdbi.registerRowMapper(new User.Mapper()); + users = jdbi.onDemand(User.Dao.class); + users.createTable(); + users.insert("John", "Doe", "john.doe@example.com", + Objects.requireNonNull(Database.class.getResourceAsStream("/john_doe_proof.pdf")).readAllBytes()); + } +} +``` + +## Connector implementation + +Finally, the actual custom connector code can be found in the `Main` class. + +```java +StorageConnector.create(TOKEN) + .setDataRequestHandler(new MappingRequestHandler.Builder() + .setSubjectMapper(Database.users::findByEmail) + .addSelectorType("CONTACT.EMAIL", User::email) + .addSelectorType("NAME", User::fullName) + .addSelectorTypeBinary("OTHER-DATA.PROOF", u -> BinaryData.fromArray(u.proof())) + .build()) + .start(); +``` + +Here, we are using the high-level API provided by the library. The `MappingRequestHandler` will +automatically process requests and map them from Blindnet-specific language (selectors, data categories, +data subjects, etc.) to app-specific objects. + +The `TOKEN` variable value should be replaced with the connector token obtained on the Dashboard. + +Let's explain the code above. + +```java +.setSubjectMapper(Database.users::findByEmail) +``` + +This sets the function used for mapping a user ID to an object in the application. Here, we are using the +`findByEmail` function defined in the `User` class. + +```java +.addSelectorType("CONTACT.EMAIL", User::email) +.addSelectorType("NAME", User::fullName) +.addSelectorTypeBinary("OTHER-DATA.PROOF", u -> BinaryData.fromArray(u.proof())) +``` + +This part is the most important one: it defines accessor functions used in `ACCESS` requests to automatically fetch +the requested data. + +Selector types (like `CONTACT.EMAIL` and `NAME`) are mapped to member methods of the user object. For binary data, +we need to use a small wrapper (`BinaryData.fromArray` instead of directly returning the array). + +```java +.start(); +``` + +When configuration is done, calling `.start()` on the `StorageConnector` object establishes the connection to the +Connector Manager. The connector will then start receiving and processing requests.