Tải bản đầy đủ
3 Example: A return to our string-reversing service

3 Example: A return to our string-reversing service

Tải bản đầy đủ

386

CHAPTER 17

Connecting in the cloud with AppFabric

Figure 17.2 To start
using AppFabric, you
must first create a
namespace. This
acts like a container
for the entire
configuration of ACS
and the Service Bus.
The name of the
namespace has to be
globally unique.

As you can see in figure 17.3, ACS has configured both a Service Bus and an ACS service
for your namespace. The service endpoints for both services will be displayed as shown
in the figure. Notice that the namespace is the hostname of the service endpoints.

Figure 17.3 Once you create a namespace, AppFabric will provision that namespace with
ACS, Service Bus, management endpoints, and security keys.

Download from Wow! eBook

387

Example: A return to our string-reversing service

A management key will be created for you as well. This 32-byte symmetric key is what
you’ll use when accessing the AppFabric management service to perform operations
on your namespace. We won’t explore the management service in this chapter, but
you should check it out. These keys should not be shared outside your organization,
or published in a book where anyone can get ahold of them.

17.3.2 Reviewing the string-reversal service
For this chapter’s purposes, we’ll use a local REST version of the string-reversal service
developed in chapter 15. You can find the complete code for this revised service in the
sample code for this chapter. We’ve removed the entire worker role and Azure-related
code to do this. ACS is about securing REST-based services, and our old service used a
TCP-based binding. We’ve changed it to use REST by using the WebServiceHost and
the WebHttpBinding classes.
The following listing shows how we’re building our simple little service. This code
will start up the service and wait for calls to the service.
Listing 17.1 A simple REST service
using System.ServiceModel;
using System.ServiceModel.Web;
public class svcProcessString
{
public static void Main(string[] args)
{
Console.WriteLine("Starting string reversal servicehost...");
WebServiceHost serviceHost = new

➥ WebServiceHost(typeof(ReverseStringTools));

Creates host to run
service code

WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.None);
serviceHost.AddServiceEndpoint(typeof(IReverseString), binding, new

➥ Uri("http://localhost/processstring"));

try
Starts service host with
{
specified configuration
serviceHost.Open();
Console.WriteLine("String reversal servicehost started.");
}
catch (Exception ex)
{
Console.WriteLine("Could not start string reverser servicehost. {0}",
➥ ex.Message);
}
Console.ReadLine();
}
}

Download from Wow! eBook

388

CHAPTER 17

Connecting in the cloud with AppFabric

If you run this sample string-reversal service, you can make all of the requests to the
service you want. The sample code includes a simple client that will call the service.
The next few steps are going to center around adding code to the service so that it
can read and use SWT tokens. Once that’s done, you can upgrade the client so it can
fetch a token from ACS and use it during a request to the service.

17.3.3 Accepting tokens from ACS
You’ll need to upgrade the service so it can receive and work with ACS tokens. This
code is fairly trivial, and much of it is supplied in the AppFabric SDK, which you’ll
have to install in order to follow these next steps. You can find the SDK on the Azure
portal. It also includes several tools that we’ll look at in the next section.
Exactly how you get the token and where you process it might change, depending
on your business situation and system architecture, but the steps will be generally the
same.
The first step is to grab the token from the incoming message. The token will usually
be included in the header as an authorization entry. In some situations, it can also be
in the URL or in the body of the message, depending on the capabilities of the client.
Exactly how you grab this header will differ based on how you’re receiving the message. In WCF it’s best to do this in a custom ServiceAuthorizationManager class that’s
added to the WCF pipeline when you set up the channel. Every message will flow
through this class, and there you can make a decision about whether to let it through
or deny it access.
In a normal WCF service, you need to use the WebOperationContext to retrieve the
header from the request:
string authorizationHeader =
➥ WebOperationContext.Current.IncomingRequest.Headers
➥ [HttpRequestHeader.Authorization];

