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