Exchange Web Services Example – Part 3 – Exchange Impersonation

In part 3 of this series on integrating Exchange Web Services with Java and OpenEdge, we’re going to talk about a technique for accessing mailboxes called Exchange Impersonation.

I had planned to get this article posted a few weeks back, but a number of things have cropped up in between. I have already been using Exchange Impersonation for a while, but it has been against Exchange 2007. As I was doing the initial work on this article, I knew that Microsoft had changed the mechanism for Impersonation in Exchange 2010, so I decided to investigate the impact of it. That took a lot more work than I was planning, as I ended up installing and running a separate 2010 server and learning a lot about Windows Powershell that I really was not planning. So I’m sorry about the delay, but I think you are probably going to agree that the due-diligence was worth it.

The first part of this article is going to talk about what Exchange Impersonation is, how it works, and the very serious risks it can potentially introduce to your enterprise. We’ll talk a little about how you can mitigate those risks, how to set it up from an administrative point of view, and then we’ll actually use it.

As with the other articles in this series, there is also a set of sample code that you can download and install to follow along later in the article.

So here we go…

The Need for Server-to-Server (S2S) Communication

Exchange Web Service Subscriptions Exchange Web Services Example   Part 3   Exchange ImpersonationAs I described in my initial article, the whole reason I am building this solution is to act as an intermediate service between a Customer Relationship Management (CRM) application and Microsoft Exchange. An OpenEdge application will be making updates to a database that need to trigger the creation of appointments. Furthermore, when any of those appointments change on the Microsoft Exchange Server, the CRM application needs to be updated accordingly. The important thing to realize is that the updates to and from the OpenEdge application will happen in the business logic and therefore there is no client user. The client is really a server process itself.

To complicate matters further, the “client” is also a service-oriented application, so there is no tight coupling between Microsoft Exchange and the CRM application. This means that all communication with Exchange is to happen via message-oriented middleware or MOM (specifically, ApacheMQ and SonicMQ), which, in turn means that the connection to Exchange will be from a service container in the middleware, and that service container can have no knowledge of the client user. Practically speaking, that container would need to know the user names and passwords for all potential users of the system and that really is not practical.

The service container (which is a server process) is communicating with Exchange Server (another server process) on behalf of a user – thus, server-to-server (S2S) communication.

So you have a server communicating with another server on behalf of a user. From the user’s point of view, it really is as though the user performed the business action that led to the effect on Exchange.

In the diagram of the architecture, the Glassfish Application Server containing the Java EWS WebService and the Java HTTP Servlet are really the one side of the server-to-server configuration and the Microsoft Exchange Server is the other.

What is Exchange Impersonation?

Exchange Impersonation is a mechanism that allows a single Windows Active Directory account to act on behalf of other users on a Microsoft Exchange mailbox as if they were performing the action themselves. In other words, assuming I create an Active Directory logon called ewsproxy (which we will do later on), and I give ewsproxy Exchange Impersonation privileges for users froggf and hippoh, ewsproxy can perform any action that froggf or hippoh can perform on their mailboxes.

There is one small caveat. Exchange Impersonation only works via the Exchange Web Services API. This means that although ewsproxy is able to act on behalf of froggf and hippoh, it only works if it is done as an Exchange Web Service call; it does not work through Outlook, Outlook Web Access, or any other Exchange client, and that is good news. I’ll get to why in a moment.

Another thing to bear in mind is that from froggf’s point of view, he cannot tell the difference between an appointment that was created by ewsproxy and an appointment created by himself. To all intents and purposes, ewsproxy is froggf when it operates on froggf’s mailbox.

Exchange Impersonation is the ideal way for a service to act on behalf of an Exchange user, and that is specifically what I want it for. Remember, I am looking at the Exchange Web Services API from the point of view of a Java service that can perform any operation on behalf of any user. It is effectively a super-user.

Other Alternatives