This code will get the raw header. You now need to do a few things to make sure this
token is valid, and then you can use it to make decisions.
The SDK has all the sample code you need to build a class called TokenValidator.
We’ve included a refactored version of this class in the chapter’s sample code that’s a
little easier to use. The validator will do a series of checks for you, and if they all pass,
it’ll return true. If the validation fails, the validator will deny access.
validator = new
➥ ACSTokenValidator("dqSsz5enDOFjUvUnrUe5p1ozEkp1ccAfUFyuYpawGW0=",
➥ "StringReversalInc", "http://localhost/stringservice");
if (!validator.ValidateAuthorizationHeader(authorizationHeader))
DenyAccess();

To initialize the validator, you need to pass in three pieces of information:
The signing key
The ACS namespace to check against
The URL of the service the message was sent to

Download from Wow! eBook

389

Example: A return to our string-reversing service

You’re passing in the key, the namespace you set up, called StringReversalInc, and
the URL of the service you’re protecting, http://localhost/stringservice.
You then call the ValidateAuthorizationHeader on the header you pulled off the
message. If this returns false, you’ll deny access by calling a simple little method,
DenyAccess, that sets up the deny message:
private static void DenyAccess()
{
WebOperationContext.Current.OutgoingResponse.StatusCode =
➥ HttpStatusCode.Unauthorized;
WebOperationContext.Current.OutgoingRequest.Headers.Add("WWW➥ Authenticate", "WRAP");
}

That’s all you need to receive the header. Most of the work involves making sure it’s a
valid header and something you can trust. This is the same job the bouncer at the bar
does, when he looks at your driver’s license to make sure it’s a real license and hasn’t
been tampered with.

17.3.4 Checking the token
We’ve put all of the token-checking logic into the ACSTokenValidator class, and we’ve
just discussed how to new up a validator. The validator includes some custom methods, namely Validate and IsHMACValid. When you pass in the header, the validator
will verify several aspects of it to make sure it’s valid. All of these checks test for the
negative; if the test passes, you have a bad token and the validator returns false.
Table 17.1 summarizes the checks that we do in the code.
Table 17.1

Validation checks performed on a token
Check to be made

Purpose

string.IsNullOrEmpty(authHeader)

Makes sure you received a header.

!authHeader.StartsWith("WRAP ")

Ensures the header starts with WRAP.

nameValuePair[0] != "access_token"

Checks that there are two pieces to the
header, and that the first is equal to
access_token.

!nameValuePair[1].StartsWith("\"") ||
!nameValuePair[1].EndsWith("\""))

Checks that the second piece starts and
ends with a slash.

!Validate(GetTokenFromHeader(authHeader))

Grabs the token part of the header and
makes sure it’s valid.

IsHMACValid(token, signingKey)

Makes sure the token has been signed
properly. If this is correct, you know who
sent it.

this.IsExpired(token)

Checks that the token hasn’t expired.
Tokens are vulnerable to replay attacks, so
this is important.

Download from Wow! eBook

390

CHAPTER 17
Table 17.1

Connecting in the cloud with AppFabric

Validation checks performed on a token (continued)
Check to be made

Purpose

this.IsIssuerTrusted(token)

Ensures the sender is recognized as a
trusted source. We’ll cover this shortly.

this.IsAudienceTrusted(token)

Checks that the audience is the intended
destination.

If the header passes all of these checks, you know you have a secure token from a
trusted source, and that it’s meant for you. This is the minimum you’ll want to do to
allow the message through to the service. You may also want to crack open the claim
set in the token to look at what claims have been sent, and make decisions on those
claims. In our example, we’ve mapped in some claims. One is the customer ID number, and the other is the customer’s service level. This might be used to determine
how long the strings they submit to our service can be. They might have to pay more
to reverse longer strings.
That’s all you have to do to enable the service and consume and use ACS tokens for
authorization. Next we’ll look at how you can configure a client to add the authorization header to their requests.

