visit
TL; DR: Yes. It actually is really easy to add the Virtru SDK to your project, as I’ll demonstrate through the construction of a “Protect & Share” Google Docs add-on. See the completed project .
Well, we’re a G Suite shop, so let’s start there. I spend a lot of time in Google Docs. Whether taking notes, writing reports, developing customer-facing documentation, or drafting this very blog post, it’s a rare day that I don’t work in at least one Google Doc. And since these documents can be internal-only or customer-facing, I’m always trying to figure out how best to share them.
When I think “add features to G Suite”, I typically think “Chrome Extension”. But recently I’ve been experimenting with , and it’s clearly the more compelling solution for this use case. The code runs directly within G Suite, so I won’t have to build and package an extension. It’s JavaScript & HTML-based, so it will support Virtru’s browser JS SDK. And it’s designed to do exactly what I ultimately want to do: build add-ons to extend Google Sheets, Docs, & Slides.
To build a Google Docs add-on that will allow me to generate a secure, encrypted copy of my Google Docs content and share it privately with authorized users. To that end, I’ll need to add the following functions to the Docs UI:
- Capture Google Doc content in a PDF.- Encrypt that PDF with policy controls set by the user such as document watermarking, expiration date, and disable re-sharing.- Download the encrypted PDF, OR
- Send the encrypted PDF as an email attachment.The first, third, and fourth functions above are all straightforward and easily accomplished with the tools available in Google Apps Script. The only new functionality I need to add is to encrypt the document and apply my access controls.<head>
<link href="//sdk.virtru.com/js/latest/auth-widget/index.css" rel="stylesheet"/>
<script src="//sdk.virtru.com/js/latest/auth-widget/index.js"></script>
<script src="//sdk.virtru.com/js/latest/virtru-sdk.min.js"></script>
</head>
Above: virtruSidebar.html - Adding Virtru SDK & styling to client-side HTML.
Next, I need to add the element that will actually perform the encryption step — the Virtru ‘client’. Again, the has some helpful code I can copy to generate the client:<body>
<div id="virtru-auth-widget-mount"></div>
<script type="text/javascript">
async function afterAuth(email) {
// Run all client code from here.
// This will only be called when the user is successfully authenticated.
const client = new Virtru.Client({email});
const yourString = prompt('Type a sting to encrypt: ', 'Hello, world!');
const encryptParams = new Virtru.EncryptParamsBuilder()
.withStringSource(yourString)
.withDisplayFilename('hello.txt')
.build();
const ct = await client.encrypt(encryptParams);
await ct.toFile('hello.html');
}
// Set up the auth widget.
Virtru.AuthWidget('virtru-auth-widget-mount', {afterAuth});
</script>
</body>
Above: virtruSidebar.html - Loading default Virtru client.
This is a good start, but as-is, this client is configured to accept a simple string input and output an encrypted .txt file; I need to take a PDF as input and output an encrypted PDF before this will actually be useful.function createPDF() {
var docBlob = DocumentApp.getActiveDocument().getBlob();
docBlob.setName(doc.getName() + '.pdf');
var blobB64 = Utilities.base64Encode(docBlob.getBytes());
return blobB64;
}
Above: Code.gs - Creating PDF blob from Google Doc content.
And then a client-side function to call the above server-side function and return the document data to the client:
/*
* Runs server-side function to return the contents
* of the document in base64 format.
*
* @return {string} Base 64'd document content.
*/
function genPDF() {
return new Promise(function(resolve, reject) {
google.script.run
.withSuccessHandler(function(blobB64) {
resolve(blobB64);
})
.createPDF();
});
}
Above: virtruSidebar.html - Calling the server-side function to generate pdf blob & transport to client.
async function afterAuth(email) {
// Run all client code from here.
// This will only be called when the user is successfully authenticated.
const client = new Virtru.Client({email});
const yourString = prompt('Type a sting to encrypt: ', 'Hello, world!');
const encryptParams = new Virtru.EncryptParamsBuilder()
.withArrayBufferSource(arrayBuffer) // Change input to accept arrayBuffer
.withDisplayFilename('hello.pdf') // Change display filename to reflect PDF
.build();
const ct = await client.encrypt(encryptParams);
await ct.toFile('hello.pdf.tdf3.html'); // Change output file extension to pdf.tdf3.html
}
// Set up the auth widget.
Virtru.AuthWidget('virtru-auth-widget-mount', {afterAuth});
Above: virtruSidebar.html - Updating the client to accept arrayBuffer and output pdf.tdf3.html.
At this point, the add-on can generate an encrypted copy of the Google Doc. Great! However, it lacks any access controls or sharing options. By default, the document owner is the only authorized user. Let’s change that. /*
* Builds policy according to user inputs.
*
* @param {Array} authUsers The list of authorized users for this piece of content.
* @return {Policy} The policy for this piece of content.
*/
function buildPolicy() {
var policy = new Virtru.PolicyBuilder();
if ($('#watermark-toggle').is(":checked")) {
policy.enableWatermarking();
}
if ($('#disable-reshare-toggle').is(":checked")) {
policy.disableReshare();
}
if ($('#expiration-toggle').is(":checked")) {
if ($('#one-hour-expire').is(":checked")) {
var expTime = (60*60); // Expiration time is set in "seconds from now"
console.log(expTime);
}
if ($('#one-day-expire').is(":checked")) {
var expTime = (60*60*24);
console.log(expTime);
}
if ($('#one-week-expire').is(":checked")) {
var expTime = (60*60*24*7);
console.log(expTime);
}
if ($('#one-month-expire').is(":checked")) {
var expTime = (60*60*24*7*4);
console.log(expTime);
}
policy.enableExpirationDeadlineFromNow([expTime]);
}
return policy.build();
}
Above: virtruSidebar.html - Creating an access control policy based on user input checkboxes.
The policy is then passed to the encryption client. Authorized users can be included in the policy object itself, or added as an additional encryption parameter {array} as shown here: const encryptParams = new Virtru.EncryptParamsBuilder()
.withArrayBufferSource(arrayBuffer)
.withDisplayFilename(`${docTitle}.pdf`)
.withPolicy(policy)
.withUsersWithAccess(authorizedUsers)
.build();
Above: virtruSidebar.html - Adding the policy object and authorized users to the encrypt client.
const client = new Virtru.Client({email});
const encryptParams = new Virtru.EncryptParamsBuilder()
.withArrayBufferSource(arrayBuffer)
.withDisplayFilename(`${docTitle}.pdf`)
.withPolicy(policy)
.withUsersWithAccess(authorizedUsers)
.build();
const ct = await client.encrypt(encryptParams);
var ctString = await ct.toString(); // Encrypt to string rather than to file
// Run server-side function to generate an email
// to the list of authorized users and include
// the HTML generated above as an attachment.
var userMessage = $('#email-body').val().replace(/\n/g, '<br/>');
// Take user input from a field in the sidebar and preserve line breaks
google.script.run.sendEmail(ctString, authorizedUsers, userMessage);
Above: virtruSidebar.html - Updating the client to send encrypted content server-side for email generation.
This string can then be passed to a server-side function to create an email with the encrypted file attached:
function sendEmail(cipherText, recipients, userMessage) {
// Get email address of file owner and assign attachment title.
var fileOwner = Session.getActiveUser().getEmail();
var fileName = DocumentApp.getActiveDocument().getName() + ".pdf.tdf3.html";
// Provide a basic email body for recipients who do not support HTML.
var emailBody = fileOwner + " has shared the encrypted file " + fileName +
" with you.\r\n\r\nIt\'s attached below; please download to open in" +
" Virtru\'s Secure Reader.";
// Assign values to variables in emailHTML.html template.
var htmlContent = HtmlService.createTemplateFromFile('emailHTML');
htmlContent.fileOwner = fileOwner;
htmlContent.fileName = fileName;
htmlContent.userMessage = userMessage;
// Create subject line based on filename and owner email address.
var subject = fileOwner + ' has shared a secure file: "' + fileName + '"';
// Convert ciphertext string to HTML blob.
var blob = Utilities.newBlob(cipherText, 'text/html', fileName);
// Send the email with the tdf.html blob as attachment.
MailApp.sendEmail(recipients, subject, emailBody, {
name: fileOwner,
attachments: [blob],
htmlBody: htmlContent.evaluate().getContent()
});
}
Above: Code.gs - Server-side function to generate and send an email with encrypted attachment.
Above: Encrypting, downloading, and previewing the document.
Encrypt & Email:Above: Encrypting, emailing, and accessing the document.