1
votes

Currently I'm facing a problem trying to use the EWS, the original requirement is to connect to the mail of a specific user, after that, search for a specific folder, search for a mail and then download the attachment to a specific folder to the local machine so that later it can be processed by Integration Services, I don't want to use the EWS Managed API because I don't want to install anything when migrating to the production server, right now my idea is to develop a WCF service that connects to the EWS service through the .asmx uri and that will be able to fulfill my requirement, right now I'm ok connecting to the company's EWS but I have a few doubts:

According to Microsoft: http://msdn.microsoft.com/en-us/library/exchange/bb408524(v=exchg.150).aspx they say you must add a web service reference to your project, although it only mentions VS 2005 and VS 2008 (I'm working with VS 2012), it should automatically creates the class ExchangeServiceBinding which gives you everything you need in order to impersonate an account, my first question is: why I'm not seeing this class added to my project? should I expect this is because I am using VS 2012?

ok, anyway I'm able to use some methods like getPasswordExpirationTime, but when I use the FindFolder method, I'm getting the error: "The account does not have permission to impersonate the requested user.", this is my method:

using (ExchangeServicePortTypeClient exchangeServicePortTypeClient = new ExchangeServicePortTypeClient())
                {
                    exchangeServicePortTypeClient.ChannelFactory.Endpoint.Address = new EndpointAddress("https://email.kraft.com/EWS/Exchange.asmx");


                    FindFolderResponseType findFolderResponseType = null;

                    exchangeServicePortTypeClient.FindFolder(
                        new ExchangeImpersonationType
                        {
                            ConnectingSID = new ConnectingSIDType
                            {
                                Item = @"[email protected]",
                                ItemElementName = ItemChoiceType1.PrimarySmtpAddress
                            }
                        },
                        null,
                        new RequestServerVersion { Version = ExchangeVersionType.Exchange2010_SP2 },
                        null,
                        new FindFolderType
                        {
                            FolderShape = new FolderResponseShapeType
                            {
                                BaseShape =
                                  DefaultShapeNamesType.Default
                            }
                        }, out findFolderResponseType);

                }                   

I have tried to set the credentials through this:

exchangeServicePortTypeClient.ClientCredentials.Windows.ClientCredential.UserName
exchangeServicePortTypeClient.ClientCredentials.Windows.ClientCredential.Password      
exchangeServicePortTypeClient.ClientCredentials.Windows.ClientCredential.Domain             
exchangeServicePortTypeClient.ChannelFactory.Credentials.Windows.ClientCredential.UserName
exchangeServicePortTypeClient.ChannelFactory.Credentials.Windows.ClientCredential.Password
exchangeServicePortTypeClient.ChannelFactory.Credentials.Windows.ClientCredential.Domain

with no luck :(.

by the way, this is my wcf config file.

<?xml version="1.0" encoding="utf-8"?>

<configuration>

    <!--Diagnostics section, we will only catch error and warning in production-->

    <system.diagnostics>
        <sources>
            <source propagateActivity="true" name="System.ServiceModel" switchValue="Error, Warning">
                <listeners>
                    <add type="System.Diagnostics.DefaultTraceListener" name="Default">
                        <filter type="" />
                    </add>
                    <add type="System.Diagnostics.DefaultTraceListener" name="SellOut.ExchangeWcfService">
                        <filter type="" />
                    </add>
                    <add name="ServiceModelTraceListener">
                        <filter type="" />
                    </add>
                </listeners>
            </source>
            <source name="System.ServiceModel.MessageLogging" switchValue="Error, Warning">
                <listeners>
                    <add type="System.Diagnostics.DefaultTraceListener" name="Default">
                        <filter type="" />
                    </add>
                    <add type="System.Diagnostics.DefaultTraceListener" name="SellOut.ExchangeWcfService">
                        <filter type="" />
                    </add>
                    <add name="ServiceModelMessageLoggingListener">
                        <filter type="" />
                    </add>
                </listeners>
            </source>
        </sources>
        <sharedListeners>
            <add initializeData="C:\Users\LFH2623\Documents\SellOut\SellOut\SellOut.Hosts.ExchangeWcfService\SellOut.ExchangeWcfService_web_tracelog.svclog"
              type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
              name="ServiceModelTraceListener" traceOutputOptions="LogicalOperationStack, DateTime, Callstack">
                <filter type="" />
            </add>
            <add initializeData="C:\Users\LFH2623\Documents\SellOut\SellOut\SellOut.Hosts.ExchangeWcfService\SellOut.ExchangeWcfService_web_messages.svclog"
                type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                name="ServiceModelMessageLoggingListener" traceOutputOptions="LogicalOperationStack, DateTime, Callstack">
                <filter type="" />
            </add>
        </sharedListeners>
        <trace autoflush="true" />
    </system.diagnostics>

    <!--Framework Section-->

    <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
    </appSettings>  

    <!--Web section-->

    <system.web>        
        <compilation debug="true" targetFramework="4.5" />
        <httpRuntime targetFramework="4.5" maxRequestLength="2147483646" />
    </system.web>

    <!--Web server section-->

    <system.webServer>
        <directoryBrowse enabled="false"/>
        <defaultDocument enabled="true"/>               
        <modules runAllManagedModulesForAllRequests="true"/>
    </system.webServer>

    <!--WS section-->

    <system.serviceModel>

        <diagnostics>
            <messageLogging logMalformedMessages="true"
                            maxMessagesToLog="10000"
                            logMessagesAtTransportLevel="true"
                            logMessagesAtServiceLevel="True"
                            logEntireMessage="true"/>
            <endToEndTracing activityTracing="false" />
        </diagnostics>

        <!--For the ExchangeWebService we will aply the following service and endpoint behaviors-->

        <behaviors>

            <serviceBehaviors>
                <behavior name="ExchangeWebService">
                    <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
                    <serviceDebug includeExceptionDetailInFaults="false" httpHelpPageEnabled="true" />
                    <dataContractSerializer maxItemsInObjectGraph="2147483646" />
                    <serviceTimeouts transactionTimeout="01:00:00" />
                    <serviceThrottling maxConcurrentCalls="100" maxConcurrentSessions="100" maxConcurrentInstances="100"/>
                    <serviceDiscovery>
                        <announcementEndpoints>
                            <endpoint kind="udpAnnouncementEndpoint"></endpoint>
                        </announcementEndpoints>
                    </serviceDiscovery>
                </behavior>             
            </serviceBehaviors>

            <!-- Define the corresponding scope for the clients to find the service through resolve message -->

            <endpointBehaviors>
                <behavior name="ExchangeWebService">
                    <endpointDiscovery enabled="true">
                        <scopes>
                            <add scope="http://SellOut.ExchangeWcfService/"/>
                        </scopes>
                    </endpointDiscovery>
                </behavior>
            </endpointBehaviors>

        </behaviors>

        <!-- In case you want to scale this service -->

        <standardEndpoints>

            <!-- We allow the service to be discoverable through the network in an adhoc architecture through UDP -->

            <udpDiscoveryEndpoint>
                <standardEndpoint name="adhocDiscoveryEndpointConfiguration"
                                  discoveryMode="Adhoc"
                                  discoveryVersion="WSDiscovery11"
                                  maxResponseDelay="00:00:10">
                </standardEndpoint>
            </udpDiscoveryEndpoint>

            <!-- We allow the service to be discoverable through the network in a managed architecture -->

            <discoveryEndpoint>
                <standardEndpoint name="managedDiscoveryEndpoint" discoveryMode="Managed" maxResponseDelay="00:01:00"/>
            </discoveryEndpoint>

            <!-- We announce the service with hello & bye -->

            <announcementEndpoint>
                <standardEndpoint name="udpAnnouncementEndpointConfiguration"
                                  discoveryVersion="WSDiscovery11" />
            </announcementEndpoint>

        </standardEndpoints>

        <!--All the Kraft's clients are .net, so we will use the proprietary binary message encoding to reduce the message's size -->

        <bindings>
            <customBinding>
                <binding name="wsHttpBindingBynaryEncoding"
                         closeTimeout="00:10:00"
                         openTimeout="00:10:00"
                         receiveTimeout="00:10:00"
                         sendTimeout="00:10:00">
                    <binaryMessageEncoding>
                        <readerQuotas maxDepth="32"
                                      maxStringContentLength="5242880"
                                      maxArrayLength="2147483646"
                                      maxBytesPerRead="4096"
                                      maxNameTableCharCount="5242880" />
                    </binaryMessageEncoding>
                    <httpTransport
                                    transferMode="Buffered"
                                    maxBufferPoolSize="2147483646"
                                    maxReceivedMessageSize="2147483646">
                    </httpTransport>
                </binding>
            </customBinding>

            <basicHttpBinding>
                <binding name="KraftEWS" messageEncoding="Text" transferMode="Buffered">
                    <security mode="Transport">
                        <transport clientCredentialType="Windows" proxyCredentialType="Windows"></transport>                        
                    </security>             
                </binding>
            </basicHttpBinding>

        </bindings>

        <!-- We reference the Kraft EWS -->

        <client>
            <endpoint binding="basicHttpBinding" bindingConfiguration="KraftEWS"
                      contract="KraftEWS.ExchangeServicePortType"                                     
                      name="ExchangeServiceBinding_ExchangeServicePortType">                
            </endpoint>
        </client>

        <!--Services section-->

        <services>
            <service name="SellOut.Services.Exchange.ExchangeWebService" behaviorConfiguration="ExchangeWebService">
                <endpoint name="rules"
                          address="rules"
                          binding="customBinding"
                          bindingConfiguration="wsHttpBindingBynaryEncoding"
                          behaviorConfiguration="ExchangeWebService"
                          contract="SellOut.Contracts.Exchange.IExchangeRulesContract"/>
                <endpoint name="udpDiscovery"
                          kind="udpDiscoveryEndpoint"
                          endpointConfiguration="adhocDiscoveryEndpointConfiguration" />
                <endpoint name="mex"
                          address="mex"
                          binding="mexHttpBinding"
                          contract="IMetadataExchange" />               
            </service>
        </services>

        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

    </system.serviceModel>

</configuration>

another thing to mention, in this config:

<basicHttpBinding>
                    <binding name="KraftEWS" messageEncoding="Text" transferMode="Buffered">
                        <security mode="Transport">
                            <transport clientCredentialType="Windows" proxyCredentialType="Windows"></transport>                        
                        </security>             
                    </binding>
                </basicHttpBinding>

if I remove the part client credential type "Windows", when trying to run the service the exception is :

The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate,NTLM'.

can you guys give me a hand.

Thanks in advice.

1

1 Answers

0
votes

I highly suggest that you use the EWS Managed API for this. I don't understand what you mean when you state that you don't want to install anything on the production server since you will have to install the WCF service on your production server, along with the object model created by the Add Web Service reference project. But, it looks like you got passed this part, so let's move on....

Excellent, good to know that you could call getPassowrdExpirationTime. Thank you for providing me that info.

The reason why you get the message "The account does not have permission to impersonate the requested user" is because the account that runs your WCF service doesn't have impersonation rights to the mailbox. You will need to setup Exchange impersonation before your service account can access the user's account. Once your service has credentials that Exchange can lookup in AD (which it already does), and it has rights to impersonate your users (which is on your TODO list), your code should work since authentication is done based on the service account's identity.

All parts of your question after your code example are not in scope. You don't need to make any changes to the web.config (at least I don't think so). I assume that you are testing against an on-premise server since the Auth schemes were Negotiate and NTLM.

With regards,

The article you linked to