17.3.5 Sending a token as a client
In this section, you’re going to build a simple command-line client. This will make it
easier to focus on the service aspects of the code. Feel free to make it sexy by using Silverlight or WPF.
For this client, we’ll share the contract by sharing a project reference. (Many projects do this, especially when you own both ends of the conversation.) You should
either share the contract through a project reference, or through a shared assembly.
In this case, our biggest customer, Maine Reversal, will be building this client.
We’ve set up a trusted relationship with them by swapping keys and configuring them
in ACS—we’ll look at how to do this in the next section. Maine Reversal won’t be sending in any custom claims of their own, just their issuer identity. This process essentially
gives them a secure username and password.
We’ve created a helpful utility class called ACSTokenValidator (found in the sample code for this chapter) that encapsulates the process of fetching an ACS header
from the AppFabric service. Again, this code is mostly from the SDK samples with some
tweaks we wanted to make. (Why write new code when they give us code that works?)
To call the GetTokenFromACS method, you’ll pass in the service namespace (StringReversalInc), the issuer name (the client’s name, which is MaineReversal in this
case), the signing key that Maine Reversal uses, and the URL that represents your protected resource. This doesn’t have to be the real URI of the intended destination, but

Download from Wow! eBook

391

Example: A return to our string-reversing service

in many cases will be. In security parlance this is referred to as the audience. The
method call looks like this:
string Token = GetTokenFromACS("StringReversalInc",
"MaineReversal",
"ltSsoI5l+8DzLSmvsVOhOmflAsKHBYrGeCR8KtCI1eE=",
"http://localhost/processstring");