Exchange Impersonation is not the only way to do this. There are a number of alternatives that can be used, but none of them work quite as well as Exchange Impersonation:

  • Delegate – You can delegate rights to other accounts to act on your behalf, but you have to do it from your Exchange client (Outlook). In other words, the end-user controls these rights and can grant and revoke privileges as desired. Delegation is really a mechanism for a user to give another user permissions on their mailbox.
  • Send As – An Administrator can set up an account so that it can send mail on behalf of another account. This permission allows most of the same behavior as Exchange Impersonation, with one subtle difference: A user can act on behalf of another user from their Exchange client, whereas with Exchange Impersonation, it is only through the Exchange Web Services API that a user can act on behalf of another user.

There are also a number of other alternatives that are not Exchange-specific, but these normally require elevated permissions, such as admin rights, which are not desirable. The beauty of Exchange Impersonation lies in the fact that the Exchange Impersonation account is nothing more than a regular user account, and you can lock down the account so that it can do nothing else. It is therefore the best alternative for this purpose.

Risks of Exchange Impersonation

When BP got the permit to drill the well that is now spewing oil into the Gulf of Mexico, they knew that there was a lot of risk associated with what they were doing. By all accounts, they cut a lot of corners for the sake of profit, which is why we have the mess on the beaches in Louisiana that we do today.

I would be lying to you if I did not tell you that there are some significant security risks associated with Exchange Impersonation, and they’re a lot more insidious than you may realize. So instead of behaving like BP and pretending that the risks do not exist, I am going to go the other way and err on the side of caution.

Exchange Impersonation is a very powerful feature that allows you to build a server-side component that can perform any action on any mailbox on behalf of any user. Obviously, the Exchange Administrator can limit which mailboxes have the permission applied to them, but those that do can have actions performed on their behalf.

The Exchange Web Services API is Secure

Microsoft has done an admirable job of locking down Exchange Web Services so that it is very tightly controlled:

  • You have to make an SSL connection;
  • You have to be authenticated against an Active Directory; and
  • By default you have to use NTLM V2 authentication.

Moreover, you can add to this security level by requiring client-side certificates which make it almost impossible for a client that is not authentic to connect to the Exchange Web Service. So locking down Exchange Server is not where the problem lies.

The Real Risk

The problem lies in the application code that you build that connects to the Exchange Web Services API. If you don’t build the same security into the connections to your code, you are opening the Exchange Server up to all kinds of risks. If you don’t lock down your client connections to your application, there is nothing stopping anything from making changes to the server.

Think, for a minute, about how easy it has been to get soapUI to connect to the Java EWS WebService. If this was a production application, the security hole would be huge. Now think about the removing all the connection information and having to only provide the e-mail address of the account for which you wish to make the change. Suddenly, what was a very secure connection is open to all kinds of abuse.

This becomes even more serious as the solution becomes more service-oriented. The messages will now flow through a MOM where they are deliberately being exposed to other services for processing.

The reason I am making this point so strongly is that the examples I am going to include do nothing to protect the channel of communication. It is completely insecure, and that’s fine in a development environment for now. The code that you are going to look at, though, has to be made far more secure and robust if it is going to be used in a production environment.

Now that you are well and truly scared of the risks that you could be creating (and if you are not, you should not be writing code), let’s get things set up.

Creating the Accounts

Back in part 1 of this series, I told you that I had 4 user accounts that I would be using:

  • Freddie F. Frogg (froggf@example.com)
  • Harriet H. Hippo (hippoh@example.com)
  • Edward E. Elifant (elifante@example.com)
  • Gerald G. Gerarf (gerarfg@example.com)

These 4 users are all sales people who sell safari tours, in my fictitious test company, so I have added them to the Sales user group.

User Accounts, AD Accounts, and Mailbox Accounts

So that we are using a common set of terms, I am going to call these the user accounts. There’s a subtlety here, though, that I need to make sure you understand: Freddie F Frogg has two accounts that we care about:

  1. His Active Directory account that gives him permission to logon to the domain. I’m going to refer to this as his Active Directory Account (or AD Account); and
  2. His Exchange Server Mailbox that is used to pick up e-mail and change his calendar. I’m going to refer to this as his Mailbox Account.

