Esempio n. 1
0
        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);
        }
Esempio n. 2
0
        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
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        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));
        }
Esempio n. 6
0
        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);
        }