Saturday, March 23, 2019

Beware: the omnipresent of Office 365 Groups complicates Information Architecture

Pre Office 365, setting up your information architecture was so much simpler (to understand)...

So your organization has implemented Office 365 as office productivity suite, which provides to you the richness in its various services / tools to employ. Say as a business department I need:
  • A place to store documents ➔ create in SharePoint Online a Modern Site
  • A place to work and chat together ➔ create a MS Teams instance
  • A place to plan work ➔ create a plan in MS Planner
  • A place to store and expose department videos ➔ create in MS Stream a Stream Group
  • A shared mailbox ➔ create via Outlook an Office 365 Group
However, one of the result of these 5 create actions in different Office 365 Services is that you do not have one (1) SharePoint site to your exposure, but actually five (5)!
And all of these 5 Modern Sites are created alike: same structure in the site, layout; all a standard provisioned Modern Site
And also in Azure AD the same multiplication effect is visible, noticable as an Azure AD Group (actually: Office 365 Groups):
Lesson: you need to think upfront on your Information Architecture, and then setup your utilization of the Office 365 services that it matches. Beware that this governance is hard to achieve, as all the creation in Office 365 is via self-service made so easy...

Thursday, March 14, 2019

Beware: User Information List (UIL) acts as memory for PeoplePicker when need to renew Azure AD B2B guest account for changed email

In Azure AD B2B, guest accounts are provisioned and redeemed with as their unique external account identifier, the own external email address of the invited guests. In rare situation that the external email changes, the guest account situation needs to be synchronized with this external identity change. However, as the email address is the primary identifier, it is not possible to change this as attribute in the existing Azure AD guest account. One needs to create in the Azure AD of the inviting company a new guest account based on the new / modified external email address, and potentially can also delete the no longer valid old guest account. On the SharePoint Online side, in each site collection in which the guest needs access, the access must also be repeated for the new guest account. Something to be aware of in such situation is the relationship between SharePoint PeoplePicker and the hidden User Information List (UIL) in site collection. The PeoplePicker uses UIL as one of its entrances to try to resolve user information for looked-up user name. As of SharePoint 2013, whenever an account on individual basis is granted access to a SharePoint site collection, immediate an entry for that account is administrated in the UIL (in SharePoint 2010 and before, the addition in UIL was delayed until first access by the authorized account). The effect in the use case where guest account is renewed by deleting from and creating new, is that the PeoplePicker default will still resolve to the user entry in UIL that was created there when earlier the guest was authorized on the previous guest account. Resulting in an inconsistent situation: the guest is assigned a new Azure AD guest account, the former might even be deleted in Azure AD, but in SharePoint Online via UIL as memory the previous account is selected by PeoplePicker upon granting access. Effect is that the guest can successful logon with the renewed guest account against the Azure AD of inviting company, but in the next step this authenticated guest account is not granted access in the SharePoint Online site: "You need permission to access this site". The way to resolve this is to go as Site Collection Administrator (SCA) into the UIL through <site-url>/_layouts/15/people.aspx?MembershipGroupId=0 , select the entry that refers to the previous guest account, and in the 'Actions' menu select "Delete users from Site Collection". After this corrective management action, the PeoplePicker on looking up the guest (by name or email) will no longer find the stale entry in the UIL, and create a new one that corresponds correct with the renewed guest account. And the invited Azure AD guest is enabled to successful access the site via modified email address as his/her 'bring-your-own-identity'.

Wednesday, March 13, 2019

Peculiarity with Azure AD B2B and Modern / Active Authentication for guest accounts

In the authentication model of Azure AD B2B, external guests are granted access on concept of 'bring-your-own-identity'. The general applied pattern in AAD B2B usage is to invite guests on their email address as the own identity. For invited guests from an organization that itself utilizes Azure AD for their cloud identity management, in AAD B2B the authentication flow delegates to the external Azure AD (see). This can result in a peculiarity, in which the invited guests after successful first authentication that is federated against the own external Azure AD, in the 2nd step cannot successful pass the MFA challenge via the Microsoft Authenticator App against the Azure AD of the inviting company. The situation arises where in the external Azure AD (of the invited organization) the UserPrincipalName (UPN) differs from the primary Office 365 email address, and the external access is via a client application that utilizes modern / active authentication. In such situation, the inviting company should invite the guest on basis of their Azure AD UPN, and not via their different Office 365 email / SMTP address. For client applications that apply passive authentication (aka: webapplications) it works fine to explicit logon with the own email as their Office 365 identity. But clients that utilize active authentication get into a conflict situation: the automated logon in active authentication flow against the external Azure AD is done on basis of the UPN, afterwards the authenticated account in the client application runtime context is equal to the UPN value of the logged-on guest. However, as the MFA enrollment is earlier done on basis of the email as guest identity, the result is an endless loop in the Authenticator App failing due mismatch in authenticated UPN logon and MFA-enrolled email.