The AD account has to have access rights to a corresponding Mailbox account in order to receive mail, etc, but an AD account can exist without a corresponding Mailbox account. The four accounts that I mentioned above are created in the Active Directory and then a corresponding mailbox is created for them on the Exchange Server. This is standard Exchange Administration.

So these four user accounts consist of an AD account and a Mailbox account.

The Service Account

As I described earlier on, we’re going to create an account called ewsproxy that will act on behalf of the four user accounts. I’m going to refer to this as the Service Account because it is used by the service that we have built to act on behalf of the user account.

The service account does not need to have a mailbox account. Instead, it only has an AD account.

So what we are going to do is create the Service account and then set some permissions on both the service and the user accounts that will allow the service account to act on behalf of the user accounts.


My Configuration

Just so you have a point of reference, I am running two versions of Exchange – Exchange 2010 and Exchange 2007 SP3.

The Exchange 2010 server is running on a Windows 2008 R2 machine.

The Exchange 2007 server is running on a Windows 2008 SP2 machine.

In both cases, the Active Directory is running on a Windows 2008 R2 domain controller.

I am going to be using command line utilities to get things set up for Exchange Impersonation, and you may find that if you are doing things on a different combination of operating system and Exchange server, that things look or are slightly different. Consult the documentation for your combination and hopefully the differences are not too big.


Adding the AD Account for the Service Account

Part3 AddUser Exchange Web Services Example   Part 3   Exchange ImpersonationThere is really nothing special about adding the AD account for the Service Account. It is simply a normal user in the domain.

Log on to the Active Directory server and simply add an account with whatever username you want to add.Part3 AddUser2 Exchange Web Services Example   Part 3   Exchange Impersonation

On the next page of the wizard, you are prompted for the password for this account. It’s important to make this a tough password and it’s also probably worthwhile setting the Password never expires flag, as this is intended to be a service account, and changing the password will affect the service as well.

My knowledge of Active Directory is not good enough to unequivocally say this, but I am fairly confident that you could create a policy that would prevent the user from being able to log in interactively. I know you can restrict a user to only log in from certain machines. I will be researching that a little later in the development process, but for now, creating a regular user account will have to do.

Applying Permissions

So far, there is no difference between working with Exchange Server 2007 and Exchange Server 2010, but here is where the two releases of the product diverge, and I have to say that I am very glad they do, because there is a marked improvement in the design of the 2010 release over the 2007 release in this area.

Exchange Server 2007 makes use of Access Control Lists (ACL) to apply the permissions. You apply two rights; one that authorizes the Service Account access to Exchange Impersonation rights on the Client Access Server (CAS), and the other that is applied to either an AD account or an entire Mailbox database. This is somewhat kludgey because you either do it on an account-by-account basis, or you do it on an entire mailbox database.

Exchange Server 2010 allows a much finer-grained control of the application of permissions because it makes use of Role-Based Access Control (RBAC) to apply the permissions. Instead of applying the tokens to an account or a mailbox database, you can define a scope to which the permission applies. This means that you can get all the users in a group, for example, and set the permission for that scope.

So let’s deal with applying these permissions to each of the server versions, one by one.

Applying Exchange Impersonation Permissions on Exchange Server 2007

As I mentioned, Exchange 2007 requires that you apply two rights to be able to get Exchange Impersonation working:

  • ms-Exch-EPI-Impersonation – This right is applied to the Client Access Server and grants the Service Account permission to function as an Exchange Impersonation account on that CAS.
  • ms-Exch-EPI-May-Impersonate – This right is applied on either a user-by-user basis for each of the users that require impersonation to be enabled, or it can be applied on a mailbox database.

The first step is to make sure we have the right setting for the Client Access Server. I have reached the point with Exchange where I do most of my administration from the command line rather than the GUI. I find it much easier to do for a number of things, especially setting permissions. So I am going to give you the command line way to do this. You can do it from the GUI if you stand on your head while juggling balls, whistling the national anthem, and playing a guitar, but that is not my idea of fun. The command line is much easier. I guess that’s what comes of being a Unix geek.