The GetTokenFromACS method performs all the work. It uses the WebClient class to
create the request to ACS. If everything goes well, the ACS service will respond with a
token you can put in your authorization header on your request to the string-reversal
service.
The following listing shows how you can request a token from the ACS service.
Listing 17.2 How a client gets a token from ACS
private static string GetTokenFromACS(string serviceNamespace, string
➥ issuerName, string issuerKey, string scope)
{
WebClient client = new WebClient();
Specifies ACS
client.BaseAddress = string.Format("https://{0}
service address
➥ .accesscontrol.windows.net", serviceNamespace);

q

w

Sends authorization
NameValueCollection values =
data
NameValueCollection();
values.Add("wrap_name", issuerName);
values.Add("wrap_password", issuerKey);
values.Add("wrap_scope", scope);

➥ new

byte[] responseBytes = client.UploadValues("WRAPv0.9", "POST", values);
string response = Encoding.UTF8.GetString(responseBytes);

e Gives token

to caller
return response
.Split('&')
.Single(value => value.StartsWith("wrap_access_token=",
➥ StringComparison.OrdinalIgnoreCase))
.Split('=')[1];
}

You have to provide the GetTokenFromACS method with the base address for the
request q. This is a combination of the ACS service address, accesscontrol.windows.
net, and the namespace for the ACS account, StringReversalInc.
To make the call, you need to provide three pieces of data: the issuer name (your
name in the ACS configuration), the signing key, and the namespace of the service
you’re trying to reach w.
At this point, ACS will check your credentials. The issuer name is basically your
username, and the signing key is your password. If everything checks out, ACS will
respond with a valid token that you can attach to your request to the service e.

Download from Wow! eBook

392

CHAPTER 17

Connecting in the cloud with AppFabric

17.3.6 Attaching the token
Attaching the token to the header of the request is fairly simple on most platforms.
You can also put the token information in the URL or the message body. Doing either
isn’t as good as using an authorization header, so only do this if your system doesn’t
support an authorization header.
To add the token to the authorization header, you can add it to the OutgoingRequest.Headers collection:
string authorizationHeader =
➥ string.Format("WRAP access_token=\"{0}\"",
➥ httpUtility.UrlDecode(Token));
WebOperationContext.Current.OutgoingRequest.Headers
➥ .Add("authorization", authorizationHeader);

To attach the token to the header, you need to use the UrlDecode method to decode
it, and then wrap it with the WRAP leading text. This tells the destination service that
the token is a WRAP token. This text will be stripped off by the server once the token is
validated. Then you add the header to the outgoing request using the WebOperationContext class.
That’s all the client needs to do. Your client should be robust enough to handle
any errors in the ACS service call or the ACS token request being denied.
In order for the token validation and generation to work, you have to set up some
configuration in the ACS service: a trusted relationship with the issuer, and some rules.

17.3.7 Configuring the ACS namespace
The ACS needs to be configured for your service. You’ve already learned how to define
a namespace, and the namespace is a container for the rest of the ACS configuration.
You can also chain namespaces together, which is the key mechanism for providing
simple delegation.
Each namespace has four components: issuers, scopes, rules, and token policies.
These elements work together to help secure your REST service.
The AppFabric SDK provides two tools for configuring your service, both of which
run locally and call into the management service: ACM.exe (used from the command
line) and the Azure configuration browser. (You can use the management service as a
third option, but that’ll require more work on your part.) Beyond the tool that sets up
the namespace, there aren’t any management tools on the ACS portal.
The ACM.exe tool can be found in the tools folder where you installed the AppFabric SDK. ACM is most useful when you’re automating a process or want to script the
configuration. But keep in mind that calls to the AppFabric management endpoint
aren’t free, like the Windows Azure management endpoints are.
The Azure configuration browser is shipped with the SDK, but as a sample in
source-code form in a separate download file. You need to load the solution and compile it to use it. This distribution approach is really useful because you can then
extend the tool to meet your needs, and the browser is a lot easier to use than the
command-line tool.

Download from Wow! eBook

Example: A return to our string-reversing service

393

The configuration browser does have a few limitations. First, it’s really ugly, but
that’s OK. The second is that, at this time, it can’t update an existing cloud configuration; it can only deploy a complete configuration. This means that any time you make
a change, you have to delete the configuration in the cloud and completely redeploy
the new configuration. An advantage of this approach is that you can store your configuration locally in an XML file, which aids in backup and configuration management.
You’ll need to provide your service name and your management key with either tool.
For the ACM.exe application, you can put your settings in the app.config file, which
saves you from having to type them in as part of your commands every single time.
ISSUERS

Issuers are actors in the ACS system and represent consumers of your service. When
you create an issuer, you need to provide both a display name and an official issuer
name. Once the issuer is created, a signing key will be generated for you. This is the
key the issuer must sign their requests with when they ask the ACS service for a token.
To create an issuer from a command line, you would use the following command:
acm create issuer
-name:MaineReversal
-issuername:MaineReversal
-autogeneratekey

In the configuration browser
you’ll need to right-click on
the Issuers node and choose
Create. Figure 17.4 shows
how to set up your first client,
Maine Reversal.
Setting up an issuer in the
system is akin to creating a
user. The issuer needs a name
(comparable to a username)
and a signing key (which is
like a password). These are
the credentials the issuer will
use to request an ACS token.
TOKEN POLICY

Figure 17.4 Creating an issuer is easy with the ACS
configuration browser. You’ll need to provide both a display
name and an official name for the issuer. You can use the tool
to automatically create the signing keys.

A token policy defines how
you want your tokens to be
created. Because token-based systems can be vulnerable to token-replay attacks, you’ll
first want to set a lifetime timeout for the token. This is expressed in seconds. When
the token is created, it’ll be stamped with the time when the token will expire. Once
it’s expired, the token will be considered invalid and won’t be accepted by a service.
You have to check for this expiration explicitly when you validate the token. We check
for this in the sample code for the chapter, as seen in the ACSTokenValidator class.

Download from Wow! eBook

394

CHAPTER 17

Connecting in the cloud with AppFabric

Figure 17.5 You’ll need to create a token
policy. This will determine the lifetime of
your tokens, and the key that will be used to
sign your ACS tokens.

The command for creating a token policy at the command line is as follows:
acm create tokenpolicy -name:StringReversalInc -autogeneratekey

To create a token policy in the configuration browser, right-click on the Token Policy
node and select Create. Figure 17.5 shows the Create New Token Policy dialog box,
where you can create a policy for your string service.
The second piece of data you’ll need for your token policy is the signing key, which
can be generated for you. This is the key that will be used to sign the tokens generated
for you by the ACS.
SCOPES

A scope is a container that’s tied to a service URI. It brings together the rules that you
want applied to your service, as well as the token policy you want used for that service.
To create a scope at the command-line level, you’ll need the ID of the token policy
you want to assign to the scope. You can get the tokenpolicyid from the output of the
create tokenpolicy command discussed in the previous section. This is the command for creating a scope:
acm create scope -name:StringServiceScope
-appliesto:http://localhost/processstring
-tokenpolicyid:tp_4cb597317c2f42cba0407a91c2553324

When you’re using the configuration browser, you won’t need to provide the token
policy ID—you’ll be able to choose it from the drop-down list. You can associate a policy to a namespace by creating a scope, as shown in figure 17.6.
There are several advanced uses for scopes that we won’t go into in this chapter.
These include managing a large number of service endpoints with a few scopes, and
chaining namespaces together for delegation.

Figure 17.6 It’s easy to create a scope. A
scope acts as a container for a set of rules for
your service. It also associates a token policy
with the service. You’ll need to define the URI
for the service the scope applies to.

Download from Wow! eBook

Example: A return to our string-reversing service

395

Figure 17.7 Creating
a rule to insert
a claim that includes
the customer’s
customerid. In this
case, you’re relying
on the issuer
of the inbound
request to know
which customer it is.
RULES

Rules are the heart of the ACS system. When a token is created by ACS, the rules for
the related scope are executed. This is the process that allows you to transform the
consumer’s request into something your application can act on. Rules can also be
used to create claims out of thin air, and put them in the resulting token.
For example, suppose you wanted to place a claim in the token that represents the
consumer’s customerid, to make it easier for your service to identify the account the
request is related to. You could create a rule that says, “If this is for issuer MaineReversal, add a claim called customerid with a value of 31415.” Figure 17.7 shows how you
could create this rule.
Another rule you could use would assign a new role value based on mappings
you’ve worked out with the customer. Perhaps their system tracks their users with a
role of ServiceManager—this would be a group the user belongs to at Maine Reversal.
Your system doesn’t know that role name, and you don’t want to add all of your customers’ role types to your system—that would get very complex very quickly. The rule
in figure 17.8 creates the roles claim with the manager value.

Figure 17.8 Creating
a rule that substitutes
the inbound roles
claim for a new one.
Using this rule, you
can map the
ServiceManager role
value that your system
doesn’t know to one
your system does
know—manager.

Download from Wow! eBook

396

CHAPTER 17

Connecting in the cloud with AppFabric

You can then create a rule that finds a claim called roles with a value of SalesManager, and replaces it with a claim called roles that has a value of manager. In this
way you’ve moved the customer configuration and mapping out of your service and
into the authorization service where it belongs.
Creating a rule at the command line is a little more complex than using the configuration browser:
acm create rule -name:MaineReversalMap
➥ -scopeid:scp_e7875331c2b880607d5709493eb2751bb7e47044
➥ -inclaimissuerid:iss_6337bf129f06f607dac6a0f6be75a3c287b7c7fa
➥ -inclaimtype:roles -inclaimvalue:ServiceManager
➥ -outclaimtype:roles -outclaimvalue:manager

To find the IDs of the scope and issuer, you can use these commands: acm getall
scope and acm getall issuer.

17.3.8 Putting it all together

en

Download from Wow! eBook

k
to

s

im

cla

ve

nd

i
ce

Se

Re

You’ve come a long way in stopping illicit use of your service. Now you can control
who uses it and how they use it. You’ve updated your service to consume tokens,
you’ve updated the client to submit tokens with service requests, and you’ve prepared
the ACS service with your configuration.
How does this all work? In
3
this simple scenario, the client
Use rules to map claims
requests an access token from
ACS, providing its secret key and
ACS
issuer name. ACS validates this
(trusted authority)
and creates a token, using the
4
S
AC
scope rules you set up to create
e
r
gu
claims in the new token. The clinfi
o
C
2
1
ent then attaches the token to
5
REST service
Send request with token
the message in the authorization
Client
(protected resource)
header.
Figure 17.9 should look familFigure 17.9 How the three actors work together to
securely access a REST service. The service configures
iar; it’s much like the DMV examACS; the client asks for a token; ACS creates a token,
ple (see figure 17.1), but it shows
based on rules; and then the client submits this token
the technical actors and how they
with its service request.
interact.
When your service finally receives the message, you’ll grab the token from the
header and verify it. You want to make sure that it’s valid and hasn’t been tampered
with. Once you trust it, you can make decisions on what to do.
In our example, you can take the customerid value and verify that they’re still a
paying customer, and if so, respond to their request. You can stop using the token at
this point and respond like normal, or you can shred the token and use the claims
throughout the application.