-
Notifications
You must be signed in to change notification settings - Fork 293
Working with Sensibility Labels
As part of Office 365 you can apply sensibility labels to you documents. A sensibility label is a meta data tag that can be set on a document. These tags can also enforce the documents to be encrypted and also apply watermarks and texts in the headers and footers. To apply sensitivity labels you use the Microsoft Information Protection SDK.
Our sample project, EPPlus.Samples.SensibilityLabels, shows how you use EPPlus with the Microsoft Information Protection SDK to handle sensibility labels on your Excel files.
To use this sample, you will need to the following:
- A Microsoft Purview account
- A Microsoft 365 Business Premium subscription or similar supporting sensibility labels.
- An application registration in Azure to get access your sensitivity labelling data.
If you don't have a Microsoft Purview account, you can create one at https://purview.microsoft.com/ using your Azure account.
This tutorial will not cover the setup of your Microsoft Purview account, but you can find guide lines here: Microsoft Purview setup guides
Add at least one sensitivity label to use in the sample under Information Protection - Sensibility labels menu.
To use the Microsoft Information Protection API, you need to register an application ("App Registrations") in the Azure portal or the Microsoft Entra admin center.
To setup your application registration and your workstation, please see this guide Microsoft Information Protection (MIP) SDK setup and configuration.
When the setup is done, in the application registration, add a redirection Uri to: http://localhost, if you run the sample from Visual Studio.
Before you can run the sample you will need to specify a few parameters in the SetupConstants.cs file.
-
_tenantId - The Directory (tenant) ID from the App Registration's Overview page.
-
_clientId - The Application (client) ID from the App Registration's Overview page.
-
_appName - The application display name from the app registration.
-
_loginAccount - The account used to login and get the access token. This account must have access to the protected content we are working with in the samples.
-
_labelSample1 - The name of a sensibility label of you choice that is set on the workbook in sample 1.
-
_protectedSampleFile - The path to a protected excel file that will be read and updated by EPPlus in sample 2, to demonstrate how to work with files protected by sensibility labels.
From EPPlus 8, you can add a sensibility label handler to EPPlus to more easily handle documents with Sensibility labels.
To do so EPPlus has the interface: ISensitivityLabelHandler
Method | Description |
---|---|
InitAsync |
Called to initiate the handler. This method is called once when the handler is assigned to EPPlus. Here you should initiate the MIP SDK and connect to the Microsoft Entra Application used to connect to you Microsoft Purview Account |
DecryptPackageAsync |
Called to decrypt a protected package. When EPPlus identifies a workbook to be encrypted with a sensibility label, the stream will be passed to this function for decryption. |
ApplyLabelAndSavePackageAsync |
Called from EPPlus when the package has been saved and to apply the active label. EPPlus will supply the package stream and the sensibility label to be applied using the the MIP SDK. Returns a stream containing the output from the MIPS SDK. |
UpdateLabelList |
Should update the supplied list of sensibility labels with name, description and other properties not present in the Sensibility Label XML document inside the package. |
GetLabels |
Get all labels from the MIPS SDK |
This code below is provided as-is to illustrate an example.
You will have to adapt it to your setup with the appropriate accounts and permissions and the type of sensibility labels you have in your organization:
using Microsoft.InformationProtection;
using Microsoft.InformationProtection.File;
using Microsoft.InformationProtection.Protection;
using OfficeOpenXml.Interfaces.SensitivityLabels;
using SensibilityLabelHandler;
using System.Collections.ObjectModel;
/// <summary>
/// Example of a sensibility label handler
/// </summary>
public class MySensibilityLabelHandler : ISensitivityLabelHandler, IExcelSensibilityLabelUpdate
{
private IFileEngine _fileEngine;
/// <summary>
/// Initialize the MIP api and setup the authentication you want to use.
/// The sample below is just an example, so you should set it up according to your organizations requirements
/// </summary>
/// <returns></returns>
public async Task InitAsync()
{
MIP.Initialize(MipComponent.File);
//Create ApplicationInfo, setting the client ID from Microsoft Entra App Registration as the ApplicationId.
ApplicationInfo appInfo = new ApplicationInfo()
{
ApplicationId = SetupConstants._clientId,
ApplicationName = SetupConstants._appName,
ApplicationVersion = "1.0.0"
};
MipConfiguration mipConfiguration = new MipConfiguration(appInfo, "mip_data", LogLevel.Trace, false);
MipContext mipContext = MIP.CreateMipContext(mipConfiguration);
var profileSettings = new FileProfileSettings(mipContext,
CacheStorageType.OnDiskEncrypted,
new ConsentDelegateImplementation());
// Load the Profile async and wait for the result.
var fileProfile = await MIP.LoadFileProfileAsync(profileSettings);
var authDelegate = new AuthDelegateImplementation(appInfo);
var engineSettings = new FileEngineSettings(SetupConstants._loginAccount, authDelegate, "", "en-US");
engineSettings.Identity = new Identity(SetupConstants._loginAccount);
_fileEngine = await fileProfile.AddEngineAsync(engineSettings);
}
/// <summary>
/// Decrypts the stream and returns it to EPPlus unencrypted.
/// </summary>
/// <param name="packageStream">The package stream to handle. If the sensibility has any type of protection, the stream is encrypted and must be decrypted before returning it to EPPlus.</param>
/// <param name="id">The unique id for the package.</param>
/// <returns>Returns the decrypted package and the protection information to EPPlus.</returns>
public async Task<IPackageInfo> DecryptPackageAsync(MemoryStream packageStream, string id)
{
PackageInformation ret = new PackageInformation();
var fileHandler = await _fileEngine.CreateFileHandlerAsync(packageStream, $@"myfile.xlsx", true);
if (fileHandler.Protection != null) //Is the stream encrypted?
{
//Yes, save the protection information and decrypt the stream.
ret.ProtectionInformation = fileHandler.Protection;
fileHandler.RemoveProtection();
var ms = new MemoryStream();
await fileHandler.CommitAsync(ms);
ret.PackageStream = ms;
}
else
{
//No, use the unencrypted stream directly.
ret.PackageStream = packageStream;
}
return ret;
}
/// <summary>
/// Applies a sensibility label and sets protection using the MIPS SDK.
/// </summary>
/// <param name="package">The package stream, protection information and the sensibilty label to apply.</param>
/// <param name="id">The unique id for the package.</param>
/// <returns></returns>
public async Task<MemoryStream> ApplyLabelAndSavePackageAsync(IPackageInfo package, string id)
{
package.PackageStream.Position = 0;
var fileHandle = await _fileEngine.CreateFileHandlerAsync(package.PackageStream, $@"myfile.xlsx", true);
if (string.IsNullOrEmpty(package.ActiveLabelId) == false)
{
var l = _fileEngine.GetLabelById(package.ActiveLabelId);
fileHandle.SetLabel(l, new LabelingOptions(), new ProtectionSettings() { });
}
if (package.ProtectionInformation is IProtectionHandler protection)
{
fileHandle.SetProtection(protection);
}
var ms = new MemoryStream();
var ret = await fileHandle.CommitAsync(ms);
if (ret)
{
return ms;
}
else
{
throw new InvalidOperationException("Could not commit sensibility label update.");
}
}
/// <summary>
/// Returns all labels from available for the package.
/// </summary>
/// <param name="id">The unique id for the package.</param>
/// <returns>A collection of labels</returns>
public IEnumerable<IExcelSensibilityLabel> GetLabels(string Id)
{
var list = new List<IExcelSensibilityLabel>();
AddLabelCollectionToList(list, _fileEngine.SensitivityLabels);
return list;
}
/// <summary>
/// Updates the labels from EPPlus with properties missing in the package, such as Name, Description and Color.
/// </summary>
/// <param name="labels"></param>
/// <param name="Id"></param>
public void UpdateLabelList(IEnumerable<IExcelSensibilityLabel> labels, string Id)
{
var lblDict = LabelsDictionary;
foreach (var sl in labels)
{
UpdateItem(lblDict, sl);
}
}
private void AddLabelCollectionToList(List<IExcelSensibilityLabel> list, ReadOnlyCollection<Label> sensitivityLabels)
{
foreach (var ss in sensitivityLabels.OrderBy(x => x.Parent))
{
list.Add(new SensibilityLabel()
{
Id = ss.Id,
Name = ss.Name,
Description = ss.Description,
Tooltip = ss.Tooltip,
Enabled = ss.IsActive,
Removed = !ss.IsActive,
SiteId = SetupConstants._tenantId,
Color = ss.Color,
Method = ss.ActionSource == ActionSource.Automatic ? eMethod.Privileged : eMethod.Standard,
ContentBits = ss.Sensitivity == 0 ? 0 : eContentBits.Encryption
});
AddLabelCollectionToList(list, ss.Children);
}
}
private static void UpdateItem(Dictionary<string, Label> lblDict, IExcelSensibilityLabel sl)
{
if (lblDict.TryGetValue(sl.Id, out Label? lbl))
{
if (sl is IExcelSensibilityLabelUpdate upd)
{
IExcelSensibilityLabel parent;
if (lbl.Parent != null)
{
parent = GetLabel(lbl.Parent);
}
else
{
parent = null;
}
upd.Update(lbl.Name, lbl.Tooltip, lbl.Description, lbl.Color, parent);
}
}
}
private static IExcelSensibilityLabel GetLabel(Label lbl)
{
if(lbl==null)
{
return null;
}
return new SensibilityLabel()
{
Id = lbl.Id,
Name = lbl.Name,
Description = lbl.Description,
Tooltip = lbl.Tooltip,
Enabled = false,
Removed = false,
SiteId = SetupConstants._tenantId,
Color = lbl.Color,
Parent = GetLabel(lbl.Parent)
};
}
Dictionary<string, Label> _labelsDictionary = null;
private Dictionary<string, Label> LabelsDictionary
{
get
{
if (_labelsDictionary==null)
{
_labelsDictionary = new Dictionary<string, Label>();
if (_fileEngine != null)
{
AddListToDict(_labelsDictionary, _fileEngine.SensitivityLabels);
}
}
return _labelsDictionary;
}
}
private static void AddListToDict(Dictionary<string, Label> lbls, IList<Label> labels)
{
foreach (var l in labels)
{
if (lbls.ContainsKey(l.Id) == false)
{
lbls.Add(l.Id, l);
if (l.Children != null && l.Children.Count > 0)
{
AddListToDict(lbls, l.Children);
}
}
}
}
}
/// <summary>
/// Implementation of the IPackageInfo interface, that passes information to EPPlus.
/// </summary>
public class PackageInformation : IPackageInfo
{
/// <summary>
/// The package stream
/// </summary>
public MemoryStream PackageStream { get; set; }
/// <summary>
/// Protection information passed to EPPlus.
/// This property may hold the protection information or any other class used to hold information between the decryption operation and saving and applying the sensibility label.
/// </summary>
public object ProtectionInformation { get; set; }
/// <summary>
/// The label id of the sensibility label to apply.
/// </summary>
public string ActiveLabelId { get; set; }
}
EPPlus Software AB - https://epplussoftware.com
- What is new in EPPlus 5+
- Breaking Changes in EPPlus 5
- Breaking Changes in EPPlus 6
- Breaking Changes in EPPlus 7
- Breaking Changes in EPPlus 8 (beta)
- Addressing a worksheet
- Dimension/Used range
- Copying ranges/sheets
- Insert/Delete
- Filling ranges
- Sorting ranges
- Taking and skipping columns/rows
- Data validation
- Comments
- Freeze and Split Panes
- Header and Footer
- Autofit columns
- Grouping and Ungrouping Rows and Columns
- Formatting and styling
- Conditional formatting
- Using Themes
- Working with custom named table- or slicer- styles