Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF Access Denied exception never returns to client after CheckAccessCore

I have a WCF service configured with TransportWithMessageCredential security. All the three implementations for IAuthorizationPolicy, ServiceAuthenticationManager and ServiceAuthorizationManager are in place and effective.

serviceHost.Credentials.ServiceCertificate.SetCertificate("CN=localhost");
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomValidator();
serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;

serviceHost.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
serviceHost.Authorization.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
serviceHost.Authentication.ServiceAuthenticationManager = new MyServiceAuthenticationManager();
serviceHost.Authorization.ExternalAuthorizationPolicies = 
    new System.Collections.ObjectModel.ReadOnlyCollection<System.IdentityModel.Policy.IAuthorizationPolicy>(
        new MyAuthorizationPolicy[] { new MyAuthorizationPolicy() });

As I know, in the ServiceAuthorizationManager inherited class, in CheckAccessCore method, a return false statement indicates an Access Denied. That's all good till I want the client side to know that he got an access denied exception where the service stops returning anything to the client and it seems the service thread hanged.

I tried all kinds of try catch in the client side and even added a FaultContract to the operation, but the problem resists.

All I can see are two error events in Diagnostics Tools.

enter image description here

What are missing from my implementations to get the service inform the user on access denied error?

Update

It's notable to say that I'm using RoutingService and now I guess the real cause is that RoutingService is somehow eating the exception but I don't know exactly where that happens. Even though I stepped into every possible method but I failed to find it.

Update 2

I have IErrorHandler in place:

   public class ServiceErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            //You can log th message if you want.            
            return true;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
        {
            if (error is FaultException)
                return;

            FaultException faultException = new FaultException(error.Message);
            MessageFault messageFault = faultException.CreateMessageFault();
            msg = Message.CreateMessage(version, messageFault, faultException.Action);
        }
    }

But event with this the calling client wont' get the exception and the same 'unhandled exception' will appear in debug events.

The sole way that I can fail the calling client is by throwing an exception in BeforeSendReply method of IDispatchMessageInspector which I think isn't the way to go as I get a CommunicationException on the client side instead of FaultException:

  public void BeforeSendReply(ref Message reply, object correlationState)
    {                        
        if (reply != null && reply.IsFault)
        {
            var messageFault = MessageFault.CreateFault(reply, Int32.MaxValue);
            throw new FaultException("Access was denied", messageFault.Code);
        }
    }

WCF Tracing: enter image description here

like image 236
Mohsen Afshin Avatar asked Jun 02 '16 06:06

Mohsen Afshin


1 Answers

You should not expect a FaultException here but instead, a Communication Exception, in your case a SecurityAccessDeniedException. I would expect all the exceptions raised at this stage to be communication one as it is about communication issue, security ones are part of it.

like image 135
LeBaptiste Avatar answered Oct 23 '22 11:10

LeBaptiste