# Managing User Data

# Overview

Now that you know how to upload data, it's time to walk through how to manage data on behalf of end users.

With Parcel, you can store data such that your users retain full ownership and control. With user permission, you can still use their data to power analyses and features in your application.

You will recall that you specified permissions for your application when you registered your app in the Parcel developer portal. This tutorial will show you how these permissions enable you to work with user data without sacrificing security or privacy.

# Document Owners

Each document always has an owner. This owner is a Parcel identity, which can represent a user, a developer, or an application.

By default, the document owner is the identity that uploads the document:

const acmeIdentity = await parcel.getCurrentIdentity();
console.log(`Uploading data with identity: ${acmeIdentity.id}`);

const data = 'Eggs and Emmentaler is the best!';
const documentDetails = { title: 'Favorite sando', tags: ['lang:en'] };
const acmeDocument = await parcel.uploadDocument(data, {
  details: documentDetails,
  toApp: undefined,
}).finished;
console.log(`Created document ${acmeDocument.id} with owner ${acmeDocument.owner}`);
Uploading data with identity: IPoxXkdvFsrqzDdU7h3QqSs
Created document DLPfSu1yGKGpxbD9RAnKEtk with owner IPoxXkdvFsrqzDdU7h3QqSs

A document owner always has the right to:

  • download the document
  • add or remove access to the document
console.log(`Downloading document ${acmeDocument.id} with identity ${acmeIdentity.id}`);
let download = parcel.downloadDocument(acmeDocument.id);
let saver = fs.createWriteStream(`./acme_document`);
await download.pipeTo(saver);
console.log(`Document ${acmeDocument.id} downloaded to ./acme_document`);

const acmeData = fs.readFileSync('./acme_document', 'utf-8');
console.log(`Here's the data: ${acmeData}`);
Downloading document DLPfSu1yGKGpxbD9RAnKEtk with identity IPoxXkdvFsrqzDdU7h3QqSs
Document DLPfSu1yGKGpxbD9RAnKEtk downloaded to ./acme_document
Here's the data: The weather will be sunny tomorrow and cloudy on Tuesday.

# Uploading User Data

Parcel allows your application to upload documents on behalf of other users, or identities, on the platform.

To do so, you specify the IDs of both the new document's intended owner and your app at the time of upload:

const bobId = process.env.BOB_IDENTITY_ID! as IdentityId; // REPLACE ME
const appId = process.env.ACME_APP_ID! as AppId; // REPLACE ME
console.log(`Uploading data for end user Bob (ID: ${bobId}) for your app (ID: ${appId})`);
const bobDocument = await parcel.uploadDocument(data, {
  details: documentDetails,
  owner: bobId,
  toApp: appId,
}).finished;
console.log(`Created document ${bobDocument.id} with owner ${bobDocument.owner}`);
Uploading data for end user Bob (ID: IJ2UnsgTss9GZw2EXKXagmj) for your app (ID: AVNidsM1HR76CFTJvGrrTrd)
Created document D5k3PDwawHhNgE8oNFgkhmV with owner IJ2UnsgTss9GZw2EXKXagmj

# Accessing User Data

By default, only document owners are able to access their documents.

If we try to access our end user's document using our application's credentials:

download = parcel.downloadDocument(bobDocument.id);
saver = fs.createWriteStream(`./bob_data_by_acme`);
try {
  console.log(
    `Attempting to access Bob's document using Acme's identity ${acmeIdentity.id} and without permission...`,
  );
  await download.pipeTo(saver);
} catch (error: any) {
  console.log(`Acme was not able to access Bob's data (this was expected): ${error}`);
}

We see an error because we do not have permission

Acme was not able to access Bob's data (this was expected): ApiError: error in document download: Error from http://api.oasislabs.com/parcel/v1/documents/D5k3PDwawHhNgE8oNFgkhmV/download: permission denied

Bob can grant app to access his document by calling createGrant():