With the release of Exchange Server 2007, Microsoft released the Exchange Management Shell (or Powershell) that allows you to perform most administration functions from the command line. To set the Impersonation permission on your Client Access Server, log on to your Exchange Server machine, and launch the Exchange Management Shell from the Microsoft Exchange Server 2007 program group in the Start menu. At the command line, type the following command (replacing intangereexs with your own CAS and EWS Proxy with the name of your own Service Account):

Add-ADPermission -Identity (Get-ExchangeServer -Identity intangereexs).DistinguishedName 
  -User (Get-User -Identity "EWS Proxy").Identity 
  -extendedRight ms-Exch-EPI-Impersonation

Note that the identity of the Client Access Server is the name of the machine that hosts it, and the identity of the user is the full name of the user. If you don’t know what the user’s full name is, you can type Get-User at the command line, and you will get a list of all the known user names.

If the command succeeds, you should see a message similar to that at the bottom of the window below:

Part3 SettingCASPerms 2007 Exchange Web Services Example   Part 3   Exchange Impersonation

The next step is to set the permissions on each user for which we want to enable Exchange Impersonation. Again, from the command line, we will use the Add-ADPermission commandlet, except this time we are adding the right to the user, not the CAS server. Below are the 4 commands that I used to set these permissions.

Add-ADPermission -Identity (Get-User -Identity "Freddie F. Frogg").DistinguishedName 
  -User (Get-User -Identity "EWS Proxy").Identity 
  -extendedRight ms-Exch-EPI-May-Impersonate

Add-ADPermission -Identity (Get-User -Identity "Harriet H. Hippo").DistinguishedName 
  -User (Get-User -Identity "EWS Proxy").Identity 
  -extendedRight ms-Exch-EPI-May-Impersonate

Add-ADPermission -Identity (Get-User -Identity "Gerald G. Gerarf").DistinguishedName 
  -User (Get-User -Identity "EWS Proxy").Identity 
  -extendedRight ms-Exch-EPI-May-Impersonate

Add-ADPermission -Identity (Get-User -Identity "Edward E. Elifant").DistinguishedName 
  -User (Get-User -Identity "EWS Proxy").Identity 
  -extendedRight ms-Exch-EPI-May-Impersonate

Assuming you have set the permissions properly, your command window should look something like this:

Part3 SettingUserPerms 2007 Exchange Web Services Example   Part 3   Exchange Impersonation

That’s all that is required to set up Exchange Impersonation for these four accounts and the Client Access Server for Exchange 2007. As I mentioned, you can set the ms-Exch-EPI-May-Impersonate permission on a mailbox store basis, but I would not advise doing that unless you have segregated your users in a way that makes sense for you to do this. The last thing you want is to give a service account permission to read a CEO’s e-mail. That would be a serious CLM – Career Limiting Move.

Applying Exchange Impersonation Permissions on Exchange Server 2010

Setting up Exchange Impersonation for Exchange Server 2010 is a lot easier than it is for Exchange Server 2007. The Roll-Based Access Control mechanism allows you to define groups of users and assign those groups of users the ApplicationImpersonation role. Once that is done, any new users that are added to the group are automatically assigned the ApplicationImpersonation permission.

Management Scope

I have used the term “group” here somewhat confusingly, and I’m going to perpetuate that confusion by using a security group as the basis for defining the permission, but that is not the only way to do this. The ApplicationImpersonation role has to be assigned to a ManagementScope.

So what is a ManagementScope? A ManagementScope is a logical group of users that are defined by a common set of criteria. It is not necessarily an Active Directory Security Group, although, for the purposes of our example, I will be using a security group to define it. You can define the scope to consist of users who meet a certain criteria, such as having a common manager.

