public void CanProvisionHierarchy() { XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var tenantContext = TestCommon.CreateTenantClientContext()) { var applyingInformation = new ProvisioningTemplateApplyingInformation(); var tenant = new Tenant(tenantContext); result = CanProvisionRulesManager.CanProvision(tenant, hierarchy, String.Empty, applyingInformation); } } Assert.IsNotNull(result); Assert.IsTrue(result.CanProvision); }
public void CanProvisionSite() { if (TestCommon.AppOnlyTesting()) { Assert.Inconclusive("Template requires term store work, so this will not work in app-only"); } XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var context = TestCommon.CreateClientContext()) { var applyingInformation = new ProvisioningTemplateApplyingInformation(); result = CanProvisionRulesManager.CanProvision(context.Web, hierarchy.Templates[0], applyingInformation); } } Assert.IsNotNull(result); #if ONPREMISES // Because the "apps" rule is verified here Assert.IsFalse(result.CanProvision); #else Assert.IsTrue(result.CanProvision); #endif }
public void CanProvisionHierarchy() { if (TestCommon.AppOnlyTesting()) { Assert.Inconclusive("Template requires term store work, so this will not work in app-only"); } XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); var applyingInformation = new ProvisioningTemplateApplyingInformation(); if (TestCommon.AppOnlyTesting()) { bool templateSupportsAppOnly = true; if (applyingInformation.HandlersToProcess.Has(PnP.Framework.Provisioning.Model.Handlers.TermGroups) || applyingInformation.HandlersToProcess.Has(PnP.Framework.Provisioning.Model.Handlers.SearchSettings)) { if (hierarchy.Templates.Count > 0) { foreach (var template in hierarchy.Templates) { templateSupportsAppOnly = this.IsTemplateSupportedForAppOnly(template); if (!templateSupportsAppOnly) { break; } } } } if (!templateSupportsAppOnly) { Assert.Inconclusive("Taxonomy and SearchSettings tests are not supported when testing using app-only context."); } } CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var tenantContext = TestCommon.CreateTenantClientContext()) { var tenant = new Tenant(tenantContext); result = CanProvisionRulesManager.CanProvision(tenant, hierarchy, String.Empty, applyingInformation); } } Assert.IsNotNull(result); Assert.IsTrue(result.CanProvision); Assert.IsTrue(result.CanProvision); }
public void CanProvisionSite() { if (TestCommon.AppOnlyTesting()) { Assert.Inconclusive("Template requires term store work, so this will not work in app-only"); } XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); var applyingInformation = new ProvisioningTemplateApplyingInformation(); var template = hierarchy.Templates[0]; if (TestCommon.AppOnlyTesting()) { if (applyingInformation.HandlersToProcess.Has(Core.Framework.Provisioning.Model.Handlers.TermGroups) || applyingInformation.HandlersToProcess.Has(Core.Framework.Provisioning.Model.Handlers.SearchSettings)) { bool templateSupportsAppOnly = this.IsTemplateSupportedForAppOnly(template); if (!templateSupportsAppOnly) { Assert.Inconclusive("Taxonomy and SearchSettings tests are not supported when testing using app-only context."); } } } CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var context = TestCommon.CreateClientContext()) { result = CanProvisionRulesManager.CanProvision(context.Web, hierarchy.Templates[0], applyingInformation); } } Assert.IsNotNull(result); #if SP2013 || SP2016 // Because the "apps" rule is verified here Assert.IsFalse(result.CanProvision); #else Assert.IsTrue(result.CanProvision); #endif }
public async Task <JsonResult> CanProvisionPackage(CanProvisionModel model) { if (String.IsNullOrEmpty(model.PackageId)) { throw new ArgumentNullException("PackageId"); } if (String.IsNullOrEmpty(model.TenantId)) { throw new ArgumentNullException("TenantId"); } if (String.IsNullOrEmpty(model.UserPrincipalName)) { throw new ArgumentNullException("UserPrincipalName"); } CanProvisionResult canProvisionResult = await CanProvisionInternal(model); return(Json(canProvisionResult)); }
private async Task <CanProvisionResult> CanProvisionInternal(CanProvisionModel model) { var canProvisionResult = new CanProvisionResult(); String provisioningScope = ConfigurationManager.AppSettings["SPPA:ProvisioningScope"]; String provisioningEnvironment = ConfigurationManager.AppSettings["SPPA:ProvisioningEnvironment"]; var tokenId = $"{model.TenantId}-{model.UserPrincipalName.ToLower().GetHashCode()}-{provisioningScope}-{provisioningEnvironment}"; var graphAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, "https://graph.microsoft.com/"); // Retrieve the provisioning package from the database and from the Blob Storage var context = dbContext; DomainModel.Package package = null; // Get the package if (ProvisioningAppManager.IsTestingEnvironment) { // Process all packages in the test environment package = context.Packages.FirstOrDefault(p => p.Id == new Guid(model.PackageId)); } else { // Process not-preview packages in the production environment package = context.Packages.FirstOrDefault(p => p.Id == new Guid(model.PackageId) && p.Preview == false); } if (package != null) { // Retrieve parameters from the package/template definition var packageFileUrl = new Uri(package.PackageUrl); var packageLocalFolder = packageFileUrl.AbsolutePath.Substring(1, packageFileUrl.AbsolutePath.LastIndexOf('/') - 1); var packageFileName = packageFileUrl.AbsolutePath.Substring(packageLocalFolder.Length + 2); ProvisioningHierarchy hierarchy = GetHierarchyFromStorage(packageLocalFolder, packageFileName); // If we have the hierarchy if (hierarchy != null) { var accessTokens = new Dictionary <String, String>(); AuthenticationManager authManager = new AuthenticationManager(); var ptai = new ProvisioningTemplateApplyingInformation(); // Retrieve the SPO URL for the Admin Site var rootSiteUrl = model.SPORootSiteUrl; // Retrieve the SPO Access Token for SPO var spoAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, rootSiteUrl, ConfigurationManager.AppSettings["ida:ClientId"], ConfigurationManager.AppSettings["ida:ClientSecret"], ConfigurationManager.AppSettings["ida:AppUrl"]); // Store the SPO Access Token for any further context cloning accessTokens.Add(new Uri(rootSiteUrl).Authority, spoAccessToken); // Define a PnPProvisioningContext scope to share the security context across calls using (var pnpProvisioningContext = new PnPProvisioningContext(async(r, s) => { if (accessTokens.ContainsKey(r)) { // In this scenario we just use the dictionary of access tokens // in fact the overall operation for sure will take less than 1 hour // (in fact, it's a matter of few seconds) return(await Task.FromResult(accessTokens[r])); } else { // Try to get a fresh new Access Token var token = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, $"https://{r}", ConfigurationManager.AppSettings["ida:ClientId"], ConfigurationManager.AppSettings["ida:ClientSecret"], ConfigurationManager.AppSettings["ida:AppUrl"]); accessTokens.Add(r, token); return(token); } })) { // If the user is an admin (SPO or Tenant) we run the Tenant level CanProvision rules if (model.UserIsSPOAdmin || model.UserIsTenantAdmin) { // Retrieve the SPO URL for the Admin Site var adminSiteUrl = model.SPORootSiteUrl.Replace(".sharepoint.com", "-admin.sharepoint.com"); // Retrieve the SPO Access Token for the Admin Site var spoAdminAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, adminSiteUrl, ConfigurationManager.AppSettings["ida:ClientId"], ConfigurationManager.AppSettings["ida:ClientSecret"], ConfigurationManager.AppSettings["ida:AppUrl"]); // Store the SPO Admin Access Token for any further context cloning accessTokens.Add(new Uri(adminSiteUrl).Authority, spoAdminAccessToken); // Connect to SPO Admin Site and evaluate the CanProvision rules for the hierarchy using (var tenantContext = authManager.GetAzureADAccessTokenAuthenticatedContext(adminSiteUrl, spoAdminAccessToken)) { using (var pnpTenantContext = PnPClientContext.ConvertFrom(tenantContext)) { // Creat the Tenant object for the current SPO Admin Site context TenantAdmin.Tenant tenant = new TenantAdmin.Tenant(pnpTenantContext); // Run the CanProvision rules against the current tenant canProvisionResult = CanProvisionRulesManager.CanProvision(tenant, hierarchy, null, ptai); } } } else { // Otherwise we run the Site level CanProvision rules // Connect to SPO Root Site and evaluate the CanProvision rules for the hierarchy using (var clientContext = authManager.GetAzureADAccessTokenAuthenticatedContext(rootSiteUrl, spoAccessToken)) { using (var pnpContext = PnPClientContext.ConvertFrom(clientContext)) { // Run the CanProvision rules against the root site canProvisionResult = CanProvisionRulesManager.CanProvision(pnpContext.Web, hierarchy.Templates[0], ptai); } } } } } } else { throw new ApplicationException("Invalid request, the requested package/template is not available!"); } return(canProvisionResult); }
public override CanProvisionResult CanProvision(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation applyingInformation) { // Prepare the default output var result = new CanProvisionResult(); #if !ONPREMISES Model.ProvisioningTemplate targetTemplate = null; if (template.ParentHierarchy != null) { // If we have a hierarchy, search for a template with ALM settings, if any targetTemplate = template.ParentHierarchy.Templates.FirstOrDefault(t => t.ApplicationLifecycleManagement.Apps.Count > 0 || (t.ApplicationLifecycleManagement.AppCatalog != null && t.ApplicationLifecycleManagement.AppCatalog.Packages.Count > 0)); if (targetTemplate == null) { // or use the first in the hierarchy targetTemplate = template.ParentHierarchy.Templates[0]; } } else { // Otherwise, use the provided template targetTemplate = template; } // Verify if we need the App Catalog (i.e. the template contains apps or packages) if ((targetTemplate.ApplicationLifecycleManagement?.Apps != null && targetTemplate.ApplicationLifecycleManagement?.Apps?.Count > 0) || (targetTemplate.ApplicationLifecycleManagement?.AppCatalog != null && targetTemplate.ApplicationLifecycleManagement?.AppCatalog?.Packages != null && targetTemplate.ApplicationLifecycleManagement?.AppCatalog?.Packages.Count > 0) || (targetTemplate.ParentHierarchy != null && targetTemplate.ParentHierarchy?.Tenant?.AppCatalog != null && targetTemplate.ParentHierarchy?.Tenant?.AppCatalog?.Packages != null && targetTemplate.ParentHierarchy?.Tenant?.AppCatalog?.Packages.Count > 0)) { // First of all check if the currently connected user is a Tenant Admin if (!TenantExtensions.IsCurrentUserTenantAdmin(web.Context as ClientContext)) { result.CanProvision = false; result.Issues.Add(new CanProvisionIssue() { Source = this.Name, Tag = CanProvisionIssueTags.USER_IS_NOT_TENANT_ADMIN, Message = CanProvisionIssuesMessages.User_Is_Not_Tenant_Admin, ExceptionMessage = null, // Here we don't have any specific exception ExceptionStackTrace = null, // Here we don't have any specific exception }); } using (var scope = new PnPMonitoredScope(this.Name)) { // Try to access the AppCatalog var appCatalogUri = web.GetAppCatalog(); if (appCatalogUri == null) { // And if we fail, raise a CanProvisionIssue result.CanProvision = false; result.Issues.Add(new CanProvisionIssue() { Source = this.Name, Tag = CanProvisionIssueTags.MISSING_APP_CATALOG, Message = CanProvisionIssuesMessages.Missing_App_Catalog, ExceptionMessage = null, // Here we don't have any specific exception ExceptionStackTrace = null, // Here we don't have any specific exception }); } else { // Try to access the AppCatalog with the current user try { using (var appCatalogContext = web.Context.Clone(appCatalogUri)) { // Get a reference to the "Apps for SharePoint" library var appCatalogLibrary = appCatalogContext.Web.GetListByUrl("AppCatalog"); // Check its permissions appCatalogContext.Web.CurrentUser.EnsureProperty(u => u.LoginName); var userEffectivePermissions = appCatalogLibrary.GetUserEffectivePermissions( appCatalogContext.Web.CurrentUser.LoginName); appCatalogContext.ExecuteQueryRetry(); if (!userEffectivePermissions.Value.Has(PermissionKind.EditListItems)) { throw new SecurityException("Invalid user's permissions for the AppCatalog"); } // we seem to have access, but is it done fully provisioning? var rootFolder = appCatalogContext.Web.EnsureProperty(w => w.RootFolder); var timeCreated = rootFolder.TimeCreated; if (DateTime.UtcNow.Subtract(timeCreated).Hours < 2) { result.CanProvision = false; result.Issues.Add(new CanProvisionIssue() { Source = this.Name, Tag = CanProvisionIssueTags.APP_CATALOG_NOT_YEY_FULLY_PROVISIONED, Message = CanProvisionIssuesMessages.App_Catalog_Not_Yet_Fully_Provisioned, ExceptionMessage = null, // Here we don't have any specific exception ExceptionStackTrace = null, // Here we don't have any specific exception }); } } } catch (Exception ex) { // And if we fail, raise a CanProvisionIssue result.CanProvision = false; result.Issues.Add(new CanProvisionIssue() { Source = this.Name, Tag = CanProvisionIssueTags.MISSING_APP_CATALOG_PERMISSIONS, Message = CanProvisionIssuesMessages.Missing_Permissions_for_App_Catalog, ExceptionMessage = ex.Message, ExceptionStackTrace = ex.StackTrace, }); } } } } #else result.CanProvision = false; #endif return(result); }
public override CanProvisionResult CanProvision(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation applyingInformation) { // Prepare the default output var result = new CanProvisionResult(); #if !ONPREMISES Model.ProvisioningTemplate targetTemplate = null; if (template.ParentHierarchy != null) { // If we have a hierarchy, search for a template with Taxonomy settings, if any targetTemplate = template.ParentHierarchy.Templates.FirstOrDefault(t => t.TermGroups.Count > 0); if (targetTemplate == null) { // or use the first in the hierarchy targetTemplate = template.ParentHierarchy.Templates[0]; } } else { // Otherwise, use the provided template targetTemplate = template; } // Verify if we need the Term Store permissions (i.e. the template contains term groups to provision, or sequences with TermStore settings) if ((targetTemplate.TermGroups != null && targetTemplate.TermGroups?.Count > 0) || targetTemplate.ParentHierarchy.Sequences.Any( s => s.TermStore?.TermGroups != null && s.TermStore?.TermGroups?.Count > 0)) { using (var scope = new PnPMonitoredScope(this.Name)) { try { // Try to access the Term Store TaxonomySession taxSession = TaxonomySession.GetTaxonomySession(web.Context); TermStore termStore = taxSession.GetDefaultKeywordsTermStore(); web.Context.Load(termStore, ts => ts.Languages, ts => ts.DefaultLanguage, ts => ts.Groups.Include( tg => tg.Name, tg => tg.Id, tg => tg.TermSets.Include( tset => tset.Name, tset => tset.Id))); var siteCollectionTermGroup = termStore.GetSiteCollectionGroup((web.Context as ClientContext).Site, false); web.Context.Load(siteCollectionTermGroup); web.Context.ExecuteQueryRetry(); var termGroupId = Guid.NewGuid(); var group = termStore.CreateGroup($"Temp-{termGroupId.ToString()}", termGroupId); termStore.CommitAll(); web.Context.Load(group); web.Context.ExecuteQueryRetry(); // Now delete the just created termGroup, to cleanup the Term Store group.DeleteObject(); web.Context.ExecuteQueryRetry(); } catch (Exception ex) { // And if we fail, raise a CanProvisionIssue result.CanProvision = false; result.Issues.Add(new CanProvisionIssue() { Source = this.Name, Tag = CanProvisionIssueTags.MISSING_TERMSTORE_PERMISSIONS, Message = CanProvisionIssuesMessages.Term_Store_Not_Admin, ExceptionMessage = ex.Message, // Here we have a specific exception ExceptionStackTrace = ex.StackTrace, // Here we have a specific exception }); } } } #else result.CanProvision = false; #endif return(result); }