Thesis - Software as a Service
September 28th, 2008

My interest in Software as a Service (SaaS) born during a trip to Microsoft in 2006. Looking for an interesting topic to elaborate on my graduate thesis, I started digging on different areas and asking different colleagues and friends. Initially, influenced by the work of Arvindra Sehmi, I got interested in agent programming (BDI agents, multi-agents systems, etc). Later, Eugenio Pace, a mentor and friend, commented me about an emerging model of software distribution. After a couple of months, Alejandro Jack (mentor and friend also), contacted me with John DeVadoos (former director of the Architecture Strategy Team at Microsoft, now leading the patterns & practices team). This group was formed by Gianpaolo Carraro and Fred Chong initially and last year Eugenio joined them and Fred left. Their daily job consists of researching the Software as a Service model from the architectural point of view. As part of this challenge, the group establishes relationships with clients interested in the model and with product teams looking for feedback to shorten the gap
on the platform. The group also publishes a number of papers and proof of concepts using Microsoft technologies. I did a good connection with them and finally decided to pick Software as a Service as the topic for my thesis.
Throughout the last two years I’ve been collaborating with this group writing proof of concepts, preparing and delivering workshops. On the right, it’s me working with Eugenio (on the left) at building 20 one year ago (Gianpaolo is taking the picture). The post-its on the wall are the user stories for Northwind Hosting (I was happy to see Google App Engine six months later as a validation of our thinking).
I’ve witnessed the growth of Software as a Service since it was in the initial stages up to now that has started being adopted by the industry and lately has been extended to a broader term: Cloud Computing.
The thesis is the sum of all the experience I gather along these years and it’s an attempt to summarize and compress the taxonomy of Software as a Service applications on a single model; that is more about “breadth” than “depth”. This model was based on Feature Modeling, a technique used in Software Product Lines (SPL). Feature Modeling is a method and notation to elicit and represent common and variable features of the system in a system family. It was first proposed by Kang et al in Feature Oriented Domain Analysis by the Software Engineering Institute (SEI, 90). It’s been used lately in Generative Software Development (Czarnecki, 2005), which aims at modeling and implementing system families in such a way that a given system can be automatically generated from specification written in one or more textual or graphical domain specific languages (DSLs). Since SaaS and Cloud Computing are evolving fast I wanted to separate the problem space from the solution space allowing the individual development and growth of each of them. Feature Modeling helped because it was focused on the capabilities. I didn’t try to use Feature Modeling to automate the generation of this kind of systems, though. The priority was having a model that allow me to frame and explain Software as a Service systems.
Separation between problem and solution space (Overview of Generative Software Development, Czarnecki)
Part of defining the problem space consisted of doing a domain analysis. This is an activity of SPL aiming to characterize a domain by understanding their commonality and variability. If you follow this blog you might have read the cloud computing taxonomy map post. That was an exercise during the domain analysis that helped me understand the different scopes, offerings and features of Software as a Service.
Domain analysis of Software as a Service
With this information, I’ve spent a couple of days pasting post-its on the wall, trying to group the features and capabilities in a way that makes sense. The end result was this “onion” diagram where each category holds the different features of Software as a Service.
The model is later refined, from the taxonomy above to the capability layer (problem space) and finally to the implementation layer (solution space) as shown in the following figure.
The thesis then describes each of the capabilities in a high level fashion and then proposes patterns, architecture styles and technologies to implement those capabilities.
The following feature tree is an instance of the capability layer of the model for the LitwareHr application (grayed capabilities are not part of LitwareHr system). This content is in Spanish, but it will be soon available in English.
The thesis also includes other non technical aspects like
- Adoption and diffusion analysis of SaaS based on market research
- Barriers for adoption
- Historical context (starting from specialization in the 19th century, passing through outsourcing, mainframes and what not
- Roles and ecosystem
I want to thanks again to all the people that helped directly or indirectly: my family and fiancee, Alejandro Jack, Gustavo López, Eugenio Pace, Gianpaolo Carraro, Fred Chong, Arvindra Sehmi, Ariel Schapiro, Angel “Java” Lopez and to all Southworks.
Fell free to download the Spanish version and let me know if you find it useful to matias at southworks dot net.
Microsoft Identity Framework (Zermatt) #1
September 27th, 2008
In these series I want to show the usage of Zermatt to solve some typical scenarios in identity management. I will assume that the reader is already familiar with concepts like security token service, claims, tokens, credentials, etc. If not, you can read this article from Vittorio Bertocci on July 2008 issue of the Architecture Journal.
This first post will be about the simplest scenario in identity management: the Active Client with a single STS. In other words, a client that calls a web service with a policy that says that you need to use a certain token to talk to him.
The sequence of actions in this scenario is:
- Since the client needs to obtain a token before talk to the service, it will make a WS-Trust call to the STS sending some kind of credentials. It could be Windows credentials, a X509 certificate, username and password or maybe another token (we’ll leave that for a future post).
- The STS will authenticate the caller and probably will output some claims about him. These claims might come from some repository like a database or AD. If the client is using Windows credentials, maybe the claims will be the groups the user belongs, the email and the full name. If the client is using username and password, the claims could be the roles stored in a database table. However, the claims could be anything you want that will be used later to perform access checks on the service.
- The response is sent to the client containing an RSTR (Request Security Token Response) that will contain the requested security token with the claims. This token can be encrypted so only the service can decrypt it. For doing that, the STS will use the public key of the service certificate. The token will be also signed by the STS to avoid untrusted issuers. To sign the token, the STS will use a private key.
- Finally, the client calls the service using the token obtained in 3. The service will only accept tokens of trusted issuers. Since there is a trust relationship between our STS and the service, the latter will have the public key of the STS that will allow him to check the signature of the token.
Having the sequence of things defined, we’ll see how this can be achieved using Zermatt Beta 1. First, let me put some context on some of the decisions I will take.
One of the things I really like about Zermatt is the WSTrustClient class. This simple class allows anyone to issue a RST to an STS. If you ever tried to implement the approach depicted above with WCF you might have met with the wsFederationHttpBinding. This binding encapsulates the sequence above so the client doesn’t have to worry about the behind the scenes of obtaining tokens. While this is good in some cases, when I started playing with this identity architecture, I felt that there was some kind of "black magic" happening. As someone that uses Reflector as its primary tool to solve most of the challenges with emergent technologies, I wanted to know how all this worked and I wanted to have control over the tokens. Well, knowing the theory helps a lot. I also recommend you to use WSTrustClient and "do it yourself".
[1] Issue token
We will use a simple username and password to call the STS. The following method uses the WSTrustClient class to call the STS in localhost:6001/IPSTS. Notice the usage of WCF WSHttpBinding with Windows credentials. The RequestType property of the RequestSecurityToken class is not the regular WS-Trust issue SOAP action URI(http://schemas.xmlsoap.org/ws/2004/04/security/trust/RST/Issue), but a custom one. This is because Zermatt supports different WS-Trust versions and will do the transformation to the correct URI on runtime depending on the WSTrust version (by default is Feb 2005 version)
static SecurityToken GetToken(string username, string password) { var binding = new WSHttpBinding(SecurityMode.Message); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows; var ipAddress = new EndpointAddress("http://localhost:6001/IPSTS"); var client = new WSTrustClient(binding, ipAddress); client.ClientCredentials.Windows.ClientCredential.UserName = username; client.ClientCredentials.Windows.ClientCredential.Password = password; var rst = new RequestSecurityToken(); rst.RequestType = "http://schemas.microsoft.com/idfx/requesttype/issue"; var token = client.Issue(rst); return token; }
[2] Authenticate & fill tokens with claims
The STS in Zermatt have two methods to override. The first is GetScope which provides information related to the "scope" where this STS will be used (things like certificates to use to encrypt, to sign, etc) The second is GetOutputSubjects that will provide information about the subject (like claims). Here we are creating one claim of type http://schemas.xmlsoap.org/claim/Group for each group the user belongs.
public override ClaimsIdentityCollection GetOutputSubjects(Scope scope, IClaimsPrincipal principal, RequestSecurityToken request) { var claimsIdentities = new ClaimsIdentityCollection(); var wI = (WindowsIdentity)principal.Identity; var identity = new ClaimsIdentity(principal.Identity); foreach (IdentityReference iD in wI.Groups) { var groupName = new SecurityIdentifier(iD.Value).Translate(typeof(NTAccount)).ToString(); identity.Claims.Add(new Claim(Constants.GroupClaimType, groupName)); } claimsIdentities.Add(identity); return claimsIdentities; }
[3] Encrypting and signing the RSTR
In the GetScope method we are indicating how we are going to encrypt and sign the token
protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request) { Scope scope = new Scope(request, SecurityTokenServiceConfiguration.SigningCredentials); scope.EncryptingCredentials = new X509EncryptingCredentials( CertificateUtil.GetCertificateByThumbprint(StoreName.TrustedPeople, StoreLocation.LocalMachine, Constants.EncryptingCertificateThumbprint)); return scope; }
[4] Call the service using the token
At this point the client have a token in his hands. Now we need to use it on the outgoing message (on the WS-Security header). Here, we use the WSTrustClientCredentials class and set the SecurityToken we obtained before.
private static void CallService(SecurityToken token) { var client = new ShippingManagerClient(); var credentials = new WSTrustClientCredentials(); credentials.IssuedSecurityToken = token; client.Endpoint.Behaviors.Remove<ClientCredentials>(); client.Endpoint.Behaviors.Add(credentials); credentials.ConfigureChannel(client.InnerChannel); client.NewShipment(); client.Close(); }
The client will use a custom binding (not the wsFederationHttpBinding). Notice the issuer address is http://notused. We can do this because we are using WSTrustClientCredentials that creates a custom WCF IssuedSecurityTokenProvider. This provider will shortcircuit the interaction with the issuer and will simply return the token we set in the IssuedSecurityToken on WSTrustClientCredentials.
<bindings> <customBinding> <binding name="CustomBinding_IShippingManager"> <security defaultAlgorithmSuite="Default" authenticationMode="IssuedTokenForCertificate" requireDerivedKeys="true" securityHeaderLayout="Strict" includeTimestamp="true" keyEntropyMode="CombinedEntropy" messageProtectionOrder="SignBeforeEncryptAndEncryptSignature" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10" requireSignatureConfirmation="true"> <issuedTokenParameters keyType="SymmetricKey" tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"> <issuer address="http://notused" binding="wsHttpBinding" /> <issuerMetadata address="http://notused/mex" /> </issuedTokenParameters> <localClientSettings cacheCookies="true" detectReplays="true" replayCacheSize="900000" maxClockSkew="00:05:00" maxCookieCachingTime="Infinite" replayWindow="00:05:00" sessionKeyRenewalInterval="10:00:00" sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true" timestampValidityDuration="00:05:00" cookieRenewalThresholdPercentage="60" /> <localServiceSettings detectReplays="true" issuedCookieLifetime="10:00:00" maxStatefulNegotiations="128" replayCacheSize="900000" maxClockSkew="00:05:00" negotiationTimeout="00:01:00" replayWindow="00:05:00" inactivityTimeout="00:02:00" sessionKeyRenewalInterval="15:00:00" sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true" maxPendingSessions="128" maxCachedCookies="1000" timestampValidityDuration="00:05:00" /> <secureConversationBootstrap /> </security> <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16" messageVersion="Default" writeEncoding="utf-8"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="65536" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> </textMessageEncoding> <httpTransport manualAddressing="false" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" allowCookies="false" authenticationScheme="Anonymous" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" keepAliveEnabled="true" maxBufferSize="65536" proxyAuthenticationScheme="Anonymous" realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false" useDefaultWebProxy="true" /> </binding> </customBinding> </bindings>
On the service side we want to check that the token was issued by someone we trust. To do that, Zermatt provides something called SamlSecurityTokenRequirements (this code is somewhere in the host).
var handler = collection[typeof(SamlSecurityToken)] as Saml11TokenHandler; handler.SamlSecurityTokenRequirement.IssuerTokenAuthenticators.Clear(); handler.SamlSecurityTokenRequirement.IssuerTokenAuthenticators.Add( new X509SecurityTokenAuthenticator( new TokenIssuerCertificateValidator(Constants.IssuerCertificateThumbprint)));
The TokenIssuerCertificateValidator derives from X509CertificateValidator which has a virtual method ValidateToken. The input parameter of this method is the certificate used to sign the token. The following code will check the thumbprint of the incoming token is the one we expect.
public override void Validate( X509Certificate2 incoming ) { if ( incoming.Thumbprint != issuerCertificate.Thumbprint ) { throw new SecurityTokenException( "Issuer certificate validation failed" ); } }
Since the service has the private key that allows decrypting the token, we can read the claims. Zermatt will populate the ClaimsPrincipal object that will be accessible from any place in the service pipeline. The WCF ServiceAuthorizationManager might be the place where you want to do check access using the ClaimsPrincipal.
var identity = ClaimsPrincipal.Current.Identity as IClaimsIdentity;
identity.Claims…