You define a ManagementScope by using the New-ManagementScope commandlet. The RecipientRestrictionFilter parameter allows you to define the criteria that select a list of users. In this case, I am looking for all users that are members of the Sales security group (fully qualified: gruenbaums.internal.intangere.com/Users/Sales). The following command creates a management scope that will only include members of that group.

New-ManagementScope -Name:"SalesImpersonation" 
        -RecipientRestrictionFilter {memberofgroup -eq 
        "cn=Sales,cn=Users,DC=gruenbaums,DC=internal,DC=intangere,DC=com"}

The string at the end of the command line is a Distinguished Name string from the Active Directory that represents the Sales security group. There are a number of different filters that can be applied to create a management scope. The nice thing about this one is that if I add or remove a user to the Sales security group, the scope defined here will ensure that the permissions for the user are affected accordingly.

Role Assignment

Once the management scope exists, one or more management roles can be applied to the management scope. In this case, I am going to assign the ApplicationImpersonation role for the ewsproxy user to the management scope that I just created, and I am going to call the role SalesImpersonation.

New-ManagementRoleAssignment -Name:"SalesImpersonation" 
         -Role:ApplicationImpersonation 
         -User:"ewsproxy@gruenbaums.internal.intangere.com" 
         -CustomRecipientWriteScope:"SalesImpersonation"

Just to confuse the issue, I called my scope SalesImpersonation, too. So in the command line above, the first occurrence of SalesImpersonation refers to the name of the management role assignment that is being created whereas the second one refers to the name of the management scope that was created in the previous step.

Part3 SettingUserPerms 2010 Exchange Web Services Example   Part 3   Exchange Impersonation

Now that the impersonation permissions have been set on both machines, we are ready to start testing.

The Sample Code

The source code for this article only contains Java code, and all the testing for this is through soapUI. The code contains a completely new version of the EWSAPI example in part 2, so you need to create a new Eclipse workspace in which to set it up.

By now, I am assuming you have worked through part 1 and part 2 of this series and are going to be okay with setting up your environment. If not, you should go back to part 2 and read the section on code setup. You can download the source code for this article and set it up by following the instructions in part 2. From the standpoint of the client application, this is not difficult to do. Most of the problems are related to the set up of the impersonation permissions.

What’s New?

The code that you will be looking at is very similar to the code for part 2. The first difference is that the namespaces for all of these objects have changed because I messed up and only realized it as I was building the code for this article. Instead of the org.tsg.ews workspace, all the code now sits in the com.tsg.ews workspace.

The following code is new:

  • EWSImpersonationCalendar.java – This class, in the com.tsg.ews package, contains the definition of the new CalendarService WebService. It performs the same functions as the AppointmentService in part 2, but it does so using Exchange Impersonation.
  • ExchangeServer.java – This class, in the com.tsg.ews.service package, is a descriptor for an Exchange Server and contains the server name and the Server Account authentication credentials.
  • ServerList.java – This class, in the com.tsg.ews.service package, allows for the registration of more than one Exchange Server. I’m really using this class to handle the persistence of the server list to an XML file, but I wrote it while I was experimenting with JAXB so I am not going to spend a lot of time covering the details.

The following code has changed:

  • ExchangeService.java – This class, in the com.tsg.ews.service package, has been changed to support server registration so that a server can be re-used and only the e-mail address of the User Account has to be supplied to operate on their mailbox.
  • EWSResponse.java – This class, in the com.tsg.ews.service package, has been changed to handle receiving the SOAP fault messages that can be returned in response to call to the Exchange Web Service. It provides more detailed information about failures and can help diagnose why a call to the server failed. This is especially important when you are trying to get the permissions right.

So now that you know what is new and what has changed, let’s walk through the code and understand what is going on.

EWSImpersonationCalendar

Open EWSImpersonationCalendar.java, and scroll down to line 16. This class is very similar to EWSCalendar.java, but there are a couple of very small changes.

RegisterServer

Line 16 through 23 define a new operation, RegisterServer, that did not exist in part 2. The operation takes the name of the Exchange Client Access Server, the domain name, a user name and a password. We will use this operation to register the server name and its associated Service Account.

