Using MIP SDK in SharePoint – II

This post is in continuation with how to use MIP SDK in C#. Please go through it first to cover some basic concepts like, setting up your system, nuget required for MIP and registering Azure AD app which will be used in the code below too.

My case scenario was to analyze a protected file present in SharePoint Document Library and set/remove protection on it depending on the contents of the document. In case you are not aware, we can use Azure Information Protection or Unified Labeling in SharePoint too. It’s pretty straight-forward and more details can be found here.

In comparison to the code I uploaded on git, there are few basic changes for using MIP SDK in Provider Hosted add-ins. Instead of relying on file path, we will use file stream from SharePoint and similarly, upload that stream back to SharePoint. I am assuming that you all are aware of provider hosted add-ins and have gone through the part one of this post. I am listing the changes below while rest of the stuff remains the same.

  1. Create a provider hosted add-in using Visual Studio. I have used SharePoint Online and MVC template for my work
  2. In AppManifest.xml give proper permission to access libraries of your target site collection
  3. Now coming to HomeController, InvokeMIP method, the first difference is how will we get tenant id of our host web. Instead of finding it in Claims, we will get it using TokenHelper’s method GetRealmFromTargetUrl
  4. Apart from this, we will create SharePoint context to get file stream of the uploaded file where we need to perform MIP operations
  5. Using this stream, MIP file handler will be created
  6. The output will be collected in memory stream
  7. Output Stream will be used to upload the changed document back to SharePoint (For more details on how to upload large files in provider hosted add-in, check this post here)

The complete code will look like below:

Make sure you have registered Azure AD app with proper permissions and your SharePoint App has correct permissions too.

P.S. Please note that MIP SDK does not support content marking (watermark, header, footer). You can apply a label with watermark but it wont be reflected until you open the document and save it in office again. More details here

Happy Coding!

Update November 9, 2019:

Now you can enable sensitivity labels for file present in OneDrive and SharePoint. The feature comes with some new capabilities and some limitations and is currently in public preview. Click here to learn more about this.

Standalone SharePoint Add-In

This post is about developing and deploying an application which works both as a provider hosted add-in as well as a standalone application depending on the URL from which it is launched. So, if a user logins to O365 and click on the app, it will open a provider hosted add-in and follow O365 authentication and if I launch the app directly using URL, then it will ask me to login using ASP.Net credentials and will follow ASP.Net Identity Framework.

It took me some time to decide on the approach as normal ASP.Net application uses AuthorizeAttribute while SharePoint uses ActionFilterAttribute and TokenHelper.cs class which comes by default while creating provider hosted add-in. This post assumes that the reader is aware about:

  • Provider Hosted Add-ins
  • ASP.Net Web Application projects using Identity Framework
  • Basics of MVC

Below are the steps I followed to achieve this.

  1. Create a provider hosted add-in using Visual Studio. You will end up with the below project structure:

    ProviderHostedAdd-In

  2. Install relevant nuget packages. Some of these will install other packages on which they are dependent. I installed the following:
    1. Microsoft ASP.NET Identity Core
    2. Microsoft ASP.NET Identity Owin
    3. Microsoft ASP.NET Identity EntityFramework
    4. Microsoft.Owin.Host.SystemWeb

      Nugetpackages

  3. Add relevant files in the project. When you create an ASP.Net MVC application, there are few files which gets added by default. Following is the list of such files which I added in the project:
    1. App_Data/IdentityConfig.cs
    2. App_Data/Startup.cs
    3. Controllers/AccountController
    4. Controllers/ManageController
    5. Models/AccountViewModel.cs
    6. Models/IdentityModel.cs
    7. Models/ManageViewModels.cs
    8. Views/Account -> complete folder
    9. Views/Manage -> complere folder
    10. Views/Shared/_LoginPartial.cshtml

      P.S. While copying all of these files; make sure the namespace of classes are correct

      NewClasses

  4. Create the tables to store user credentials in your database. You can find the script here or download from internet. Run this script in your relevant database and add connection string in web.config.
    <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=AuthenticationProviderHosted;Integrated Security=True" providerName="System.Data.SqlClient" />
    </connectionStrings>
    

    Also add this connection string name in IdentityModel.cs file.

  5. The final step is to add a common filter attribute which will work with both Sharepoint authentication and asp.net authentication. For this I created a new class called CommonContextFilterAttribute which inherits from AuthorizeAttribute. The code is simple here, if request has host URL present then authenticate using SharePoint else move to asp.net.

    FilterAttribute

  6. In the HomeController.cs file change [SharePointContextFilter] to [CommonContextFilter]. Also, modify the Index action to handle the SP User class. I just added a host URL filter again to check if it is SP user or ASP.Net User. And you are done!

Try running the app as provider hosted add-in, just click on “Start” at the top and you will get the below screen

ProviderAddInRun

And if you run as standalone app then you will get below screen:

Stanalone

You can register a new user here and login to get the desired screen:

Stanalone-LoggedIn

Notice the user name here i.e. garima@outlook.com. I used this login to register a user and then logged in to the site.

Similarly, you can modify the code to integrate login through Facebook, Twitter or any other website using OWIN.

