Skip to content

Commit

Permalink
Refactors for #10, LogoutHandler not handling expired sessions, needs…
Browse files Browse the repository at this point in the history
… testing before merging back in.
  • Loading branch information
i8beef committed Nov 11, 2016
1 parent c07b7a4 commit 704d0db
Showing 1 changed file with 137 additions and 71 deletions.
208 changes: 137 additions & 71 deletions src/SAML2/Protocol/Saml20LogoutHandler.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Xml;
using SAML2.Bindings;
using SAML2.Config;
Expand Down Expand Up @@ -77,12 +75,9 @@ protected override void Handle(HttpContext context)

if (idpEndpoint == null)
{
// TODO: Reconsider how to accomplish this.
context.User = null;
FormsAuthentication.SignOut();

Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, string.Empty);
throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, string.Empty));
// Current session is missing authentication.
DoLogout(context);
return;
}

TransferClient(idpEndpoint, context);
Expand Down Expand Up @@ -180,7 +175,7 @@ private void HandleSoap(HttpContext context, Stream inputStream)
InResponseTo = req.Id
};

var endpoint = RetrieveIDPConfiguration(StateService.Get<string>(IdpLoginSessionKey));
var endpoint = RetrieveIDPConfiguration(idp.Id);
var destination = DetermineEndpointConfiguration(BindingType.Redirect, endpoint.Endpoints.LogoutEndpoint, endpoint.Metadata.IDPSLOEndpoints);

builder.RedirectFromLogout(destination, response);
Expand Down Expand Up @@ -233,99 +228,170 @@ private void HandleSoap(HttpContext context, Stream inputStream)
private void HandleRequest(HttpContext context)
{
Logger.DebugFormat(TraceMessages.LogoutRequestReceived);
if (context.Request.RequestType == "GET")
{
// HTTP Redirect binding
HandleRequestGet(context);
}
else if (context.Request.RequestType == "POST")
{
// HTTP Post binding
HandleRequestPost(context);
}
else
{
// Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS
Logger.ErrorFormat(ErrorMessages.UnsupportedRequestType, context.Request.RequestType);
throw new Saml20Exception(string.Format(ErrorMessages.UnsupportedRequestType, context.Request.RequestType));
}
}

/// <summary>
/// Handles the request via GET.
/// </summary>
/// <param name="context">The context.</param>
private void HandleRequestGet(HttpContext context)
{
// HTTP Redirect binding
var parser = new HttpRedirectBindingParser(context.Request.Url);
Logger.DebugFormat(TraceMessages.LogoutRequestRedirectBindingParse, parser.Message, parser.SignatureAlgorithm, parser.Signature);

var message = parser.Message;

var req = Serialization.DeserializeFromXmlString<LogoutRequest>(message);

// Fetch the endpoint configuration
var idp = RetrieveIDPConfiguration(StateService.Get<string>(IdpLoginSessionKey));
var idp = RetrieveIDPConfiguration(req.Issuer.Value);
if (idp == null || idp.Metadata == null)
{
Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id);
throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id));
}

var metadata = idp.Metadata;
if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.Signing)))
{
Logger.Error(ErrorMessages.RequestSignatureInvalid);
throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid);
}

var destination = DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.LogoutEndpoint, idp.Metadata.IDPSLOEndpoints);

// Fetch config object
var config = Saml2Config.GetConfig();

// Build the response object
var response = new Saml20LogoutResponse
{
Issuer = config.ServiceProvider.Id,
Destination = destination.Url,
StatusCode = Saml20Constants.StatusCodes.Success
};

string message;
if (context.Request.RequestType == "GET")
{
// HTTP Redirect binding
var parser = new HttpRedirectBindingParser(context.Request.Url);
Logger.DebugFormat(TraceMessages.LogoutRequestRedirectBindingParse, parser.Message, parser.SignatureAlgorithm, parser.Signature);
Issuer = config.ServiceProvider.Id,
Destination = destination.Url,
StatusCode = Saml20Constants.StatusCodes.Success,
InResponseTo = req.Id
};

var endpoint = config.IdentityProviders.FirstOrDefault(x => x.Id == idp.Id);
if (endpoint == null || endpoint.Metadata == null)
{
Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id);
throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id));
}
Logger.DebugFormat(TraceMessages.LogoutRequestParsed, message);

// Log the user out locally
DoLogout(context, true);