@WebMethod(operationName = "RegisterServer")
@WebResult(name = "RegisterServerResponse")
public EWSResponse setServer(@WebParam(name = "ExchangeServer") String hostName,
		@WebParam(name = "Domain") String domain,
		@WebParam(name = "User") String username,
		@WebParam(name = "Password") String passwd) {
	return ExchangeService.registerServer(hostName, domain, username, passwd);
}

Line 22 makes a call to ExchangeService.registerServer, which  simply creates an ExchangeServer object and stores it in the ServerList. When an ExchangeServer object is instantiated, it is assigned a UUID, which is returned as a string from the registerServer method call. This UUID, which is returned as a ServerID, is used in later calls.

I’m not going to spend a lot of time discussing the internal workings of registerServer. When I wrote the code, I was experimenting with JAXB, so there is some stuff that needs to be cleaned up, but it does what I need it to do reasonably well.

CreateAppointment

Lines 27 through 49 define the CreateAppointment operation, which is carried over from EWSCalendar. The difference lies in the first two parameters to this operation:

  • The ServerID parameter is the ServerID that was received in response to a previous RegisterServer call.
  • The UserAccount parameter is the e-mail address of the UserAccount for which the appointment is to be created.
@WebMethod(operationName = "CreateAppointment")
@WebResult(name = "Response")
public EWSResponse createAppointment(@WebParam(name = "ServerID") String serverID,
		@WebParam(name = "UserAccount") String account,
		@WebParam(name = "Description") String description, 
		@WebParam(name = "Location") String location, 
		@WebParam(name = "StartDateTime") GregorianCalendar startDate, 
		@WebParam(name = "EndDateTime") GregorianCalendar endDate,
		@WebParam(name = "Notes") String notes,
		@WebParam(name = "SetReminder") boolean reminder,
		@WebParam(name = "ReminderMinutes") int minutes) {
	ExchangeService srvc = new ExchangeService(serverID);
	Appointment appt = new Appointment(srvc);
	appt.setEmailAddress(account);
	appt.setDescription(description);
	appt.setLocation(location);
	appt.setStartDate(startDate);
	appt.setEndDate(endDate);
	appt.setNotes(notes);
	appt.setReminder(reminder);
	appt.setReminderMinutes(minutes);
	EWSResponse resp = appt.create(); 
	return resp;
}

In line 36, we instantiate a new ExchangeService object, just as we did in line 31 of EWSCalendar, except that this time we are passing in the ServiceID. The ExchangeService object will handle communication with the Exchange Server and in a commercial application, this would really be a pool.

The only other line of code that is different in this code is line 38, where we call setEmailAddress on the Appointment object. The way that I have implemented this code, that e-mail address is the User Account of the calendar in which we will create the calendar item.


Note that were this not code that was already written as a prototype, I would probably do things slightly differently with the object model. Using the e-mail address as an indicator for whether or not to use impersonation is probably not the best idea, but as I said in my previous posts, this is more about showing how to use the technology than it is about where things fit in object models.


GetAppointment and DeleteAppointment

Like CreateAppointment, the only changes to the GetAppointment and DeleteAppointment operations are that we are passing in the ServerID and the e-mail account, and using them to identify the Exchange Server and User Account for which the appointment is to be retrieved.

So where does the work get done?

Right now, you are probably wondering what it is that causes Exchange to behave differently. Nothing in the code that I have shown you really changes the way we communicate with Exchange Server.

The “work” happens in the construction of the SOAP request that will be sent to the Exchange Server, which actually takes place in the SOAPRequest class which is instantiated in Appointment.create(), Appointment.retrieve(), and Appointment.delete(), just before the call to ExchangeService.makeRequest(). We walked through this area of the code in part 2, so I am not going to rehash the instantiation of SOAPRequest, but the following code is extracted from Appointment.create(), lines 322 and 323:

SOAPRequest req = new SOAPRequest(request.toString(), emailAddress);
EWSResponse retVal = service.makeRequest(req);