console.log(
  `Bob granting Acme app ${process.env.ACME_APP_ID} permission to access document ${bobDocument.id}...`,
);
const parcelBob = new Parcel({
  clientId: process.env.BOB_SERVICE_CLIENT_ID!,
  privateKey: {
    kid: 'bob-service-client',
    use: 'sig',
    kty: 'EC',
    crv: 'P-256',
    alg: 'ES256',
    x: 'kbhoJYKyOgY645Y9t-Vewwhke9ZRfLh6_TBevIA6SnQ',
    y: 'SEu0xuCzTH95-q_-FSZc-P6hCSnq6qH00MQ52vOVVpA',
    d: '10sS7lgM_YWxf79x21mWalCkAcZZOmX0ZRE_YwEXcmc',
  },
});
await parcelBob.createGrant({
  grantee: process.env.ACME_APP_ID! as AppId,
  condition: { 'document.id': { $eq: bobDocument.id } },
});
Bob granting Acme app AVNidsM1HR76CFTJvGrrTrd permission to access document D5k3PDwawHhNgE8oNFgkhmV...

Now, with Bob's permission, we are able to access the document:

console.log(
  `Attempting to access Bob's document using Acme's identity ${acmeIdentity.id} and with his permission...`,
);
download = parcel.downloadDocument(bobDocument.id);
saver = fs.createWriteStream(`./bob_data_by_acme`);
await download.pipeTo(saver);
console.log(`Document ${bobDocument.id} has been downloaded to ./bob_data_by_acme`);
const bobData = fs.readFileSync('./bob_data_by_acme', 'utf-8');
console.log(`Here's the data: ${bobData}`);
Attempting to access Bob's document with Acme identity IPoxXkdvFsrqzDdU7h3QqSs
Document D5k3PDwawHhNgE8oNFgkhmV has been downloaded to ./bob_data_by_acme
Here's the data: The weather will be sunny tomorrow and cloudy on Tuesday.

If you wish to learn more about document permissions and grants, check the Constraining Data Access chapter. In addition, Oasis Steward offers your users a web interface for granting your app permission to access user's documents. Consult this chapter to learn how to embed it in your app.

# Document Access History

Besides providing clear grant-based access to documents, Parcel also logs all accesses of the document including the identity which performed the access and the exact time the access was initiated. The document's access events can be queried by calling document.history().

Let's see who downloaded Bob's document in our example:

console.log(`Access log for document ${bobDocument.id}:`);
for (const event of (await bobDocument.history()).results) {
  console.log(`${event.accessor} accessed this document on ${event.createdAt.toISOString()}`);
}

This prints something that will look like the following:

Access log for document D3aQ6QEReyAxMUUKedtbNRr:
I25QxGDwwf42QeWzxuW8G8h accessed this document on 2021-06-03T16:15:32.503Z

The access event printed above belongs to the successful download event at the end of the previous section. Parcel logs successfully initiated accesses only and denied accesses to the document are ignored.

Now we will perform another download and see, if the document's history changes accordingly:

console.log(`Downloading document ${bobDocument.id} again...`);
download = parcel.downloadDocument(bobDocument.id);
saver = fs.createWriteStream(`./bob_data_by_acme_again`);
await download.pipeTo(saver);

console.log(`Access log for document ${bobDocument.id}:`);
for (const event of (await bobDocument.history()).results) {
  console.log(`${event.accessor} accessed this document on ${event.createdAt.toISOString()}`);
}
Downloading document D3aQ6QEReyAxMUUKedtbNRr again...
Access log for document D3aQ6QEReyAxMUUKedtbNRr:
I25QxGDwwf42QeWzxuW8G8h accessed this document on 2021-06-03T16:15:32.503Z
I25QxGDwwf42QeWzxuW8G8h accessed this document on 2021-06-03T16:15:32.546Z

EXAMPLE

You can view the full example in the Parcel Examples repository.

Next, we will show you how you can run confidential analyses on granted user data with Parcel workers, so you never need to directly access or download raw user data.