var metadata = endpoint.Metadata;
if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.Signing)))
// Respond using redirect binding
if (destination.Binding == BindingType.Redirect)
{
var builder = new HttpRedirectBindingBuilder
{
Logger.Error(ErrorMessages.RequestSignatureInvalid);
throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid);
}
RelayState = context.Request.Params["RelayState"],
Response = response.GetXml().OuterXml,
SigningKey = Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate().PrivateKey
};

message = parser.Message;
Logger.DebugFormat(TraceMessages.LogoutResponseSent, builder.Response);

context.Response.Redirect(destination.Url + "?" + builder.ToQuery(), true);
return;
}
else if (context.Request.RequestType == "POST")
{
// HTTP Post binding
var parser = new HttpPostBindingParser(context);
Logger.DebugFormat(TraceMessages.LogoutRequestPostBindingParse, parser.Message);

if (!parser.IsSigned)
// Respond using post binding
if (destination.Binding == BindingType.Post)
{
var builder = new HttpPostBindingBuilder(destination)
{
Logger.Error(ErrorMessages.RequestSignatureMissing);
throw new Saml20Exception(ErrorMessages.RequestSignatureMissing);
}
Action = SamlActionType.SAMLResponse
};

var endpoint = config.IdentityProviders.FirstOrDefault(x => x.Id == idp.Id);
if (endpoint == null || endpoint.Metadata == null)
{
Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id);
throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id));
}
var responseDocument = response.GetXml();

var metadata = endpoint.Metadata;
Logger.DebugFormat(TraceMessages.LogoutResponseSent, responseDocument.OuterXml);

// Check signature
if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.Signing)))
{
Logger.Error(ErrorMessages.RequestSignatureInvalid);
throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid);
}
XmlSignatureUtils.SignDocument(responseDocument, response.Id);
builder.Response = responseDocument.OuterXml;
builder.RelayState = context.Request.Params["RelayState"];
builder.GetPage().ProcessRequest(context);
}
}

message = parser.Message;
/// <summary>
/// Handles the request via POST.
/// </summary>
/// <param name="context">The context.</param>
private void HandleRequestPost(HttpContext context)
{
// HTTP Post binding
var parser = new HttpPostBindingParser(context);
Logger.DebugFormat(TraceMessages.LogoutRequestPostBindingParse, parser.Message);

if (!parser.IsSigned)
{
Logger.Error(ErrorMessages.RequestSignatureMissing);
throw new Saml20Exception(ErrorMessages.RequestSignatureMissing);
}
else

var message = parser.Message;

var req = Serialization.DeserializeFromXmlString<LogoutRequest>(message);

// Fetch the endpoint configuration
var idp = RetrieveIDPConfiguration(req.Issuer.Value);
if (idp == null || idp.Metadata == null)
{
// Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS
Logger.ErrorFormat(ErrorMessages.UnsupportedRequestType, context.Request.RequestType);
throw new Saml20Exception(string.Format(ErrorMessages.UnsupportedRequestType, context.Request.RequestType));
Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id);
throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id));
}

var metadata = idp.Metadata;
if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.Signing)))
{
Logger.Error(ErrorMessages.RequestSignatureInvalid);
throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid);
}

var destination = DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.LogoutEndpoint, idp.Metadata.IDPSLOEndpoints);

// Fetch config object
var config = Saml2Config.GetConfig();

// Build the response object
var response = new Saml20LogoutResponse
{
Issuer = config.ServiceProvider.Id,
Destination = destination.Url,
StatusCode = Saml20Constants.StatusCodes.Success,
InResponseTo = req.Id
};

Logger.DebugFormat(TraceMessages.LogoutRequestParsed, message);

// Log the user out locally
DoLogout(context, true);

var req = Serialization.DeserializeFromXmlString<LogoutRequest>(message);
response.InResponseTo = req.Id;

// Respond using redirect binding
if (destination.Binding == BindingType.Redirect)
{
var builder = new HttpRedirectBindingBuilder
{
RelayState = context.Request.Params["RelayState"],
Response = response.GetXml().OuterXml,
SigningKey = Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate().PrivateKey
};
{
RelayState = context.Request.Params["RelayState"],
Response = response.GetXml().OuterXml,
SigningKey = Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate().PrivateKey
};

Logger.DebugFormat(TraceMessages.LogoutResponseSent, builder.Response);

Expand All @@ -337,9 +403,9 @@ private void HandleRequest(HttpContext context)
if (destination.Binding == BindingType.Post)
{
var builder = new HttpPostBindingBuilder(destination)
{
Action = SamlActionType.SAMLResponse
};
{
Action = SamlActionType.SAMLResponse
};

var responseDocument = response.GetXml();

Expand Down

0 comments on commit 704d0db

Please sign in to comment.