You can download the complete source code from here.

Note: To debug the add-in as a standalone application, please make the following changes:

  • Set the web project as start-up project
  • In the “Properties” of web project -> “Web” tab -> select “Current Page” as “Start Action”.
  • Revert these changes to debug as SP add-in.
  • You can deploy this project as a normal provider hosted add-in and it should work both ways. I tested by deploying the web project in IIS. Skipping the steps here as it is straightforward.

Hope this helps. Happy coding!

Provider Hosted App – Copy Document across Site Collections

To copy documents across site collections in provider hosted app, we need to perform the following steps:

  1. Provide appropriate permissions to app
  2. Create client context for the source location
  3. Downloading document stream from source
  4. Create client context for the destination
  5. Uploading document to destination (This step is same as mentined in my previous post)

The approach I am going to display will also help if you want to create another site collection context from the existing one.

For the above scenario, please note the following points:

  1. App should have minimum “Write” permission on “Tenant” for the code to work as we are moving document from one site collection to another.
  2. I have used app-only policy. This is used as user might not have write permission on the whole tenant. The only downside is, in “Created By” field, instead of user name, it shows “SharePoint App”. I created a custom column to save actual user name.

Hope this helps. Happy coding!!

 

Provider Hosted App – Upload Large Files using CSOM

We all at some time struggled with uploading large files using CSOM in O365. Using managed client object model, we can upload a maximum of 2MB file size. If the file is of larger size, we usually had two options:

  1. File.SaveBinaryDirect
  2. Using REST which supports upto 2GB

The first option can not be used in Sharepoint Online because SaveBinaryDirect does not work with claims authentication (check this link).

Update: We can now use SaveBinaryDirect in SP Online by using SharePointOnlineCredentials class which could be used for authentication of context but I am yet not able to make it work in provider hosted add-in. It works in normal console app by providing credentials though.

Does that mean we are left with REST only? Actually NO. We have one more way by which we can upload files greater than 2MB in O365 using CSOM in provider hosted app. But let’s first see the standard CSOM way which allows us 2MB file upload.

Now the method to upload larger files is exactly the same. We only have to use FileCreationInformation.ContentStream property instead of FileCreationInformation.Content property. The whole code is present below:

In some cases, you might get a FileNotFoundException while using above code. Below is another way to use the code

Note that I replaced FileStream with the input stream present in the uploaded file itself.

P.S. I have tested the above code for files upto 50 MB. It depends largely on internet speed. Once for 50MB file size it gave me time out error (which could be fixed by setting RequestTimeout property of client context)

Hope this helps.

Reference:

  1. O365 AMS

Unique Identifier for O365

Multi-tenant apps are a very common scenario in real world. Whenever we create an app, we mostly target it to multiple tenants for wider reach. There are some cases in which we want to save our app data in custom database instead of app web or host web. These scenarios are very frequent and at that time we need a unique identifier to differentiate among tenants. So my search started for a pre-existing identifier of O365 and ended right at TokenHelper.cs file.
Yes, Microsoft has already provided a code for it, I just didn’t realize it’s a unique identifier. 🙂

In TokenHelper.cs there is a public method GetRealmFromTargetUrl(Uri targetApplicationUri) that takes URI of the target sharepoint site as its parameter. This method actually return a string representation of the realm GUID. According to msdn,

 Realm is unique to each tenant in Office 365 or to each SharePoint farm on-premises. It is possible to discover the realm at run time. So, it is not necessary to cache this information between requests, but it will cost you an extra round trip to SharePoint each time you want to look it up. If you use code similar toTokenHelper.GetRealmFromTargetUrl with the site URL, and cache the result per site (or even per site and per user), you can use this later without making the extra call.

For more details, visit this link.

Therefore, above method return a unique id for tenant. You can pass host web uri in it. This id can be saved in database for further use and can even be cached.

Source code for this method is given below. This is copy pasted from TokenHelper.cs class which Visual Studio auto-generates on creating provider hosted app.

 


public static string GetRealmFromTargetUrl(Uri targetApplicationUri)
{
WebRequest request = WebRequest.Create(targetApplicationUri + "/_vti_bin/client.svc");
request.Headers.Add("Authorization: Bearer ");

try
{
using (request.GetResponse())
{
}
}
catch (WebException e)
{
if (e.Response == null)
{
return null;
}

string bearerResponseHeader = e.Response.Headers["WWW-Authenticate"];
if (string.IsNullOrEmpty(bearerResponseHeader))
{
return null;
}

const string bearer = "Bearer realm=\"";
int bearerIndex = bearerResponseHeader.IndexOf(bearer, StringComparison.Ordinal);
if (bearerIndex < 0)
{
return null;
}

int realmIndex = bearerIndex + bearer.Length;

if (bearerResponseHeader.Length >= realmIndex + 36)
{
string targetRealm = bearerResponseHeader.Substring(realmIndex, 36);

Guid realmGuid;

if (Guid.TryParse(targetRealm, out realmGuid))
{
return targetRealm;
}
}
}
return null;
}

 

Hope this helps. 🙂