by Martin Laukkanen | Jul 25, 2014 | Apps, SharePoint 2013, Troubleshooting
Recently while working with a colleague we came across a few easy to miss issues when configuring the App Management Service in a SharePoint farm and while troubleshooting those issues found a lack of online resources on what turned out to be super-simple issues to resolve. (Hindsight is great)
Issue 1: ‘Let’s try this again’ error installing from the App Store
After installing and configuring the services, app domain and catalog as required, we could now open the SharePoint App Store, and choose an app to install, however the following message was displayed and the installation of any App would never begin.
Error text: Everything is fine, but we had a small problem getting your license. Please go back to the SharePoint Store to get this app again and you won’t be charged for it.
Everything’s fine hey? Looking at the ULS revealed that this was caused by security:
w3wp.exe (0x0CA0) 0x1DF4 SharePoint Foundation App Marketplace High An exception was thrown while trying to import the app license. AppName:Bulk Edit; Exception:System.UnauthorizedAccessException: You don’t have the right to perform this operation. at Microsoft.SharePoint.SPAppLicenseManager.ImportLicense …
w3wp.exe (0x0CA0) 0x1DF4 SharePoint Foundation App Marketplace High Post app license acquisition did not result in a successful license import. HugState:Retry
But how can this be, we’re testing using the Farm Admin account and so we have all permissions! It turns out, that is actually the problem! While I can’t say specifically where / what setting causes this, in a typical SharePoint 2013 install the Farm Admin (/ Installer account) is intentionally restricted in certain ways, my guess is something to do with local computer policies or such.
Solution
Login using a user account (who can be Site Collection Admin and all that)!
Yay my favourite app!
Issue 2: App installs without issue but does not work
Now we have our first app installed, however running it doesn’t quite work out. Unfortunately this time we get very little indication of the problem, in fact with Bulk Edit the app will start and look good, except nothing works! The buttons do nothing, and no data is displayed! :(
Similar issues happen with other apps installed and without looking at the F12 browser debug console the only indication that there is a problem is the top right hand corner site icon image which is just a little ‘x’.
If you do debug you will see an error like this:
SCRIPT5009: ‘RegisterSod’ is undefined
File: Default.aspx, Line: 14, Column: 32
Basically that tells me as a developer that something is seriously wrong in the farm (SP.js is missing which the whole JSOM api requires)!
Cause & Solution
Fortunately the solution was simple: SP.js was actually missing! Well not exactly, as it turns out while we had provisioned a single PWA site collection at /PWA in our Web App, we had not actually created a Default Root site collection at ‘/’. This configuration actually can break lots of things including SSRS and so this comes as no surprise really.
Simply creating a root site collection (using Blank or any template) fixed the issue and now all apps work as normal.
Other potential issues
While possible the topic of another rather long post, here’s a list of other things to check if you get permissions issues trying to install apps (sorry this is from memory so pls comment below if you think any of this has changed):
Hope that helps someone else out there!
by Martin Laukkanen | Dec 9, 2013 | Development, How to, SharePoint 2013
This is one of the new features of SharePoint 2013 that I have been looking forward to trying out since I first read about it; as someone who has often uses JavaScript to enhance the usability of Project Server JSLink is a perfect feature to make these customisation’s simply and in a supportable way.
JSLink in Action
Check out my Exposure column on an otherwise default Project Site risk list! :)
JSLink enables the client-side rendering to be customised with just about any JavaScript or html changes that you think of, and better yet not only does it apply to views, but also New and Edit forms. For me that means I will probably never suggest InfoPath forms to a customer again!
Example Updating the Project Site Template
The screenshot above shows a simple example of a JSLink script configured on the project site out-of-the-box Risks list, to demonstrate how to do that quickly and easily against an existing list (or your Project Workspace Site template), first you need a bit of JavaScript:
JSLink script riskColor.js
Type.registerNamespace('CustomFormat');
CustomFormat.riskColor = function () {
var riskFieldsContext = {};
riskFieldsContext.Templates = {};
riskFieldsContext.Templates.Fields = {
"Exposure": { "View": CustomFormat.exposureColourTemplate }
};
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(riskFieldsContext);
}
// This function provides the rendering logic for list view
CustomFormat.exposureColourTemplate = function(ctx) {
var fieldValue = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];
if (parseFloat(fieldValue) > 7) {
return "<span style='background-color : red'> </span> High (" + fieldValue + ")";
}
else if (parseFloat(fieldValue) > 4) {
return "<span style='background-color : gold'> </span> Medium (" + fieldValue + ")";
}
else {
return "<span style='background-color : green'> </span> Low (" + fieldValue + ")";
}
}
//CSR-override for MDS disabled site
CustomFormat.riskColor();
if (typeof _spPageContextInfo != "undefined" && _spPageContextInfo != null) {
// CSR-override for MDS enabled site
RegisterModuleInit(_spPageContextInfo.siteServerRelativeUrl + "/SiteCollectionDocuments/riskColor.js", CustomFormat.riskColor);
}
In summary what is happening is the following:
On line 8 inside the CustomFormat.riskColor() function I define the field names which we want to customise and specify the formatting callback function for the type of customisation, in this case we are customising the “View” but we could use “DisplayForm”, “NewForm” or “EditForm” here also.
Two things to note here, firstly make sure you use the Internal Name of your field, so for instance if you created a custom column called “Risk Rating” then that would be “Risk_x0020_Rating”, secondly you can specify as many fields / columns by name here as you want each with a separate formatting function.
For more details on JSLink and all the other options available I’d recommend the following reading:
Next from line 19 to 26 in the CustomFormat.exposureColorTemplate() function I simply return our modified HTML based on the value of the field in question, in this example I’m comparing the number against three arbitrary values for High, Medium and Low, then returning a html string including a colour and some text to emphasize the value.
Finally and thanks to Wictor Wilen for his article on fixing the issues caused by MDS (SharePoint’s Minimal Download Strategy feature) I’m registering and then executing the script properly when called.
Registering the JSLink Script
Now that we have our script, we just need to register it in our site template, so to do so first save your script somewhere central in your site collection, I personally like Site Collection Documents under PWA, but wherever it is make sure everyone has access.
Secondly open your Risks list view and Edit Page from the SharePoint menu. With the page in Edit mode click the dropdown arrow for the Risks list web part and select Edit Web Part:
Now in the Web Part Properties expand Miscellaneous and locate the JS Link field:
The full path I’m using is:
~sitecollection/SiteCollectionDocuments/riskColor.js
This path also is referenced at the bottom of the script as _spPageContextInfo.siteServerRelativeUrl + “/SiteCollectionDocuments/riskColor.js” so make sure to set both correctly.
NOTE: I’ve found this step is critical, the “~sitecollection” token is REQUIRED in the web part misc properties! At least in my case I was able to consistently cause IE to throw script errors if I tried to use a relative path like /PWA/SiteCollectionDocuments/riskColor.js! It did work in Chrome so maybe it is something in IE? Either way you can replace that with ~site ,or ~layouts as required.
Finally save your webpart configuration and it should immediately work.
Final Words
A few things; firstly double check the URL using in the Web Part properties and make certain to specify the path correctly in both the script and the web part properties. If you noticed the views fail (I did many times) then double check the note above about the ~sitecollection token.
Secondly because we are using the out-of-the-box Risks list we must add this JS Link script reference to every view and web part display of our Risks list.
Thirdly clear your browser cache! This one had me stumped as it seemed like none of my changes were working when in fact the script changes were not being refreshed, I found disabling the cache (easy in F12 mode in Chrome) made testing this super easy.
Finally for a bunch of cool examples of JSLink in action don’t forget to have a look at:
http://code.msdn.microsoft.com/office/Client-side-rendering-JS-2ed3538a
I hope you find this useful.
by Martin Laukkanen | Jul 23, 2013 | Apps, Development, How to, SharePoint 2013
One of the noticeable gaps that comes up immediately when you start planning any significant SharePoint 2013 deployment with requirements such as multi-tenancy and SAML based authentication (ADFS, ACS, etc) are the some of the limitations with the new features of 2013.
One such limitation is the new App Store which only supported Windows Authentication and didn’t support hostheaders at RTM! Fortunately Microsoft fixed the hostheader limitation in the March PU release (KB2768001), however the SAML limitation remains.
Steve Peschka wrote about one solution to part of this problem here: Using SharePoint Apps with SAML and FBA Sites in SharePoint 2013, however that only covers the Provider hosted (or Autohosted) apps, which leaves a big gaping hole where the simplest Apps of all are not supported.
The Problem with SharePoint hosted Apps
Basically the problem is in the the App Domain configuration and authentication requirements, take a typical example:
- First tenant:
App Domain: https://tenant1-*******.contosoapps.com
- Second tenant:
App Domain: https://tenant2-*******.contosoapps.com
As you know those App Domains are auto created for each installed app with a unique name per app. (Note that you need March PU to configure the above App Domains)
So with that in mind, when a user is authenticated to tenant1.contoso.com that does not automatically authenticate them to tenant1-******.contosoapps.com forcing re-authentication, that’s where we hit our problem.
Azure ACS and ADFS – No Apps??
Unfortunately Azure ACS and ADFS don’t work at all in this scenario. The problem:
UseWReplyParameter while supported by both is restricted to sub sites only (e.g. reply to , as a result in our scenario above what you end up with is this annoying authentication loop:
The reason for this is for security, as allowing any arbitrary WReply parameter could potentially allow an attacker from one Relaying Party (RP) to be redirected with a valid authentication token to a second RP.
So in my scenario above it may be possible for tenant1 users to authenticate not only with tenant1*** apps, but also tenant2 sites!
Solutions?
Unless Microsoft changes ACS and ADFS (unlikely) or they modify the SPTrustedIdentityTokenIssuer (maybe?), then the only option right now is to roll your own STS Identity Provider based on the the Windows Identity Framework SDK!
Fortunately you’re not alone, plenty of examples of this exist;
And my favourite:
Lots of good examples to work with but for this blog I’m going to extend the last one on the list by Steve Peschka using SAML with Microsoft Accounts (LiveID). So if you want to implement my changes below, you’ll need to start by reading the last article above and download and get familiar with the source for that one.
Changes to WindowsLiveOauthSts Solution
In summary we need to make the following changes to the solution:
- Firstly we need to update the Custom STS to accept and use the WReplyParameter.
- Secondly we need to capture the original request WReplyParameter in the PassiveSTS.cs and send it to the Custom STS.
- Finally we need to ensure that we maintain the security of our solution in with all changes.
That’s it, simple hey?
Updates to CustomSecurityTokenService.cs
First add somewhere to store the parameter to the class:
protected String WReplyParameter { get; set; }
Next extend the constructor to accept the parameter:
public CustomSecurityTokenService(CustomSecurityTokenServiceConfiguration configuration,
Dictionary<string, string> ClaimValues, String wReplyParameter = "")
: base(configuration)
{
this.oAuthValues = ClaimValues;
this.WReplyParameter = wReplyParameter;
}
And finally use the saved value in the GetScope() method:
// Set the ReplyTo address for the WS-Federation passive protocol (wreply).
// Use the provided WReplyParameter if it exists
if (String.IsNullOrEmpty(WReplyParameter))
scope.ReplyToAddress = scope.AppliesToAddress;
else
scope.ReplyToAddress = WReplyParameter;
return scope;
Updates to PassiveSTS.cs
First we have to get the Query String parameter from the request:
string wReplyParameter = HttpUtility.ParseQueryString(HttpUtility.UrlDecode(state))["wreply"];
Then we make sure to pass the parameter to the CustomSTS when instantiated:
//create an instance of our sts and pass in the dictionary of values we got from Windows Live oAuth
SecurityTokenService sts =
new CustomSecurityTokenService(CustomSecurityTokenServiceConfiguration.Current, values,
wReplyParameter);
Security Considerations
The risk arises if we allow any ‘wreply’ parameter to be be used as our returned ReplyToAddress in the Custom STS, in this example implementation a simple validation is included using an array of allowed URL’s (unmodified code quoted here);
// TODO: Set enableAppliesToValidation to true to enable only the RP Url's specified in the
// PassiveRedirectBasedClaimsAwareWebApps array to get a token from this STS
static bool enableAppliesToValidation = false;
Obviously for a production multi-tenanted environment you would need something more sophisticated, but I’ll leave that to you. Also another thing that particular solution does not allow for is our dynamically created App Domains, so that too will require some changes.
But hang on..
However it is worth saying here that in our simple example when using only Live ID across all Web Apps we have not exposed anything extra (yet) with this change! Think about it; if an attacker in wants to login to Tenant2 then all they need to do is open and login! Our STS is passing only identity and user related claims so all of the securing of resources is left to the tenant. IE; John from Tenant 1 has no site collection SPUser rights to Tenant 2 sites! So as it stands this scenario is similar to an equivalent Azure ACS implementation using multiple Relaying Parties.
Final Words
It’s worth mentioning that this likely will become redundant after a future service pack, assuming Microsoft fixes this by changing SharePoint that is.
A good indication is to look at how Office365’s doing it right now, and a quick Fiddler trace shows that the two cookies (rtFa and FedAuth) are passed directly to the AppDomain from the tenant Web App, so clearly MS is handling this in the initial Auth.
That may be a topic for investigation another time.
Hope this is useful for someone out there.