Saturday, September 16, 2006 6:28 PM
I've been doing some work with adding custom context to our WCF web services. I've successfully added context to the operation and instance context, but now I'm doing some work at the IAuthorizationPolicy level and finding out that the contexts that I've hooked so far (i.e. operation context and instance context) are not available yet at this point.
First, is there a context that flows all the way through all these layers of a WCF request? Second, I cannot find any good documentation on what the life cycle of a request looks. If I could find that I would probably be able to figure the rest out on my own.
Tuesday, September 19, 2006 11:29 PMModerator
Can you elaborate on the type of context you need during IAuthorizationPolicy execution? Or what it is you need to flow through the entire lifecycle of the request? It will help to picture what you are doing.
Tuesday, September 19, 2006 11:47 PM
Well, we have a custom context of our own in which we cache of data. I never want to look up certain data more than once per-request. So, if I happen to look up the user or some data about that user during the authorization process, I don't want to have to look it up again if the underlying method needs the same data. All of our code relies on this context to exist and accesses it via a static property. However, this custom context's lifetime is managed independently of the code that calls it and is usually configured to meet the needs of the application. For example, in ASP.NET applications we tie it to the lifetime of HttpContext.Current by storing it in the Items collection when it's first requested and then making sure to dispose of it when the request ends.
Basically I'm looking for someplace I can store my context that will be available in both authorization policy evaluation time as well as operation execution time. I've already got it storing in ItemContext and OperationContext for WCF with custom extensions, but neither of those is available at the point that I'm evaluating my authorization policy, so that pretty much leaves me stuck at this point.
Is that enough info?
Thursday, September 21, 2006 8:41 PMModerator
I have posted a blog entry with a sample here.
It illustrates the creating context for the lifetime of the request using a message inspector to hook up the custom operation context extension. I'm not sure why you say you can't access the custom context from the authorization context, I had no problem with that as illustrated in my sample. Check it out and let me know if that does the trick.
Thursday, September 21, 2006 11:04 PM
First, thanks for the sample. I actually have done a lot of experimenting with providing custom context (documented here) and have no problems hooking that all up. Second, I have gone back and realized that my problem is not actually in the IAuthorizationPolicy, but rather in my custom UserNamePasswordValidator implementation. I actually do have full context in my IAuthorizationPolicy... so my apologies for the mistake (it had been a long day when I wrote the original post).
So, my problem is getting context in UserNamePasswordValidator. At that point, no form of extension I have tried is even invoked yet (i.e. IInstanceContextInitializer, IDispatchMessageInspecter or ICallContextInitializer). What can I do for that? What chance do I have to hook up my own custom context before that stage of authentication? Am I better off taking some other route to validate the user/pass?
Thursday, September 21, 2006 11:15 PMModerator
My first question is: what type of context information do you need in order to perform the username/password validation. Or, are you trying to insert information into the context bag during password validation? Could it be that you should be inspecting the evaluation context during IAuthorizationPolicy instead? To see the result of the validation? Just a thought.
IContextChannel could give you scope for the entire channel, but that is a very different use than a per request context.
Thursday, September 21, 2006 11:37 PM
I think the disconnect we're having is that it's not just one specific area of the request that I need to access this data, it's throughout the whole request. I'm trying to avoid describing exactly what the data is because I don't think it makes a difference and if I get into that I'm afraid it will cloud the simplicity of the fact that I just want to know how to make "some data" available to every single possible stage of a WCF request, but if you really want to know I wrote a little bit about the problem I'm trying to solve in this blog post when I originally started solving the problem.
So, to simplify it as much as possible, let's pretend I need to store a integer and increment it at every possible stage of the request that I could ever have custom code be a part of. Where could I put that integer so that it's accessible from the same exact place when the server begins processing the request to 'til the server finishes processing the request?
Thanks again for your help, it is much appreciated!
Thursday, September 21, 2006 11:55 PMModeratorSure, the bottom line is, there is no such context easily accessible. You'll probably have to do a IChannelContext implementation and provide static access to the request specific instance somehow.
Friday, September 22, 2006 12:10 AM
Drew, if you attach an IExtension<OperationContext> that context will be available for the life of the request. You say that you're looking for just such a context:
"...let's pretend I need to store a integer and increment it at every possible stage of the request that I could ever have custom code be a part of...."
My question is, under what conditions do you find that your opcontext is not there for the lifetime of the operation? I guess I'm not clear on where the custom context attached could "go" during the operation processing. Can you explain a touch more?
Friday, September 22, 2006 5:48 AM
Actually, now that I really READ your posts, the solution is to increase your scope until you have a context that you can use for your needs. If OperationContext and InstanceContext did not occur soon enough, then move on out to IExtension<IContextChannel>, which is created prior to the instancecontext. And if that doesn't work, we have a larger scope for you: ServiceHostBase.
Friday, September 22, 2006 5:15 PM
Thanks, I'm gonna check this out. Just to make sure I understand correctly:
- Loop all the ChannelDispatchers of the ServiceHostBase in my IServiceBehavior::ApplyDispatchBehavior
- For each ChannelDispatcher, add a IChannelInitializer whose Initialize implementation adds a custom IExtension<IContextChannel> to the channel
- I can hook IContextChannel::Closed if I need to dispose of any resources stored in my custom extension
Sound right? Be great if you could just validate that when you get a chance because I don't think I'll be able to work on it today, but would love to know that I'm grokking it correctly.
Thanks a ton,
Friday, September 22, 2006 6:06 PM
Adding an extension to the channel is also possible, but that would result in per-channel state instead of per-request state. That did not sound like what you wanted.
Before that and in the channel, there is the request message itself. Message.Properties is a collection indexed by name whose value is any object. You can keep state there as well.
If you need access to your state both in the channel stack and in the dispatcher, you can use both of these mechanisms. Then if you implement IDispatchMessageInspector, you have access to both the Message and the OperationContext.Current and can migrate your state from Message to OperationContext.
Does that address your scenario?
Monday, September 25, 2006 3:39 PM
Ok, so I got some time to try extending the IContextChannel the way I described above. Even that is still not happening before the UserNamePasswordValidator is being called. I've included the callstack of where I need to initially access my custom context so you can see exactly where it is in the lifecycle of the request.
Michael, you are correct that I am trying to achieve both per-request or per-instance context state. Per channel does not sound like something I'd want or have to manage myself. Your proposal for migrating the state sounds like a possible solution if necessary, however as I stated above I've tried extending the channel and it's still not "early" enough in the lifecycle to help with the exact problem I'm dealing with here.
Thanks to all for all the help, I hope we can find a good solution to this.
==== BEGIN STACK TRACE OF WHERE I NEED ACCESS TO MY CONTEXT ====
Mimeo.OrderingWebService.dll!Mimeo.Common.IdentityModel.Selectors.UserSystemUserNamePasswordValidator.Validate(string userName, string password) Line 22 C#
System.IdentityModel.dll!System.IdentityModel.Selectors.CustomUserNameSecurityTokenAuthenticator.ValidateUserNamePasswordCore(string userName, string password) + 0x27 bytes
System.IdentityModel.dll!System.IdentityModel.Selectors.UserNameSecurityTokenAuthenticator.ValidateTokenCore(System.IdentityModel.Tokens.SecurityToken token) + 0x65 bytes
System.IdentityModel.dll!System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(System.IdentityModel.Tokens.SecurityToken token) + 0x11a bytes
System.ServiceModel.dll!System.ServiceModel.Security.ReceiveSecurityHeader.ReadToken(System.Xml.XmlReader reader, System.IdentityModel.Selectors.SecurityTokenResolver tokenResolver, System.Collections.Generic.IList<System.IdentityModel.Selectors.SecurityTokenAuthenticator> allowedTokenAuthenticators, out System.IdentityModel.Selectors.SecurityTokenAuthenticator usedTokenAuthenticator) + 0x93 bytes
System.ServiceModel.dll!System.ServiceModel.Security.ReceiveSecurityHeader.ReadToken(System.Xml.XmlDictionaryReader reader, int position, byte decryptedBuffer, System.IdentityModel.Tokens.SecurityToken encryptionToken, string idInEncryptedForm, System.TimeSpan timeout) + 0xe9 bytes
System.ServiceModel.dll!System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(System.Xml.XmlDictionaryReader reader) + 0x283 bytes
System.ServiceModel.dll!System.ServiceModel.Security.StrictModeSecurityHeaderElementInferenceEngine.ExecuteProcessingPasses(System.ServiceModel.Security.ReceiveSecurityHeader securityHeader, System.Xml.XmlDictionaryReader reader) + 0x23 bytes
System.ServiceModel.dll!System.ServiceModel.Security.ReceiveSecurityHeader.Process(System.TimeSpan timeout) + 0x7e8 bytes
System.ServiceModel.dll!System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessageCore(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout) + 0x46a bytes
System.ServiceModel.dll!System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout) + 0x8f bytes
System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.VerifyIncomingMessage(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout, System.ServiceModel.Security.SecurityProtocolCorrelationState correlationStates) + 0x30 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelListener<System.ServiceModel.Channels.IReplyChannel>.ServerSecurityChannel<System.ServiceModel.Channels.IReplyChannel>.VerifyIncomingMessage(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout, System.ServiceModel.Security.SecurityProtocolCorrelationState correlationState) + 0x55 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelListener<System.ServiceModel.Channels.IReplyChannel>.SecurityReplyChannel.ProcessReceivedRequest(System.ServiceModel.Channels.RequestContext requestContext, System.TimeSpan timeout) + 0xfb bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelListener<System.ServiceModel.Channels.IReplyChannel>.ReceiveRequestAndVerifySecurityAsyncResult.ProcessInnerItem(System.ServiceModel.Channels.RequestContext innerItem, System.TimeSpan timeout) + 0x34 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelListener<System.ServiceModel.Channels.IReplyChannel>.ReceiveItemAndVerifySecurityAsyncResult<System.ServiceModel.Channels.RequestContext,System.ServiceModel.Channels.IReplyChannel>.OnInnerReceiveDone() + 0x76 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelListener<System.ServiceModel.Channels.IReplyChannel>.ReceiveItemAndVerifySecurityAsyncResult<System.ServiceModel.Channels.RequestContext,System.ServiceModel.Channels.IReplyChannel>.InnerTryReceiveCompletedCallback(System.IAsyncResult result) + 0x13c bytes
SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x3d bytes
System.ServiceModel.dll!System.ServiceModel.AsyncResult.Complete(bool completedSynchronously) + 0x11a bytes
System.ServiceModel.dll!System.ServiceModel.AsyncResult.Complete(bool completedSynchronously, System.Exception exception) + 0x2f bytes
System.ServiceModel.dll!System.ServiceModel.Channels.InputQueue<System.ServiceModel.Channels.RequestContext>.AsyncQueueReader.Set(System.ServiceModel.Channels.InputQueue<System.ServiceModel.Channels.RequestContext>.Item item) + 0xb0 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.InputQueue<System.ServiceModel.Channels.RequestContext>.EnqueueAndDispatch(System.ServiceModel.Channels.InputQueue<System.ServiceModel.Channels.RequestContext>.Item item, bool canDispatchOnThisThread) + 0x2f7 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.InputQueue<System.ServiceModel.Channels.RequestContext>.EnqueueAndDispatch(System.ServiceModel.Channels.RequestContext item, System.ServiceModel.Channels.ItemDequeuedCallback dequeuedCallback, bool canDispatchOnThisThread) + 0x78 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.InputQueueChannel<System.ServiceModel.Channels.RequestContext>.EnqueueAndDispatch(System.ServiceModel.Channels.RequestContext item, System.ServiceModel.Channels.ItemDequeuedCallback dequeuedCallback, bool canDispatchOnThisThread) + 0x39 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SingletonChannelAcceptor<System.ServiceModel.Channels.IReplyChannel,System.ServiceModel.Channels.ReplyChannel,System.ServiceModel.Channels.RequestContext>.Enqueue(System.ServiceModel.Channels.RequestContext item, System.ServiceModel.Channels.ItemDequeuedCallback dequeuedCallback, bool canDispatchOnThisThread) + 0x67 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SingletonChannelAcceptor<System.ServiceModel.Channels.IReplyChannel,System.ServiceModel.Channels.ReplyChannel,System.ServiceModel.Channels.RequestContext>.Enqueue(System.ServiceModel.Channels.RequestContext item, System.ServiceModel.Channels.ItemDequeuedCallback dequeuedCallback) + 0x24 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(System.ServiceModel.Channels.HttpRequestContext context, System.ServiceModel.Channels.ItemDequeuedCallback callback) + 0x29a bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(System.IAsyncResult result) + 0x2d1 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(System.IAsyncResult result) + 0x32 bytes
SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x3d bytes
System.dll!System.Net.LazyAsyncResult.Complete(System.IntPtr userToken) + 0x9a bytes
System.dll!System.Net.LazyAsyncResult.ProtectedInvokeCallback(object result, System.IntPtr userToken) + 0xb8 bytes
System.dll!System.Net.LazyAsyncResult.InvokeCallback(object result) + 0x1f bytes
System.dll!System.Net.ListenerAsyncResult.WaitCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped) + 0x227 bytes
mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) + 0x68 bytes
Friday, March 09, 2007 1:48 AM
I'm also facing same problem
Friday, March 14, 2008 7:48 PMI am having the same issue. Have you ever found a solution to this?
Saturday, November 08, 2008 5:32 PMCan someone post the final solution to this...?
Monday, November 17, 2008 7:00 AMHi..
Just wondering if this problem got resolved..
I am having the exact same issue of accessing OperationContext from a channel where I need to store user related data, so it is available later in the processin pipeline..
Any help would be great..