Note that when we instantiate the SOAPRequest, we are passing in the emailAddress (which may be null) to the constructor, and then we pass the SOAPRequest object into the makeRequest call on the ExchangeService.

Open SOAPRequest.java in the com.tsg.ews.service package and scroll to the getMessage method in line 23:

String getMessage() {
    StringBuilder builder = new StringBuilder();
    builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
    builder.append(String.format("<soap:Envelope xmlns:xsi=\"%1$s\"\n",XSI_URI));
    builder.append(String.format("               xmlns:xsd=\"%1$s\"\n",XSD_URI));
    builder.append(String.format("               xmlns:soap=\"%1$s\"\n",SOAP_URI));
    builder.append(String.format("               xmlns=\"%1$s\"\n",MESSAGES_URI));
    builder.append(String.format("               xmlns:t=\"%1$s\">\n",TYPES_URI));
    builder.append("    <soap:Header>\n");
    if (emailAddress != null) {
    	builder.append(getImpersonation());
    }
    builder.append("    </soap:Header>\n");
	builder.append("<soap:Body>\n");
	builder.append(soapBody);
	builder.append("</soap:Body>\n");
	builder.append("</soap:Envelope>\n");
    return builder.toString();
}

This code builds the SOAP message that will be sent to the Exchange WebService and the important piece is in lines 32 through 34 where, if the emailAddress is not null, we append the results of a call to getImpersonation().

The getImpersonation() method is in lines 43 through 53:

private String getImpersonation() {
    StringBuilder builder = new StringBuilder();
    builder.append("        <t:ExchangeImpersonation>\n");
    builder.append("            <t:ConnectingSID>\n");
    builder.append("                <t:PrimarySmtpAddress>");
    builder.append(emailAddress);
    builder.append("</t:PrimarySmtpAddress>\n");
    builder.append("            </t:ConnectingSID>\n");
    builder.append("        </t:ExchangeImpersonation>\n");
    return builder.toString();
}

The code in getImpersonation builds an ExchangeImpersonation node that contains a ConnectingSID node. This node specifies the User Account on behalf of which the action is being performed. There are three ways of identifying the account to be used:

  • Principal Name – The User Principal Name for the account and it is in the format of an e-mail address. I could have used it as the node name in this call, but I have a reason for using the Primary SMTP Address which I will mention later. This, and the SID are the most efficient authentication mechanisms as they only need to communicate with the Active Directory once.
  • SID – This is the security identifier for a user. SIDs are a string of numbers that have a meaning to Active Directory and Windows, but really don’t mean a whole lot to anyone else and are pretty difficult to know if you are coming from a Unix-based server system.
  • Primary SMTP Address – Each Active Directory account has a primary SMTP address associated with it and this address can be used to authenticate the user. The problem with the primary SMTP address is that it results in two calls to the Active Directory and this is somewhat inefficient. The reason that I chose to use it is that it is often true that the primary SMTP address and the PrincipalName are different, and the primary SMTP address is normally the one used by an employee on his business card, and therefore likely to be in the CRM application.

The upshot of this is that the header for the message now contains an ExchangeImpersonation node that will be read by the Exchange Web Service. This node contains a User Account and Exchange will perform the action on that user’s behalf, even though the login used to connect to the Exchange Server was the Service Account (ewsproxy, in this case).

The full SOAP message for a CreateItem operation call to create a CalendarItem using Exchange Impersonation therefore looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"
               xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
    <soap:Header>
        <t:ExchangeImpersonation>
            <t:ConnectingSID>
                <t:PrimarySmtpAddress>hippoh@intangere.internal.intangere.com</t:PrimarySmtpAddress>
            </t:ConnectingSID>
        </t:ExchangeImpersonation>
    </soap:Header>
    <soap:Body>
	<CreateItem 
		SendMeetingInvitations="SendToNone"
		xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"
		xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
	    <Items>
	        <t:CalendarItem>
	            <t:Subject>Test Appointment</t:Subject>
	            <t:Body BodyType="Text">Something or anothar</t:Body>
	            <t:ReminderIsSet>true</t:ReminderIsSet>
	            <t:ReminderMinutesBeforeStart>10</t:ReminderMinutesBeforeStart>
	            <t:Start>2010-06-28T17:50:00.000-07:00</t:Start>
	            <t:End>2010-06-28T18:30:00.000-07:00</t:End>
	            <t:Location>Whereever</t:Location>
	        </t:CalendarItem>
	    </Items>
	</CreateItem>
    </soap:Body>