Saturday, March 2, 2019

Execute 'Site information' (logo, privacy settings) on "Modern" team site requires to be Office 365 Group owner

Site information option in Modern team sites is the way to change the site logo and/or the privacy settings. This menu option + application popup dialog is available for accounts that are Site Collection Administrator (SCA). However, this is not enough. As "only" SCA I get the error "Problem uploading your picture. Please try again later." (see also uservoice on this: "Bug? Uploading team logo does not always work"). It appears that one needs to be an owner of the Office 365 Group that is connected to the Modern team site, then one can successfully change e.g. the site logo. Cause of this behavior is that "SharePoint Online team sites that are connected to an Office 365 group use the same logo as the Office 365 group to which they are connected. When you change the logo for your SharePoint group-connected team site, the logo for the corresponding Office 365 group will change also." (Manage your SharePoint site settings)

Sunday, February 10, 2019

'Check Permissions' displays 'Deny' for Azure AD B2B guest of domains that are not whitelisted

Individual authorized guests of non-whitelisted domains rightful denied access

SharePoint external sharing via Azure AD B2B adds an additional access-control layer. On top of the authorizations granted in the SharePoint site collection, you can explicitly control for guests whether their domain is allowed. Although I personally consider it a design flaw that business users can share their site with guests of non-whitelisted domains without warning that they are not actually allowed access, what eventually matters is that these invited guests will be blocked from accessing the site as long as it is not allowed on the higher authorisation-governance level of site collection sharing. The invited guests of non-whitelisted domains will get an Access Denied on trying to access the site for which they received a SharePoint invitation. Also when you as site owner check the permissions of such a guest, you will see both an allowed part for the authorizations granted via the site permissions, as well as a long list of 'Deny' due the lack of their guest domain within the list of whitelisted domains.
Domain of guest account not whitelisted in site collection sharing settings
Guest account can be invited to the site although its external domain not whitelisted
"Check Permissions" displays 'Deny'-permissions for guest accounts of non-whitelisted domain
"Check Permissions" displays for guest accounts of whitelisted domain only granted site-permissions
Noteworthy also is that the observed behavior is different depending on whether the guest invitation is redeemed or not. The above is for an invited Azure AD B2B guest account which has been activated already, and invited guest tries to access the SharePoint site. For non-redeemed guest account it is still possible to authorize to SharePoint site despite the domain not white-listed. But for non-redeemed, 'Check Permissions' displays "None", independent on whether the external domain is whitelisted.
Guest account can be invited to the site although its external domain not whitelisted

Saturday, February 2, 2019

Alternative to detach initial page loadtime from retrieving structural navigation

In the post 'Optimize for bad-performing navigation in SharePoint Online' I outlined an approach to release the initial pageload from the server-side execution time to retrieve the global site navigation. This approach serves its purpose to optimize the pageload time. However, it also comes with some aspects that complicate the usage. In particular in the perception of the site owner (maintainer): one thinks to configure the global navigation via "Site Settings \ Navigation", but next experience that it does not have immediate effect due the caching, nor is it complete in case one explicitly adds entries in the navigation that are not tied to the site structure itself. To address these drawbacks I designed + developed an alternative approach:
  1. Use webservice call /_api/navigation/menustate?mapprovidername='GlobalNavigationSwitchableProvider' to client-side request the navigation structure, exact as how it is configured by the site owner via "Site Settings \ Navigation'
  2. Cache the retrieved structure in the property bag available for all users
  3. Each time the Site Settings page is succesful loaded in browser, which implies it must be by someone with site maintenance authorization, redetermine via the webservice the global navigation structure, compare it with the one cached in property bag, and if changed replace the value in the property bag
Main advantage of this alternative compared to the 'search-driven' is that this restores for site owners the capability to manage the displayed global navigation via the out-of-the-box "Site Settings \ Navigation" configuration. And this includes the option to add specific entries also direct in that same configuration experience, no need to maintain a separate 'additional links' administration. Another advantage is that changes in the Navigation are now immediate applied, and for all users. No need to wait on client cache expiration. Which likely occurs on different moments for different visitors, and colleague X then can see a different menu from his/her colleague Y sitting on next desk. Also on code- and execution-level some improvements: the code is severly reduced + simplified, and there is no longer a dependency on Linq.js to runtime derive tree-model data structure from the flat web-nodes data structure determined via search.
Disadvantage is that the retrieved + cached global navigation is based on the authorizations of the person visiting the site settings page. In case some elements in the global navigation structure are restricted for other site visitors, they will now see those items in the global navigation. SharePoint authorization model prevents them from actually accessing the non-authorized content, however at the expense of a dissatisfying user experience: click on a menu item, and then get a 404 Access Denied. Therefore in case you have restrictive areas in the global navigation, then this approach might not be the one to use. A 2nd alternative is then to combine the 2: in essence, let each individual visitor request via the webservice the global navigation structure that (s)he is authorized to access, and cache this clientside for reuse on next visits. The drawback of this approach is that it suffers from risk of outdated navigation: if site owner makes changes to the navigation via "Site Settings \ Navigate", there is no trigger to enforce flush of all the individual site-visitors cached representations. But that can still be softened by putting a maximum on the cache duration, e.g. after 3 hours re-invoke the webservice to potential renew the navigation structure for you.

