//----------------------------------------------------------------------- // // 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); } }