</soap:Envelope>

Exchange Impersonation in Action

Let’s now take a look at how this works in practice. The first thing to do is open Eclipse and make sure you have the code built and running on your Glassfish server. The instructions in Part 2 on getting this code running should work for you.

Now that your server is running, open soapUI and create a new project. In the Initial WSDL/ASDL field, type the following URL:

http://localhost:8080/EWSAPI/CalendarService?wsdl

The RegisterServer Call

When you choose the OK button, a new project called CalendarService will be created. Expand the nodes until you can see the RegisterServer node.

Part3 RegisterServer Exchange Web Services Example   Part 3   Exchange Impersonation

With RegisterServer, all you are doing is setting up the parameters for the server machine you want to connect to and you need to provide the username, password and domain for the Service Account you created – in my case, ewsproxy. When the call completes, the SOAP message that is returned contains an ErrorState attribute that should be “NoError”. It also contains a responseMessage attribute that contains a hex string that is the UUID for the server that you just registered. This string is the value for the StringID element in the CreateAppointment call that we will make next.

The CreateAppointment Call

The CreateAppointment call will create a calendar item in the User Account’s mailbox.

Part3 CreateAppointment Exchange Web Services Example   Part 3   Exchange Impersonation

This time, you are going to copy the string that was returned in the response to the RegisterServer call above into the ServerID element. In the UserAccount element, you need to supply the e-mail address of the User Account in whose calendar you want to create the appointment. Complete the rest of the fields per the example in Part 2. When you run this call, you will receive a ChangeKey and an ItemID in the response if it succeeded, and the appointment will be created in the user’s calendar.

Now go and change the e-mail address in the UserAccount element to another User Account for which you have enabled impersonation.  Again, you should get a ChangeKey and ItemID in the response, in which case all is good.

Note that you did not have to provide any credentials for either the first or second CreateAppointment calls. They just worked. Scary thought. Without any special permission, you have been able to create appointments in someone else’s calendar. That’s why I said that although this is powerful functionality, if you don’t secure it properly, it has the potential for significant security risks.

Likely Exception

It is possible that you will hit an Impersonation error along the way. I have seen this error so many times, it is scary:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:CreateAppointmentResponse xmlns:ns2="http://ews.tsg.com/">
         <Response ErrorState="EWSError" 
		responseCode="ErrorImpersonateUserDenied" 
		responseMessage="The account does not have permission to impersonate the requested user."/>
      </ns2:CreateAppointmentResponse>
   </S:Body>
</S:Envelope>

The problem is that the description that I provided above on how to configure impersonation generally works pretty well. But if you are trying to figure out some special criteria for Exchange 2010 in the -RecipientRestrictionFilter parameter when you allocate permissions for impersonation, you can spend hours getting this message, trying to figure out why. Unfortunately, I can’t offer too much advice other than to consult the documentation for the PowerShell add-ins for Exchange Server which are available in the Exchange Server SDK.

In Summary

I have tried to present a fairly extensive example of using Exchange Impersonation. I’ve also tried to make sure you understand that you, as a developer, can transform a secure Microsoft Exchange Server into a pretty big security hole by using Exchange Impersonation without paying attention to security.

There is no doubt that for serious S2S applications that need to interact with an Exchange Server, Exchange Impersonation is incredibly valuable. Just make sure you use it carefully.

Part 4 of this series will discuss the Subscription API and I will provide a working example that shows how to use the Push subscription API.

%d bloggers like this: