Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to list, approve and deny service principal requests #1100

Merged
merged 6 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions src/sdk/PnP.Core.Admin.Test/SharePoint/PermissionRequestsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PnP.Core.Admin.Model.SharePoint;
using PnP.Core.Admin.Services.Core.CSOM.Requests.ServicePrincipal;
using PnP.Core.Admin.Test.Utilities;
using PnP.Core.Services;
using PnP.Core.Services.Core.CSOM.Utils;
using PnP.Core.Test.Common.Utilities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PnP.Core.Admin.Test.SharePoint
{
[TestClass]
public class PermissionRequestsTests
{
[ClassInitialize]
public static void TestFixtureSetup(TestContext context)
{
TestCommon.Instance.Mocking = false;
}

[TestMethod]
public void ApprovePermissionRequestTest()
{
ApprovePermissionRequest request = new() {RequestId = "0000aaaa00000"};

request.GetRequest(new IteratorIdProvider());

const string response =
"[{\"SchemaVersion\":\"15.0.0.0\",\"LibraryVersion\":\"16.0.23401.12003\",\"ErrorInfo\":null,\"TraceCorrelationId\":\"a12697a0-3075-6000-2b64-1556c644e788\"},2,{\"IsNull\":false},4,{\"IsNull\":false},6,{\"IsNull\":false},8,{\"IsNull\":false},9,{\"_ObjectType_\":\"Microsoft.Online.SharePoint.TenantAdministration.Internal.SPOWebAppServicePrincipalPermissionGrant\",\"ClientId\":\"9b994571-2cf2-4fa4-acd0-9fe1f271410f\",\"ConsentType\":\"AllPrincipals\",\"IsDomainIsolated\":false,\"ObjectId\":\"cUWZm_IspE-s0J_h8nFBDyE1Bl7_7v5Jkj7RHjh3Yt0\",\"PackageName\":null,\"Resource\":\"Kylix Provisioning\",\"ResourceId\":\"5e063521-eeff-49fe-923e-d11e387762dd\",\"Scope\":\"user_impersonation\"}]";

request.ProcessResponse(response);

Assert.IsNotNull(request.Result);
Assert.AreEqual("Kylix Provisioning", request.Result.Resource);
}

[TestMethod]
public void DenyPermissionRequestTest()
{
DenyPermissionRequest request = new() {RequestId = "0000aaaa00000"};

request.GetRequest(new IteratorIdProvider());

const string response =
"[{\"SchemaVersion\":\"15.0.0.0\",\"LibraryVersion\":\"16.0.23401.12003\",\"ErrorInfo\":null,\"TraceCorrelationId\":\"e82697a0-1009-6000-2b64-1b9a0d83d5e2\"},2,{\"IsNull\":false},4,{\"IsNull\":false},6,{\"IsNull\":false}]";

request.ProcessResponse(response);
Assert.IsNotNull(request.Result);
}


[TestMethod]
public void GetPermissionsRequestsRequestTest()
{
GetPermissionRequestsRequest request = new();

request.GetRequest(new IteratorIdProvider());

const string response =
"[{\"SchemaVersion\":\"15.0.0.0\",\"LibraryVersion\":\"16.0.23401.12003\",\"ErrorInfo\":null,\"TraceCorrelationId\":\"23d996a0-5009-6000-2b64-14f4eaefefa8\"},2,{\"IsNull\":false},4,{\"_ObjectType_\":\"Microsoft.Online.SharePoint.TenantAdministration.Internal.SPOWebAppServicePrincipalPermissionRequestCollection\",\"_Child_Items_\":[{\"_ObjectType_\":\"Microsoft.Online.SharePoint.TenantAdministration.Internal.SPOWebAppServicePrincipalPermissionRequest\",\"ClientComponentItemUniqueId\":\"\",\"Id\":\"/Guid(4c9fb5af-b111-4b96-9ecd-efc3dbb53ac1)/\",\"IsDomainIsolated\":true,\"IsolatedDomainUrl\":\"microsoft.com\",\"MultiTenantAppId\":\"\",\"MultiTenantAppReplyUrl\":\"\",\"PackageApproverName\":\"John Doe\",\"PackageName\":\"somesolution-ui-client-side-solution\",\"PackageVersion\":\"1.0.0.0\",\"Resource\":\"api://com.loitzl.test/somesolution\",\"ResourceId\":\"api://com.loitzl.test/somesolution\",\"Scope\":\"Config.Manage\",\"TimeRequested\":\"/Date(1636643974000)/\"},{\"_ObjectType_\":\"Microsoft.Online.SharePoint.TenantAdministration.Internal.SPOWebAppServicePrincipalPermissionRequest\",\"ClientComponentItemUniqueId\":\"\",\"Id\":\"/Guid(9a3338df-8f97-4ba1-bdde-03fde282dfc0)/\",\"IsDomainIsolated\":false,\"IsolatedDomainUrl\":\"\",\"MultiTenantAppId\":\"\",\"MultiTenantAppReplyUrl\":\"\",\"PackageApproverName\":\"John Doe\",\"PackageName\":\"Kylix Intranet Workspaces\",\"PackageVersion\":\"2.0.0.0\",\"Resource\":\"Kylix Workspaces\",\"ResourceId\":\"Kylix Workspaces\",\"Scope\":\"user_impersonation\",\"TimeRequested\":\"/Date(1651759632000)/\"},{\"_ObjectType_\":\"Microsoft.Online.SharePoint.TenantAdministration.Internal.SPOWebAppServicePrincipalPermissionRequest\",\"ClientComponentItemUniqueId\":\"\",\"Id\":\"/Guid(f669b2e6-cb54-47cc-b91a-e3f589fc984d)/\",\"IsDomainIsolated\":false,\"IsolatedDomainUrl\":\"\",\"MultiTenantAppId\":\"\",\"MultiTenantAppReplyUrl\":\"\",\"PackageApproverName\":\"John Doe\",\"PackageName\":\"Kylix Intranet Hub\",\"PackageVersion\":\"6.0.0.0\",\"Resource\":\"Office 365 Exchange Online\",\"ResourceId\":\"Office 365 Exchange Online\",\"Scope\":\"Tasks.Read\",\"TimeRequested\":\"/Date(1651760702000)/\"},{\"_ObjectType_\":\"Microsoft.Online.SharePoint.TenantAdministration.Internal.SPOWebAppServicePrincipalPermissionRequest\",\"ClientComponentItemUniqueId\":\"\",\"Id\":\"/Guid(f929f6da-921f-4580-a9a2-6ff8817e5569)/\",\"IsDomainIsolated\":false,\"IsolatedDomainUrl\":\"\",\"MultiTenantAppId\":\"\",\"MultiTenantAppReplyUrl\":\"\",\"PackageApproverName\":\"John Doe\",\"PackageName\":\"Kylix Intranet Provisioner\",\"PackageVersion\":\"6.0.0.0\",\"Resource\":\"Microsoft Graph\",\"ResourceId\":\"Microsoft Graph\",\"Scope\":\"User.Read.All\",\"TimeRequested\":\"/Date(1651760702000)/\"}]}]";

request.ProcessResponse(response);

Assert.IsNotNull(request.Result);
Assert.AreEqual(4, request.Result.Count);
}

[TestMethod]
public async Task ApprovePermissionsRequestTest_Async()
{
//TestCommon.Instance.Mocking = false;
using PnPContext context = await TestCommon.Instance.GetContextAsync(TestCommonBase.TestSite);

ServicePrincipal principal = new(context);
List<IPermissionRequest> permissionRequests = await principal.GetPermissionRequestsAsync();

var result = await principal.ApprovePermissionRequestAsync(permissionRequests.First().Id.ToString());

Assert.IsNotNull(result);
Assert.IsTrue(!string.IsNullOrWhiteSpace(result.ObjectId));
}

[TestMethod]
public async Task GetPermissionsRequestsTest_Async()
{
//TestCommon.Instance.Mocking = false;
using PnPContext context = await TestCommon.Instance.GetContextAsync(TestCommonBase.TestSite);

ServicePrincipal principal = new(context);
List<IPermissionRequest> permissionRequests = await principal.GetPermissionRequestsAsync();

Assert.IsNotNull(permissionRequests);
Assert.IsTrue(permissionRequests.Count > 0);
}

[TestMethod]
public async Task DenyPermissionsRequestTest_Async()
{
//TestCommon.Instance.Mocking = false;
using PnPContext context = await TestCommon.Instance.GetContextAsync(TestCommonBase.TestSite);

ServicePrincipal principal = new(context);
List<IPermissionRequest> permissionRequests = await principal.GetPermissionRequestsAsync();

await principal.DenyPermissionRequestAsync(permissionRequests.First().Id.ToString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace PnP.Core.Admin.Model.SharePoint
{
internal sealed class PermissionGrant : IPermissionGrant
{
public string ClientId { get; set; }
public string ConsentType { get; set; }
public bool IsDomainIsolated { get; set; }
public string ObjectId { get; set; }
public string PackageName { get; set; }
public string Resource { get; set; }
public string ResourceId { get; set; }
public string Scope { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace PnP.Core.Admin.Model.SharePoint
{
internal sealed class PermissionRequest : IPermissionRequest
{
public Guid Id { get; set; }
public bool IsDomainIsolated { get; set; }
public string IsolatedDomainUrl { get; set; }
public string MultiTenantAppId { get; set; }
public string MultiTenantAppReplyUrl { get; set; }
public string PackageApproverName { get; set; }
public string PackageName { get; set; }
public string PackageVersion { get; set; }
public string Resource { get; set; }
public string ResourceId { get; set; }
public string Scope { get; set; }
public DateTime TimeRequested { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using PnP.Core.Admin.Services.Core.CSOM.Requests.ServicePrincipal;
using PnP.Core.Model.SharePoint;
using PnP.Core.Services;
using PnP.Core.Services.Core.CSOM.Requests;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

namespace PnP.Core.Admin.Model.SharePoint
{
internal sealed class ServicePrincipal : IServicePrincipal
{
private readonly PnPContext _context;

internal ServicePrincipal(PnPContext context)
{
_context = context;
}

public IPermissionGrant ApprovePermissionRequest(string id, VanityUrlOptions vanityUrlOptions = null)
{
return ApprovePermissionRequestAsync(id, vanityUrlOptions).GetAwaiter().GetResult();
}

public async Task<IPermissionGrant> ApprovePermissionRequestAsync(string id, VanityUrlOptions vanityUrlOptions = null)
{
using PnPContext tenantAdminContext = await _context
.GetSharePointAdmin()
.GetTenantAdminCenterContextAsync(vanityUrlOptions)
.ConfigureAwait(false);

ApprovePermissionRequest request = new() {RequestId = id};

ApiCall getPermissionRequestsCall = new(
new List<IRequest<object>> {request});

ApiCallResponse csomResult = await ((Web)tenantAdminContext.Web)
.RawRequestAsync(getPermissionRequestsCall, HttpMethod.Post)
.ConfigureAwait(false);

return (IPermissionGrant)csomResult.ApiCall.CSOMRequests[0].Result;
}

public void DenyPermissionRequest(string id, VanityUrlOptions vanityUrlOptions = null)
{
DenyPermissionRequestAsync(id, vanityUrlOptions).GetAwaiter().GetResult();
}

public async Task DenyPermissionRequestAsync(string id, VanityUrlOptions vanityUrlOptions = null)
{
using PnPContext tenantAdminContext = await _context
.GetSharePointAdmin()
.GetTenantAdminCenterContextAsync(vanityUrlOptions)
.ConfigureAwait(false);

DenyPermissionRequest request = new() {RequestId = id};

ApiCall getPermissionRequestsCall = new(
new List<IRequest<object>> {request});

await ((Web)tenantAdminContext.Web)
.RawRequestAsync(getPermissionRequestsCall, HttpMethod.Post)
.ConfigureAwait(false);
}

public List<IPermissionRequest> GetPermissionRequests(VanityUrlOptions vanityUrlOptions = null)
{
return GetPermissionRequestsAsync(vanityUrlOptions).GetAwaiter().GetResult();
}

public async Task<List<IPermissionRequest>> GetPermissionRequestsAsync(VanityUrlOptions vanityUrlOptions = null)
{
using PnPContext tenantAdminContext = await _context
.GetSharePointAdmin()
.GetTenantAdminCenterContextAsync(vanityUrlOptions)
.ConfigureAwait(false);

GetPermissionRequestsRequest request = new();

ApiCall getPermissionRequestsCall = new(
new List<IRequest<object>> {request});

ApiCallResponse csomResult = await ((Web)tenantAdminContext.Web)
.RawRequestAsync(getPermissionRequestsCall, HttpMethod.Post)
.ConfigureAwait(false);

return (List<IPermissionRequest>)csomResult.ApiCall.CSOMRequests[0].Result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace PnP.Core.Admin.Model.SharePoint
{
/// <summary>
/// A permission grant
/// </summary>
public interface IPermissionGrant
{
/// <summary>
/// ClientId
/// </summary>
string ClientId { get; set; }
/// <summary>
/// Type of consent
/// </summary>
string ConsentType { get; set; }
/// <summary>
/// Domain Isolation
/// </summary>
bool IsDomainIsolated { get; set; }
/// <summary>
/// The object id
/// </summary>
string ObjectId { get; set; }
/// <summary>
/// Name of the package
/// </summary>
string PackageName { get; set; }
/// <summary>
/// The requested resource
/// </summary>
string Resource { get; set; }
/// <summary>
/// Id of the requested resource
/// </summary>
string ResourceId { get; set; }
/// <summary>
/// Permission scope
/// </summary>
string Scope { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;

namespace PnP.Core.Admin.Model.SharePoint
{
/// <summary>
/// A permission request on the SharePoint apps principal
/// </summary>
public interface IPermissionRequest
{
/// <summary>
/// The id
/// </summary>
Guid Id { get; set; }
/// <summary>
/// Domain Isolation
/// </summary>
bool IsDomainIsolated { get; set; }
/// <summary>
/// The isolated domain url
/// </summary>
string IsolatedDomainUrl { get; set; }
/// <summary>
/// App id of a multi-tenant app
/// </summary>
string MultiTenantAppId { get; set; }
/// <summary>
/// The reply url of the multi-tenant app
/// </summary>
string MultiTenantAppReplyUrl { get; set; }
/// <summary>
/// Name of the package approver
/// </summary>
string PackageApproverName { get; set; }
/// <summary>
/// Name of the package
/// </summary>
string PackageName { get; set; }
/// <summary>
/// Version of the package
/// </summary>
string PackageVersion { get; set; }
/// <summary>
/// The requested resource
/// </summary>
string Resource { get; set; }
/// <summary>
/// Id of the requested resource
/// </summary>
string ResourceId { get; set; }
/// <summary>
/// Permission scope
/// </summary>
string Scope { get; set; }
/// <summary>
/// Requested timestamp
/// </summary>
DateTime TimeRequested { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Threading.Tasks;

namespace PnP.Core.Admin.Model.SharePoint
{
/// <summary>
/// Manage the SharePoint apps service principal
/// </summary>
public interface IServicePrincipal
{
/// <summary>
/// Approves the specified permission request
/// </summary>
/// <param name="id">permission request id</param>
/// <param name="vanityUrlOptions">Optionally specify the custom vanity URI's used by this tenant</param>
/// <returns>permission grant</returns>
IPermissionGrant ApprovePermissionRequest(string id, VanityUrlOptions vanityUrlOptions = null);

/// <summary>
/// Approves the specified permission request
/// </summary>
/// <param name="id">permission request id</param>
/// <param name="vanityUrlOptions">Optionally specify the custom vanity URI's used by this tenant</param>
/// <returns>permission grant</returns>
Task<IPermissionGrant> ApprovePermissionRequestAsync(string id, VanityUrlOptions vanityUrlOptions = null);

/// <summary>
/// Denies the specified permission request
/// </summary>
/// <param name="id">permission request id</param>
/// <param name="vanityUrlOptions">Optionally specify the custom vanity URI's used by this tenant</param>
void DenyPermissionRequest(string id, VanityUrlOptions vanityUrlOptions = null);

/// <summary>
/// Denies the specified permission request
/// </summary>
/// <param name="id">permission request id</param>
/// <param name="vanityUrlOptions">Optionally specify the custom vanity URI's used by this tenant</param>
Task DenyPermissionRequestAsync(string id, VanityUrlOptions vanityUrlOptions = null);

/// <summary>
/// Lists pending permission requests
/// </summary>
/// <param name="vanityUrlOptions">Optionally specify the custom vanity URI's used by this tenant</param>
/// <returns>the list of permission requests</returns>
List<IPermissionRequest> GetPermissionRequests(VanityUrlOptions vanityUrlOptions = null);

/// <summary>
/// Lists pending permission requests
/// </summary>
/// <param name="vanityUrlOptions">Optionally specify the custom vanity URI's used by this tenant</param>
/// <returns>the list of permission requests</returns>
Task<List<IPermissionRequest>> GetPermissionRequestsAsync(VanityUrlOptions vanityUrlOptions = null);
}
}
Loading