Not strictly identity related, but anything to make my life easier is all good:
A little while ago I asked a question on the TFS forum. It was a simple request to email the person who requested the build when the build was finished, including the status of the build (success/failure).
The answer I received was to implement a web service, with a known interface, and subscribe this service to receive events from the build process. The web service receives a chunk of xml, which I'd need to parse to retrieve the user name. In turn I'd need to look up the email of this user (somehow!). All a bit of pain, frankly. Suffice to say, we never seemed to have the time to do this.
Recently I have been investigating gated builds, and in particular an open source product called OpenGauntlet. I noticed that these guys are emailing developers on start and on finish of their gated builds - brilliant. A quick look through the source code showed me how:
The TFS API is rich in functionality, but the important interface in this case is
IGroupSecurityService. Here's how it can be used to retrieve a user's email address:
// First we need to get our TFS and then find it's build store
TeamFoundationServer server = new TeamFoundationServer( serverUrl, CredentialCache.DefaultCredentials );
// Now get the correct interface
IGroupSecurityService gss = ( IGroupSecurityService ) server.GetService( typeof( IGroupSecurityService ) );
// Retrieve the identity
Identity objIdentity = gss.ReadIdentity( SearchFactor.AccountName, userName, QueryMembership.None );
// Get the email address
string email = objIdentity.MailAddres;
The userName is retrieved like so:
BuildStore store = ( BuildStore ) server.GetService( typeof( BuildStore ) );
// Get the build from our uri
BuildData bd = store.GetBuildDetails( buildUri );
// user name
string userName = buildData.RequestedBy;
Knowing this, it is a simple matter of writing a task to email the developer, passing serverUri, buildUri etc as optional parameters.
Simple.
Wednesday, 25 June 2008
Friday, 28 March 2008
Federation over a Duplex channel
Me again.
This time I wanted to do something a little beyond the norm, at least as far as the available examples are concerned. I would really like to implement a publish/subscriber service, where an application subscribes to various system wide events. Seems like a job for a Duplex channel - the client subscribes to service, and also provides an endpoint as a means to receive events.
The big catch here is that I'd love this service to execute under the same security context as the rest of my architecture, i.e. I'd like the service to be federated with my STS.
After trawling the net, I found several useful snippets, but nothing that solved the whole problem. Specifically, I wanted to achieve all this via config, if possible.
So here's how I did it...
The client binding looks like
<customBinding>
<binding name="DuplexBinding">
<security authenticationMode="SecureConversation">
<secureConversationBootstrap authenticationMode="IssuedToken">
<issuedTokenParameters tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<issuer address="http://localhost:8080/STS/Slave" binding="wsFederationHttpBinding" bindingConfiguration="UserNameFederationBinding">
<identity>
<dns value="My STS" />
</identity>
</issuer>
<issuerMetadata address="http://localhost:8080/STS/Slave/mex" />
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<compositeDuplex clientBaseAddress="http://localhost:8080/Client"/>
<oneWay/>
<textMessageEncoding />
<httpTransport />
</binding>
</customBinding>
Here, the original authentication mechanism was via a Username token. Notice the order of the elements within thetag - this foxed me for a good while before it clicked! Also note that the authentication mode is set to 'SecureConversation', with the bootstrap mode set to 'IssuedToken'. Initially I thought this seemed counter-intuitive, but when you consider the order in which WCF (or more correctly, WS-Security) performs its negotiation, this makes more sense.
In the service, the binding looks like
<customBinding>
<binding name="DuplexBinding">
<security authenticationMode="SecureConversation">
<secureConversationBootstrap authenticationMode="IssuedToken">
<issuedTokenParameters tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<issuer address="http://localhost:8080/STS/Slave" binding="wsHttpBinding" />
<issuerMetadata address="http://localhost:8080/STS/Slave/mex" />
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<compositeDuplex/>
<oneWay/>
<textMessageEncoding />
<httpTransport />
</binding>
</customBinding>
The service works in exactly the same way a standard Duplex service would, i.e. it simply retains a reference to the callback channel, like so
IRegisterCallback callback = OperationContext.Current.GetCallbackChannel();
in order to use this when necessary.
I hope this helps anybody else wishing to do the same, or similar, with their architecture.
Cheers
Graeme
This time I wanted to do something a little beyond the norm, at least as far as the available examples are concerned. I would really like to implement a publish/subscriber service, where an application subscribes to various system wide events. Seems like a job for a Duplex channel - the client subscribes to service, and also provides an endpoint as a means to receive events.
The big catch here is that I'd love this service to execute under the same security context as the rest of my architecture, i.e. I'd like the service to be federated with my STS.
After trawling the net, I found several useful snippets, but nothing that solved the whole problem. Specifically, I wanted to achieve all this via config, if possible.
So here's how I did it...
The client binding looks like
<customBinding>
<binding name="DuplexBinding">
<security authenticationMode="SecureConversation">
<secureConversationBootstrap authenticationMode="IssuedToken">
<issuedTokenParameters tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<issuer address="http://localhost:8080/STS/Slave" binding="wsFederationHttpBinding" bindingConfiguration="UserNameFederationBinding">
<identity>
<dns value="My STS" />
</identity>
</issuer>
<issuerMetadata address="http://localhost:8080/STS/Slave/mex" />
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<compositeDuplex clientBaseAddress="http://localhost:8080/Client"/>
<oneWay/>
<textMessageEncoding />
<httpTransport />
</binding>
</customBinding>
Here, the original authentication mechanism was via a Username token. Notice the order of the elements within the
In the service, the binding looks like
<customBinding>
<binding name="DuplexBinding">
<security authenticationMode="SecureConversation">
<secureConversationBootstrap authenticationMode="IssuedToken">
<issuedTokenParameters tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<issuer address="http://localhost:8080/STS/Slave" binding="wsHttpBinding" />
<issuerMetadata address="http://localhost:8080/STS/Slave/mex" />
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<compositeDuplex/>
<oneWay/>
<textMessageEncoding />
<httpTransport />
</binding>
</customBinding>
The service works in exactly the same way a standard Duplex service would, i.e. it simply retains a reference to the callback channel, like so
IRegisterCallback callback = OperationContext.Current.GetCallbackChannel
in order to use this when necessary.
I hope this helps anybody else wishing to do the same, or similar, with their architecture.
Cheers
Graeme
Thursday, 21 February 2008
Token expiration dates
Here's another (brief) problem regarding the setting of validity dates within a token, or more specifically an RSTR (Issue), as implemented in WCF.
I was attempting to set validity date information within an RSTR. Firstly, I updated the dates within the SAML assertion itself, like so
DateTime validFrom = DateTime.UtcNow;
DateTime validTo = validFrom + TimeSpan.FromDays(1);
SamlAssertion assertion = new SamlAssertion();
assertion.Conditions = new SamlConditions(validFrom, validTo);
Avid readers (!) will note that the WS-Trust schema defines the following elements for specifying the valid date range of the accompanying token:
where wst namespace is http://schemas.xmlsoap.org/ws/2005/02/trust
and wsu namespace is
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd
It is these dates that WCF will use to determine the lifetime of a token. The trick, when setting these dates in your RSTR, is to supply them in the correct format. If you don't you'll receive a very unhelpful error message within your client, the gist of which will be 'I don't understand your date formats, but I'm not going to tell you why'. After alot of messing, and finally resorting to code decomposition, I determined that the correct format is:
time.ToUniversalTime().ToString("o");
The RSTR is modified like so (within the OnWriteBodyContents method of the RequestSecurityTokenResponse class as per the WCF samples), for completeness:
// This part tells WCF what the lifetime of the token is, without having to parse the token itself
writer.WriteStartElement("Lifetime", TrustNS);
writer.WriteElementString("Created", UtilityNS, FormatDate(validFrom));
writer.WriteElementString("Expires", UtilityNS, FormatDate(validTo));
writer.WriteEndElement();
I guess that, with hindsight, it is reasonably obvious that the time should be UTC, but as they say, hindsight is a wonderful thing.
Hopefully, this will save somebody at least a small amount of time in future!
Fluffy
I was attempting to set validity date information within an RSTR. Firstly, I updated the dates within the SAML assertion itself, like so
DateTime validFrom = DateTime.UtcNow;
DateTime validTo = validFrom + TimeSpan.FromDays(1);
SamlAssertion assertion = new SamlAssertion();
assertion.Conditions = new SamlConditions(validFrom, validTo);
Avid readers (!) will note that the WS-Trust schema defines the following elements for specifying the valid date range of the accompanying token:
wst:RequestSecurityTokenResponse/wst:Lifetime
wst:RequestSecurityTokenResponse/wst:Lifetime/wsu:Createdwst:RequestSecurityTokenResponse/wst:Lifetime/wsu:Expires
where wst namespace is http://schemas.xmlsoap.org/ws/2005/02/trust
and wsu namespace is
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd
It is these dates that WCF will use to determine the lifetime of a token. The trick, when setting these dates in your RSTR, is to supply them in the correct format. If you don't you'll receive a very unhelpful error message within your client, the gist of which will be 'I don't understand your date formats, but I'm not going to tell you why'. After alot of messing, and finally resorting to code decomposition, I determined that the correct format is:
time.ToUniversalTime().ToString("o");
The RSTR is modified like so (within the OnWriteBodyContents method of the RequestSecurityTokenResponse class as per the WCF samples), for completeness:
// This part tells WCF what the lifetime of the token is, without having to parse the token itself
writer.WriteStartElement("Lifetime", TrustNS);
writer.WriteElementString("Created", UtilityNS, FormatDate(validFrom));
writer.WriteElementString("Expires", UtilityNS, FormatDate(validTo));
writer.WriteEndElement();
I guess that, with hindsight, it is reasonably obvious that the time should be UTC, but as they say, hindsight is a wonderful thing.
Hopefully, this will save somebody at least a small amount of time in future!
Fluffy
SAML token serialization
So, as promised, my first post is regarding simple serialization/de-serialization of a SAML token.
The driver for this exercise was to implement the 'Renew' verb of the WS-Trust specification, using the framework that WCF provides. To my endless frustration, the development team over at Microsoft stopped short of implementing the entire WS-Trust spec for anything but SCTs! Instead we're left with just 'Issue'.
My STS is signing tokens using self-issued X509 certificates (one for the STS and one for each client). Here's the (shortened) code for the serialization of the token:
///
/// Write the given token into an XmlWriter object
///
///
///
public virtual void WriteToken(SamlSecurityToken token, XmlWriter writer)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
serializer.WriteToken(writer, token);
}
Now, when I receive the token back from the client for renewal I need to de-serialize it in order to update its validity time span. Here's the code for this:
///
/// Read the token from an XmlDictionaryReader reader
///
///
///
public virtual SamlSecurityToken ReadToken(XmlDictionaryReader reader)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
System.Collections.Generic.List tokens = new System.Collections.Generic.List();
tokens.Add(SigningToken);
tokens.Add(UnencryptingToken);
System.IdentityModel.Selectors.SecurityTokenResolver resolver =
System.IdentityModel.Selectors.SecurityTokenResolver.CreateDefaultSecurityTokenResolver(tokens.AsReadOnly(), false);
SamlAssertion assertion = new SamlAssertion();
assertion.ReadXml(reader, new SamlSerializer(), serializer, resolver);
return new SamlSecurityToken(assertion);
}
Note that here the signing token is the certificate used by the STS to sign the assertion, and the unencrypting token is the private key equivalent of the certificate used to encrypt the proof key in the RSTR.
The point here is that in order to de-serialize the token, you're going to need the private key of the certificate used to encrypt the original. I guess this isn't a problem when you're in control of client certification, but I think you can see why this might cause issues.
The driver for this exercise was to implement the 'Renew' verb of the WS-Trust specification, using the framework that WCF provides. To my endless frustration, the development team over at Microsoft stopped short of implementing the entire WS-Trust spec for anything but SCTs! Instead we're left with just 'Issue'.
My STS is signing tokens using self-issued X509 certificates (one for the STS and one for each client). Here's the (shortened) code for the serialization of the token:
///
/// Write the given token into an XmlWriter object
///
///
///
public virtual void WriteToken(SamlSecurityToken token, XmlWriter writer)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
serializer.WriteToken(writer, token);
}
Now, when I receive the token back from the client for renewal I need to de-serialize it in order to update its validity time span. Here's the code for this:
///
/// Read the token from an XmlDictionaryReader reader
///
///
///
public virtual SamlSecurityToken ReadToken(XmlDictionaryReader reader)
{
System.ServiceModel.Security.WSSecurityTokenSerializer serializer = new System.ServiceModel.Security.WSSecurityTokenSerializer();
System.Collections.Generic.List
tokens.Add(SigningToken);
tokens.Add(UnencryptingToken);
System.IdentityModel.Selectors.SecurityTokenResolver resolver =
System.IdentityModel.Selectors.SecurityTokenResolver.CreateDefaultSecurityTokenResolver(tokens.AsReadOnly(), false);
SamlAssertion assertion = new SamlAssertion();
assertion.ReadXml(reader, new SamlSerializer(), serializer, resolver);
return new SamlSecurityToken(assertion);
}
Note that here the signing token is the certificate used by the STS to sign the assertion, and the unencrypting token is the private key equivalent of the certificate used to encrypt the proof key in the RSTR.
The point here is that in order to de-serialize the token, you're going to need the private key of the certificate used to encrypt the original. I guess this isn't a problem when you're in control of client certification, but I think you can see why this might cause issues.
Welcome!
Hi guys!
I've recently been doing a lot of work around Identity in the .NET framework, especially as it applies in 3.0 and 3.5. I've found that much of this stuff isn't covered in any great detail in the documentation. To get a problem solved I've simply had to 'suck it and see', if you'll pardon the expression. This has been frustrating, to say the least! I finally decided to blog about some of my exploits with SAML, WCF, WS-Trust, etc, in order that I might be able to help out a few people who come across the same issues!
I'll begin posting in the very near future - I'll start with a simple SAML serialization/de-serialization problem I encountered a few months ago. I also want to share my experiences of WS-Trust and how it applies (or doesn't!) to WCF, plus one or two salutory tales of developing an STS (Security Token Service) from scratch.
Cheers
Fluffy
I've recently been doing a lot of work around Identity in the .NET framework, especially as it applies in 3.0 and 3.5. I've found that much of this stuff isn't covered in any great detail in the documentation. To get a problem solved I've simply had to 'suck it and see', if you'll pardon the expression. This has been frustrating, to say the least! I finally decided to blog about some of my exploits with SAML, WCF, WS-Trust, etc, in order that I might be able to help out a few people who come across the same issues!
I'll begin posting in the very near future - I'll start with a simple SAML serialization/de-serialization problem I encountered a few months ago. I also want to share my experiences of WS-Trust and how it applies (or doesn't!) to WCF, plus one or two salutory tales of developing an STS (Security Token Service) from scratch.
Cheers
Fluffy
Subscribe to:
Posts (Atom)