Sunday, January 20, 2019

Tip: how-to resolve in SharePoint 2016, XsltListViewWebPart displaying Calculated Fields as HTML-encoded

As of June 2017, Microsoft made a change to explicit block in XlstListViewWebPart the rendering of custom HTML markup in Calculated Fields: Handling HTML markup in SharePoint calculated fields: "Some users have added HTML markup or script elements to calculated fields. This is an undocumented use of the feature, and we will block the execution of custom markup in calculated fields in SharePoint Online from June 13, 2017 onwards. The June 2017 Public Update (PU) and later PUs will make blocking a configurable option for on-premises use in SharePoint Server 2016 and SharePoint Server 2013." In the article, Microsoft states that for SharePoint on-premises (2013 version and later) one can restore to the previous behavior through a new property on webapplication level: CustomMarkupInCalculatedFieldDisabled. For SharePoint Online there is no such configuration option provided.
A bit by coincidence (and also by mere perseverance 😉), I detected an alternative - undocumented - approach that does not require an overall webapplication setting. And as bonus, likely also works in SPO context. Default the XslLink property in XsltListViewWebPart XmlDefinition is configured via the View property: SPView.XslLink. The essence of the workaround / approach is to make use of the Xsl-behavior in the superclass of XsltListViewWebPart: DataFormWebPart. If you specify Xslt transformation at that level (either via XslLink, or via Xsl property), then you can configure for single XsltListViewWebPart instance that custom HTML markup of Calculated Field is still executed.
Although undocumented, it is an own Microsoft source that helped me discovering this => the SharePoint Escalation Service Team Blog - Guidance for XslLink property with the XsltListViewWebPart: "There exist other alternatives to let the XsltListViewWebPart use your XSL Transform. "
Note also that when you used 'Design' mode in SharePoint Designer 2010 to customize the Xslt-rendering of a list column, the customized Xslt-template is also injected in the Xsl property on webpart level.
My findings clarified through pictures:

At first the issue: custom HTML markup of Calculated Field is rendered as html-encoded

Move in the XMLDefinition the location of XslLink from 'View' to 'WebPart' level

And the rendering of custom HTML markup is restored, as we know it from before

Thing to be aware of: with using the 'XslLink' property it is a prerequisite that the applied Xslt stylesheet file is located in the layouts folder (see: Guidance for XslLink property with the XsltListViewWebPart: "however you would need to place your XSL file in the default “_layouts/xsl” folder"). In the above scenario that is guaranteed as I simply refer to the standard 'main.xsl' stylesheet that is by Microsoft itself provisioned in the layouts folder, both on-premises as online. If however you need a deviation from the standard XsltListViewWebPart rendering, then you need to provision your own custom Xsl stylesheet thus also in the layouts folder. Which in SharePoint Online context is of course not possible. If the deviation is only minimal, e.g. to override the standard rendering for only one column, then I advise to make use of the 'Xsl' property instead, and only include in it the overloading 'xsl:template'.
Example applied to scenario of above
<xsl>
   <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" 
      xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" 
      version="1.0" 
      exclude-result-prefixes="xsl msxsl ddwrt" 
      xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" 
      xmlns:asp="http://schemas.microsoft.com/ASPNET/20" 
      xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
      xmlns:SharePoint="Microsoft.SharePoint.WebControls" 
      xmlns:ddwrt2="urn:frontpage:internal" 
      xmlns:o="urn:schemas-microsoft-com:office:office">
         <xsl:include href="/_layouts/15/xsl/main.xsl"/>
         <xsl:include href="/_layouts/15/xsl/internal.xsl"/>
         <xsl:param name="AllRows" select="/dsQueryResponse/Rows/Row[$EntityName = '' or (position() >= $FirstRow and position() <= $LastRow)]"/>
         <xsl:param name="dvt_apos">'</xsl:param>                                                      
         <xsl:template match="FieldRef[@Name='Folder']" mode="Number_body">
            <xsl:param name="thisNode" select="."/>
            <b><i>
            <xsl:value-of disable-output-escaping="yes" select="$thisNode/@*[name()=current()/@Name]" />
            </i></b>
         </xsl:template>
   </xsl:stylesheet>
</xsl>