Microsoft Information Protection SDK - file handler concepts

In the MIP File SDK, mip::FileHandler exposes the operations that read and write labels or protection across file types with built-in support.

Supported file types

  • Office file formats based on OPC (Office 2010 and later)
  • Legacy Office file formats (Office 2007)
  • PDF
  • Generic PFILE support
  • Files that support Adobe XMP

File handler functions

mip::FileHandler exposes methods for reading, writing, and removing both labels and protection information. For the full list, consult the API reference.

This article covers the following methods:

  • GetLabel()
  • SetLabel()
  • DeleteLabel()
  • RemoveProtection()
  • CommitAsync()

Requirements

To create a FileHandler to work with a specific file, provide:

Create a file handler

The first step in managing files in the File SDK is to create a FileHandler object. This class includes the functionality required to get, set, update, delete, and commit label changes to files.

Create the FileHandler by calling the FileEngine's CreateFileHandlerAsync function by using the promise/future pattern.

CreateFileHandlerAsync accepts the following parameters: the path to the file to read or modify, the path to use for audit reporting, a flag that enables audit discovery, the mip::FileHandler::Observer for asynchronous event notifications, and the promise for the FileHandler.

Note

Implement the mip::FileHandler::Observer class in a derived class because CreateFileHandler requires the Observer object.

auto createFileHandlerPromise = std::make_shared<std::promise<std::shared_ptr<mip::FileHandler>>>();
auto createFileHandlerFuture = createFileHandlerPromise->get_future();
fileEngine->CreateFileHandlerAsync(filePath, filePath, true, std::make_shared<FileHandlerObserver>(), createFileHandlerPromise);
auto fileHandler = createFileHandlerFuture.get();

After you create the FileHandler object, you can perform file operations (get/set/delete/commit).

Read a label

Metadata requirements

Successfully reading metadata from a file and translating it into something that applications can use has a few requirements.

  • The label being read must still exist in the Microsoft 365 service. If someone deleted the label, the SDK fails to obtain information about that label and returns an error.
  • The file metadata must be intact. This metadata includes:
    • Attribute1
    • Attribute2

GetLabel()

After you create the handler that points to a specific file, read the label synchronously by calling fileHandler->GetLabel(). The method returns a mip::ContentLabel object that contains all of the information about the applied label.

auto label = fileHandler->GetLabel();

You can read label data from the label object and pass it to any other component or functionality in the application.


Set a label

Setting a label is a two-part process. After you create a handler that points to the file in question, set the label by calling FileHandler->SetLabel() with some parameters: mip::Label, mip::LabelingOptions, and mip::ProtectionOptions. First, resolve the label ID to a label and then define the labeling options.

Resolve label ID to mip::Label

The SetLabel function's first parameter is a mip::Label. Often, the application works with label identifiers rather than labels. Resolve the label identifier to mip::Label by calling GetLabelById on the file or policy engine:

std::shared_ptr<mip::Label> label = engine->GetLabelById(labelId);

Labeling options

The second parameter required to set the label is mip::LabelingOptions.

LabelingOptions specifies more information about the label, such as the AssignmentMethod and justification for an action.

  • mip::AssignmentMethod is an enumerator that has three values: STANDARD, PRIVILEGED, or AUTO. Review the mip::AssignmentMethod reference for more details.
  • Provide justification only if the service policy requires it and when lowering the existing sensitivity of a file.

This snippet demonstrates how to create the mip::LabelingOptions object and set downgrade justification and message.

auto labelingOptions = mip::LabelingOptions(mip::AssignmentMethod::STANDARD);
labelingOptions.SetDowngradeJustification(true, "Because I made an educated decision based upon the contents of this file.");

Protection settings

Some applications might need to perform operations on behalf of a delegated user identity. The mip::ProtectionSettings class lets the application define the delegated identity per handler. Previously, the engine classes performed the delegation. That design had significant disadvantages in application overhead and service round trips. Moving the delegated user settings to mip::ProtectionSettings and making them part of the handler class eliminates this overhead, which improves performance for applications that perform many operations on behalf of diverse sets of user identities.

If you don't need delegation, pass mip::ProtectionSettings() to the SetLabel function. If you need delegation, create a mip::ProtectionSettings object and set the delegated mail address:

mip::ProtectionSettings protectionSettings;
protectionSettings.SetDelegatedUserEmail("alice@contoso.com");

Set the label

After you fetch mip::Label by using the ID, set the labeling options, and optionally set the protection settings, you can set the label on the handler.

If you didn't set protection settings, set the label by calling SetLabel on the handler:

fileHandler->SetLabel(label, labelingOptions, mip::ProtectionSettings());

If you need protection settings to perform a delegated operation, use:

fileHandler->SetLabel(label, labelingOptions, protectionSettings);

After you set the label on the file that the handler references, commit the change and write a file to disk or create an output stream.

Commit changes

The final step in committing any change to a file in the MIP SDK is to commit the change. Use the FileHandler->CommitAsync() function.

To implement the commitment function, return to promise/future, creating a promise for a bool. The CommitAsync() function returns true if the operation succeeded or false if it failed for any reason.

After you create the promise and future, call CommitAsync() and provide two parameters: the output file path (std::string) and the promise. Lastly, get the result by getting the value of the future object.

auto commitPromise = std::make_shared<std::promise<bool>>();
auto commitFuture = commitPromise->get_future();
fileHandler->CommitAsync(outputFile, commitPromise);
auto wasCommitted = commitFuture.get();

Important

The FileHandler won't update or overwrite existing files. You must implement replacement for the file you're labeling.

If you write a label to FileA.docx, CommitAsync() creates a copy of the file, FileB.docx, with the label applied. Write code to remove or rename FileA.docx and rename FileB.docx.


Delete a label

auto createFileHandlerPromise = std::make_shared<std::promise<std::shared_ptr<mip::FileHandler>>>();
auto createFileHandlerFuture = createFileHandlerPromise->get_future();
mEngine->CreateFileHandlerAsync(filePath, filePath, true, std::make_shared<FileHandlerObserver>(), createFileHandlerPromise);
auto fileHandler = createFileHandlerFuture.get();

mip::LabelingOptions labelingOptions(mip::AssignmentMethod::PRIVILEGED);
labelingOptions.SetDowngradeJustification(true, "Label unnecessary.");
fileHandler->DeleteLabel(labelingOptions);

auto commitPromise = std::make_shared<std::promise<bool>>();
auto commitFuture = commitPromise->get_future();
fileHandler->CommitAsync(outputFile, commitPromise);

Remove protection

Validate that the user has rights to remove protection from the file being accessed. Perform an access check before removing protection.

The RemoveProtection() function behaves similarly to SetLabel() or DeleteLabel(). Call the method on the existing FileHandler object, and then commit the change.

Important

As the application developer, it's your responsibility to perform this access check. Failure to properly perform the access check can result in data leakage.

C++ example:

// Validate that the file referred to by the FileHandler is protected.
if (fileHandler->GetProtection() != nullptr)
{
    // Validate that user is allowed to remove protection.
    if (fileHandler->GetProtection()->AccessCheck(mip::rights::Export()) || fileHandler->GetProtection()->AccessCheck(mip::rights::Owner()))
    {
        auto commitPromise = std::make_shared<std::promise<bool>>();
        auto commitFuture = commitPromise->get_future();
        // Remove protection and commit changes to file.
        fileHandler->RemoveProtection();
        fileHandler->CommitAsync(outputFile, commitPromise);
        result = commitFuture.get();
    }
    else
    {
        // Throw an exception if the user doesn't have rights to remove protection.
        throw std::runtime_error("User doesn't have EXPORT or OWNER right.");
    }
}

.NET example:

if(handler.Protection != null)
{
    // Validate that user has rights to remove protection from the file.
    if(handler.Protection.AccessCheck(Rights.Export) || handler.Protection.AccessCheck(Rights.Owner))
    {
        // If user has Extract right, remove protection and commit the change. Otherwise, throw exception.
        handler.RemoveProtection();
        bool result = handler.CommitAsync(outputPath).GetAwaiter().GetResult();
        return result;
    }
    else
    {
        throw new Microsoft.InformationProtection.Exceptions.AccessDeniedException("User lacks EXPORT right.");
    }
}

Next steps