//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.ChannelElements { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using Validation; /// /// A tamper protection applying binding element that can use any of several given /// binding elements to apply the protection. /// internal class SigningBindingElementChain : ITamperProtectionChannelBindingElement { /// /// The various signing binding elements that may be applicable to a message in preferred use order. /// private readonly ITamperProtectionChannelBindingElement[] signers; /// /// Initializes a new instance of the class. /// /// /// The signing binding elements that may be used for some outgoing message, /// in preferred use order. /// internal SigningBindingElementChain(ITamperProtectionChannelBindingElement[] signers) { Requires.NotNullOrEmpty(signers, "signers"); Requires.NullOrNotNullElements(signers, "signers"); Requires.That(signers.Select(s => s.Protection).Distinct().Count() == 1, "signers", OAuthStrings.SigningElementsMustShareSameProtection); this.signers = signers; } #region ITamperProtectionChannelBindingElement Properties /// /// Gets or sets the delegate that will initialize the non-serialized properties necessary on a signed /// message so that its signature can be correctly calculated for verification. /// May be null for Consumers (who never have to verify signatures). /// public Action SignatureCallback { get { return this.signers[0].SignatureCallback; } set { foreach (ITamperProtectionChannelBindingElement signer in this.signers) { signer.SignatureCallback = value; } } } #endregion #region IChannelBindingElement Members /// /// Gets the protection offered (if any) by this binding element. /// public MessageProtections Protection { get { return this.signers[0].Protection; } } /// /// Gets or sets the channel that this binding element belongs to. /// public Channel Channel { get { return this.signers[0].Channel; } set { foreach (var signer in this.signers) { signer.Channel = value; } } } /// /// Prepares a message for sending based on the rules of this channel binding element. /// /// The message to prepare for sending. /// The cancellation token. /// /// The protections (if any) that this binding element applied to the message. /// Null if this binding element did not even apply to this binding element. /// public async Task ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { foreach (IChannelBindingElement signer in this.signers) { ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null."); MessageProtections? result = await signer.ProcessOutgoingMessageAsync(message, cancellationToken); if (result.HasValue) { return result; } } return null; } /// /// Performs any transformation on an incoming message that may be necessary and/or /// validates an incoming message based on the rules of this channel binding element. /// /// The incoming message to process. /// The cancellation token. /// /// The protections (if any) that this binding element applied to the message. /// Null if this binding element did not even apply to this binding element. /// public async Task ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { foreach (IChannelBindingElement signer in this.signers) { ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null."); MessageProtections? result = await signer.ProcessIncomingMessageAsync(message, cancellationToken); if (result.HasValue) { return result; } } return null; } #endregion #region ITamperProtectionChannelBindingElement Methods /// /// Creates a new object that is a copy of the current instance. /// /// /// A new object that is a copy of this instance. /// ITamperProtectionChannelBindingElement ITamperProtectionChannelBindingElement.Clone() { return new SigningBindingElementChain(this.signers.Select(el => (ITamperProtectionChannelBindingElement)el.Clone()).ToArray()); } #endregion } }