//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
using Validation;
///
/// Implements the interface for all incoming
/// request messages to an OpenID Provider.
///
[Serializable]
internal abstract class Request : IRequest {
///
/// The incoming request message.
///
private readonly IDirectedProtocolMessage request;
///
/// The incoming request message cast to its extensible form.
/// Or null if the message does not support extensions.
///
private readonly IProtocolMessageWithExtensions extensibleMessage;
///
/// The version of the OpenID protocol to use.
///
private readonly Version protocolVersion;
///
/// Backing store for the property.
///
[NonSerialized]
private Protocol protocol;
///
/// The list of extensions to add to the response message.
///
private List responseExtensions = new List();
///
/// Initializes a new instance of the class.
///
/// The incoming request message.
/// The security settings from the channel.
protected Request(IDirectedProtocolMessage request, ProviderSecuritySettings securitySettings) {
Requires.NotNull(request, "request");
Requires.NotNull(securitySettings, "securitySettings");
this.request = request;
this.SecuritySettings = securitySettings;
this.protocolVersion = this.request.Version;
this.extensibleMessage = request as IProtocolMessageWithExtensions;
}
///
/// Initializes a new instance of the class.
///
/// The version.
/// The security settings.
protected Request(Version version, ProviderSecuritySettings securitySettings) {
Requires.NotNull(version, "version");
Requires.NotNull(securitySettings, "securitySettings");
this.protocolVersion = version;
this.SecuritySettings = securitySettings;
}
#region IRequest Properties
///
/// Gets a value indicating whether the response is ready to be sent to the user agent.
///
///
/// This property returns false if there are properties that must be set on this
/// request instance before the response can be sent.
///
public abstract bool IsResponseReady { get; }
///
/// Gets or sets the security settings that apply to this request.
///
/// Defaults to the on the .
public ProviderSecuritySettings SecuritySettings { get; set; }
#endregion
///
/// Gets the original request message.
///
/// This may be null in the case of an unrecognizable message.
protected internal IDirectedProtocolMessage RequestMessage {
get { return this.request; }
}
///
/// Gets the protocol version used in the request.
///
protected Protocol Protocol {
get {
if (this.protocol == null) {
this.protocol = Protocol.Lookup(this.protocolVersion);
}
return this.protocol;
}
}
#region IRequest Methods
///
/// Adds an extension to the response to send to the relying party.
///
/// The extension to add to the response message.
public void AddResponseExtension(IOpenIdMessageExtension extension) {
// Because the derived AuthenticationRequest class can swap out
// one response message for another (auth vs. no-auth), and because
// some response messages support extensions while others don't,
// we just add the extensions to a collection here and add them
// to the response on the way out.
this.responseExtensions.Add(extension);
}
///
/// Removes any response extensions previously added using .
///
///
/// This should be called before sending a negative response back to the relying party
/// if extensions were already added, since negative responses cannot carry extensions.
///
public void ClearResponseExtensions() {
this.responseExtensions.Clear();
}
///
/// Gets an extension sent from the relying party.
///
/// The type of the extension.
///
/// An instance of the extension initialized with values passed in with the request.
///
public T GetExtension() where T : IOpenIdMessageExtension, new() {
if (this.extensibleMessage != null) {
return this.extensibleMessage.Extensions.OfType().SingleOrDefault();
} else {
return default(T);
}
}
///
/// Gets an extension sent from the relying party.
///
/// The type of the extension.
///
/// An instance of the extension initialized with values passed in with the request.
///
public IOpenIdMessageExtension GetExtension(Type extensionType) {
if (this.extensibleMessage != null) {
return this.extensibleMessage.Extensions.OfType().Where(ext => extensionType.IsInstanceOfType(ext)).SingleOrDefault();
} else {
return null;
}
}
#endregion
///
/// Gets the response to send to the user agent.
///
/// The cancellation token.
/// The response.
/// Thrown if is false.
internal async Task GetResponseAsync(CancellationToken cancellationToken) {
RequiresEx.ValidState(this.IsResponseReady, OpenIdStrings.ResponseNotReady);
if (this.responseExtensions.Count > 0) {
var responseMessage = await this.GetResponseMessageAsync(cancellationToken);
var extensibleResponse = responseMessage as IProtocolMessageWithExtensions;
ErrorUtilities.VerifyOperation(extensibleResponse != null, MessagingStrings.MessageNotExtensible, responseMessage.GetType().Name);
foreach (var extension in this.responseExtensions) {
// It's possible that a prior call to this property
// has already added some/all of the extensions to the message.
// We don't have to worry about deleting old ones because
// this class provides no facility for removing extensions
// that are previously added.
if (!extensibleResponse.Extensions.Contains(extension)) {
extensibleResponse.Extensions.Add(extension);
}
}
}
return await this.GetResponseMessageAsync(cancellationToken);
}
///
/// Gets the response message, once is true.
///
/// The cancellation token.
/// The response message.
protected abstract Task GetResponseMessageAsync(CancellationToken cancellationToken);
}
}