Adding Remote Event Receivers To An Exisiting List In Office 365

This is a generic approach in order to add event receivers to an existing list, say at the host or app web level really doesn’t matter. There are a couple of methods that make it nice and clean.

This first part will define a little helper method to build the creation information for the event receiver. Notice you have to specify the remote app url in your manifest tokens.

[csharp]

public static EventReceiverDefinitionCreationInformation BuildGenericEventRecieverWithoutEventType()
{
string remoteAppurl = HttpContext.Current.Request[“RemoteAppUrl”];
string remoteEventEndPointUrl = String.Format(“{0}/PutYourServiceNameHere.svc”, remoteAppurl);
EventReceiverDefinitionCreationInformation eventReceiver = new EventReceiverDefinitionCreationInformation();
eventReceiver.ReceiverName = “GiveYourRevc”;
eventReceiver.ReceiverUrl = remoteEventEndPointUrl;
eventReceiver.SequenceNumber = 5000;
eventReceiver.ReceiverClass = “Fully qualified Path To RER Assembly”;
eventReceiver.ReceiverAssembly = Assembly.GetExecutingAssembly().FullName;
return eventReceiver;
}

[/csharp]

If you are adding or removing a receiver, you of course have to test whether that receiver exists! That’s what the below method does. We just use the class name since that should be unique.

[csharp]

public static bool DoesEventReceiverDefintionExistBasedOnCreationInfo(EventReceiverDefinitionCreationInformation info, List list)
{
bool exists = false;
list.Context.Load(list, x => x.EventReceivers);
list.Context.ExecuteQuery();
foreach (EventReceiverDefinition eventReceiverDefinition in list.EventReceivers)
{
string innerDefId = eventReceiverDefinition.ReceiverClass;
string outerDefId = info.ReceiverClass;
if (innerDefId.Equals(outerDefId, StringComparison.InvariantCultureIgnoreCase))
{
exists = true;
break;
}
}
return exists;
}

[/csharp]

Since the receivers were built without a specific event type, you can specify as many receiver capture behaviors as you want, as demonstrated in the below example.

[csharp]

public static void ApplyEventReceiver(ClientContext context, List list)
{
EventReceiverDefinitionCreationInformation eventReceiver = Helpers.BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemAdded;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
eventReceiver = BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemUpdated;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
eventReceiver = BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemDeleted;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
eventReceiver = BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemFileMoved;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
context.ExecuteQuery();
}

[/csharp]

Unapplying event receivers is pretty easy to, but remember you are modifying the collection so you have to ensure that the enumeration can execute! That is why you see the two LINQ chains.

[csharp]

public static void UnApplyEventReceiver(ClientContext context, List list)
{
EventReceiverDefinitionCollection eventReceiverDefinitionCollection = list.EventReceivers;
context.Load(eventReceiverDefinitionCollection);
context.ExecuteQuery();
List ids = eventReceiverDefinitionCollection.Select(eventReceiverDefinition => eventReceiverDefinition.ReceiverId).ToList();
foreach (EventReceiverDefinition definition in ids.Select(eventReceiverDefinitionCollection.GetById))
{
definition.DeleteObject();
context.ExecuteQuery();
}
}

[/csharp]

Share

SharePoint And ADFS: SecurityTokenException – The issuer of the token is not a trusted issuer

This is a pretty common ADFS error, and there are all sorts of reasons that it could happen.

The stack trace will be this:

[code]

Microsoft.SharePoint.IdentityModel.SPTrustedIssuerNameRegistry.GetIssuerName(SecurityToken securityToken)

   at Microsoft.SharePoint.IdentityModel.SPPassiveIssuerNameRegistry.GetIssuerName(SecurityToken securityToken)

   at Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler.CreateClaims(SamlSecurityToken samlSecurityToken)

   at Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler.ValidateToken(SecurityToken token)

   at Microsoft.IdentityModel.Web.TokenReceiver.AuthenticateToken(SecurityToken token, Boolean ensureBearerToken, String endpointUri)

   at Microsoft.IdentityModel.Web.WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequest request)

   at Microsoft.IdentityModel.Web.WSFederationAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args)

   at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()

   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

[/code]

At the end of the day though, don’t sit around and fiddle with the SharePoint trusted authorities and yada yada yada, it boils down to a certificate problem. Basically the one that was specified as the signing certificate, when exported during the ADFS setup, is either malformed (the certificate chain is incomplete) or plainwrong wrong when the trusted issuer was being built up in SharePoint ala powershell. So to get around the error follow two pretty basic steps.

  1. Verify the appropriate certificate chain is present on the SharePoint server in both the trusted root authorities as well as in the SharePoint folder within the Certificate MMC snap-in. Never ever, ever delete the self issued ones that SharePoint provisioned within that folder. You will cause a Micheal Bay-spolosion. To verify the chain, just popup open the certificate details within some interface (like, the MMC :) ) doesn’t really matter what and verify that the chain is trusted and existent.
  2. Next, verify that you actually used the right certificate when specifying the certificate path when building the System.Security.Cryptography.X509Certificates.X509Certificate2 object to pass into your SPTrustedIdentityTokenIssuer. This is pretty easy to mess up when troubleshooting if you are swapping certs all over the place.

Both of these are in place, then that error will go away. Not that another won’t popup :)

Share

SharePoint Timer Jobs And Feature Receivers Accessing the Term Store (Service Applications)

When working with timer jobs, feature receivers, and other SharePoint assets that use the service application architecture components you may encounter the error:

UnauthorizedAccessException: “The current user has insufficient permissions to perform this operation”

even when leveraging a highly privileged accounts such as domain administrators. While the focus of this post is just going to use the Term Store as an example, this can happen with anything that subscribes to the 2010 services architecture.

Consider the below sample code which will attempt instantiating a few objects from the Term Store:

[csharp]
TaxonomySession session = new TaxonomySession(site);
// can also be a Metadata Term Store Name
TermStore store = session.TermStores[1];

// Bad news bear happens here
GroupCollection groups = store.Groups;

// Or other actions can cause this error
Group group = store.CreateGroup(“Group”);
TermSet set = group.CreateTermSet(“Term Set 1”);
set.CreateTerm(“Term 1”, 1033);
store.CommitAll();
[/csharp]

The second sample is just there for posterity, assume the first example piece of code because it is a pretty simple collection hydration. TaxonomySession is the entry point to accessing all data associated with TermStore objects as can be seen with the use of the TaxonomySession.TermStores property following. You can pretty much do whatever after those proxy objects are set in place.

Due to service application interaction in SharePoint 2010, services architecture process accounts running things like timer jobs and feature event sinks still lean on a proxy for connectivity. In 2007 this was a lot different because it was more blanket, timer process accounts were more liberally leveraged across processes. In 2010, it becomes increasingly important to ensure that accounts are specifically designated for particular services.

Under these pretenses, it is necessary to determine what is being used for the SharePoint\System account (your app pool and farm account, I know you can mask in a similar format ala policies) and add it to the Term Store Administrators or whatever service group you need. Should run just fine afterwards.

Share