Skip to content

Commit

Permalink
More convenient interface
Browse files Browse the repository at this point in the history
  • Loading branch information
drwatson1 committed Jun 7, 2017
1 parent 92d22dc commit ba344c1
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 58 deletions.
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
# adfs-metadata
Simple service to load and parse metadata from ADFS 3.0 metadata endpoint
# ADFS Metadata Parser
It is a pack of simple utilities to load and parse ADFS metadata. Parser was tested on ADFS 3.0.

## Installing

```
Install-Package DrWatson.Adfs.Metadata
```

## Usage

```
AdfsMetadataServiceAsync svc = new AdfsMetadataServiceAsync(() =>
{
return new HttpClient().GetStringAsync(
"https://fs.example.com/FederationMetadata/2007-06/FederationMetadata.xml"
);
});
// Exception can be thrown
await svc.Load();
if(svc.Ready)
{
X509Certificate2 signingCert = svc.SigningCertificate();
string identity = svc.Identity;
}
```
10 changes: 5 additions & 5 deletions src/DrWatson.Adfs.Metadata.Test/ParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,33 @@ public class ParserTest
[TestMethod]
public void ParserShouldNotThrowOnValidMetadata()
{
Action act = () => new AdfsMetadataParser(Metadata);
Action act = () => AdfsMetadataParser.Parse(Metadata);

act.ShouldNotThrow();
}

[TestMethod]
public void IdentityShouldHaveValidValue()
{
var p = new AdfsMetadataParser(Metadata);
var p = AdfsMetadataParser.Parse(Metadata);

p.Identity.Should().Be("http://fs.geocyber.ru/adfs/services/trust");
}

[TestMethod]
public void SigningCertificateStringShouldStartsWithValidValue()
{
var p = new AdfsMetadataParser(Metadata);
var p = AdfsMetadataParser.Parse(Metadata);

p.SigningCertificateString.Should().StartWith("MIIC2DCCAcCgAwIBAgIQFDIYq8YLU5BCIiG0yk1");
}

[TestMethod]
public void SigningCertificateShouldNotBeNull()
{
var p = new AdfsMetadataParser(Metadata);
var p = AdfsMetadataParser.Parse(Metadata);

p.SigningCertificate.Should().NotBeNull();
p.GetSigningCertificate().Should().NotBeNull();
}
}
}
11 changes: 11 additions & 0 deletions src/DrWatson.Adfs.Metadata/AdfsMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Security.Cryptography.X509Certificates;

namespace DrWatson.Adfs.Metadata
{
public class AdfsMetadata
{
public string Identity { get; set; }
public string SigningCertificateString { get; set; }
}
}
10 changes: 10 additions & 0 deletions src/DrWatson.Adfs.Metadata/AdfsMetadataExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Security.Cryptography.X509Certificates;

namespace DrWatson.Adfs.Metadata
{
public static class AdfsMetadataExtensions
{
public static X509Certificate2 GetSigningCertificate(this AdfsMetadata obj) => new X509Certificate2(Convert.FromBase64String(obj.SigningCertificateString));
}
}
32 changes: 12 additions & 20 deletions src/DrWatson.Adfs.Metadata/AdfsMetadataParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,33 @@

namespace DrWatson.Adfs.Metadata
{
public class AdfsMetadataParser
public static class AdfsMetadataParser
{
public AdfsMetadataParser(string metadata)
: this(new StringReader(metadata))
public static AdfsMetadata Parse(string metadata)
{
return Parse(new StringReader(metadata));
}

public AdfsMetadataParser(Stream reader)
: this(new StreamReader(reader))
public static AdfsMetadata Parse(Stream reader)
{
return Parse(new StreamReader(reader));
}

public AdfsMetadataParser(TextReader reader)
{
Parse(reader);
}

public string Identity { get; private set; }
public string SigningCertificateString { get; private set; }
public X509Certificate2 SigningCertificate { get => new X509Certificate2(Convert.FromBase64String(SigningCertificateString)); }

#region Implementation

private void Parse(TextReader reader)
public static AdfsMetadata Parse(TextReader reader)
{
var doc = XDocument.Load(reader);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("d", "urn:oasis:names:tc:SAML:2.0:metadata");
namespaceManager.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");

Identity = LoadIdentity(doc, namespaceManager);
SigningCertificateString = LoadSigningCertificateString(doc, namespaceManager);
return new AdfsMetadata
{
Identity = LoadIdentity(doc, namespaceManager),
SigningCertificateString = LoadSigningCertificateString(doc, namespaceManager)
};
}

#region Implementation
private static string LoadSigningCertificateString(XDocument doc, XmlNamespaceManager namespaceManager)
{
return doc.XPathSelectElement("/d:EntityDescriptor/ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate", namespaceManager)?.Value;
Expand All @@ -50,7 +43,6 @@ private static string LoadIdentity(XDocument doc, XmlNamespaceManager namespaceM
{
return doc.XPathSelectElement("/d:EntityDescriptor", namespaceManager)?.Attribute("entityID")?.Value;
}

#endregion Implementation
}
}
10 changes: 2 additions & 8 deletions src/DrWatson.Adfs.Metadata/AdfsMetadataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ namespace DrWatson.Adfs.Metadata
{
public interface AdfsMetadataService
{
string Identity { get; }
string SigningCertificateString { get; }

X509Certificate2 SigningCertificate { get; }

bool Ready { get; }

Task Load();
Task<AdfsMetadata> Get();
void Invalidate();
}
}
44 changes: 22 additions & 22 deletions src/DrWatson.Adfs.Metadata/AdfsMetadataServiceAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,49 @@

namespace DrWatson.Adfs.Metadata
{
public class AdfsMetadataServiceAsync
public class AdfsMetadataLoader
: AdfsMetadataService
{
private Func<Task<string>> Loader;
private Task<string> loadTask;
private AdfsMetadataParser parser;
volatile private Task<AdfsMetadata> loadTask;

public AdfsMetadataServiceAsync(Func<Task<string>> loader)
public AdfsMetadataLoader(Func<Task<string>> loader)
{
Loader = loader ?? throw new ArgumentNullException(nameof(loader));
}

public string Identity
public Task<AdfsMetadata> Get()
{
get
if (loadTask != null)
return loadTask;

lock(this)
{
return Ready ? parser.Identity : throw new MetadataServiceException(MetadataServiceException.ErrorCode.NotReady);
if (loadTask != null)
return loadTask;

InternalInvalidate();

return loadTask;
}
}

public string SigningCertificateString
public void Invalidate()
{
get
lock (this)
{
return Ready ? parser.SigningCertificateString : throw new MetadataServiceException(MetadataServiceException.ErrorCode.NotReady);
InternalInvalidate();
}
}

public X509Certificate2 SigningCertificate
private void InternalInvalidate()
{
get
{
return Ready ? parser.SigningCertificate : throw new MetadataServiceException(MetadataServiceException.ErrorCode.NotReady);
}
loadTask = StartLoading();
}

public bool Ready { get; private set; }

public async Task Load()
private async Task<AdfsMetadata> StartLoading()
{
var metadata = await Loader();
parser = new AdfsMetadataParser(metadata);
Ready = true;
return AdfsMetadataParser.Parse(await Loader());
}
}
}
2 changes: 1 addition & 1 deletion src/DrWatson.Adfs.Metadata/MetadataServiceException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace DrWatson.Adfs.Metadata
{
public class MetadataServiceException
class MetadataServiceException
: Exception
{
public enum ErrorCode
Expand Down

0 comments on commit ba344c1

Please sign in to comment.