summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/Consumer/App_Code/TracePageAppender.cs22
-rw-r--r--samples/Consumer/TracePage.aspx.cs42
-rw-r--r--samples/ConsumerWpf/App.xaml.cs28
-rw-r--r--samples/ConsumerWpf/Properties/AssemblyInfo.cs92
-rw-r--r--samples/ServiceProvider/App_Code/DataApi.cs40
-rw-r--r--samples/ServiceProvider/App_Code/DataClasses.designer.cs1846
-rw-r--r--samples/ServiceProvider/App_Code/IDataApi.cs36
-rw-r--r--samples/ServiceProvider/App_Code/TokenAuthorizationState.cs48
-rw-r--r--samples/ServiceProvider/App_Code/TracePageAppender.cs22
-rw-r--r--samples/ServiceProvider/Members/AuthorizedConsumers.aspx.cs30
-rw-r--r--samples/ServiceProvider/TracePage.aspx.cs42
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs584
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs52
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs194
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs202
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs112
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestMessage.cs146
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs204
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs128
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs124
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs292
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs70
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs120
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs348
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs74
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs248
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs304
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs1666
-rw-r--r--src/DotNetOpenAuth/Messaging/IExtensionMessage.cs26
-rw-r--r--src/DotNetOpenAuth/Messaging/IMessage.cs88
-rw-r--r--src/DotNetOpenAuth/Messaging/IProtocolMessage.cs54
-rw-r--r--src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs50
-rw-r--r--src/DotNetOpenAuth/Messaging/MessageSerializer.cs200
-rw-r--r--src/DotNetOpenAuth/Messaging/ProtocolException.cs544
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs642
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs656
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerBase.cs332
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs544
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs338
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs254
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs306
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs64
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs36
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs478
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs368
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs548
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs366
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs446
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs254
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactory.cs168
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs622
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs646
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs76
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs62
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs138
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs302
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs292
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs100
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs150
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs296
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs154
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/PositiveAssertionResponse.cs148
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs372
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs184
-rw-r--r--src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs60
-rw-r--r--src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs8
-rw-r--r--src/DotNetOpenAuth/Util.cs442
68 files changed, 8967 insertions, 8967 deletions
diff --git a/samples/Consumer/App_Code/TracePageAppender.cs b/samples/Consumer/App_Code/TracePageAppender.cs
index b3b539a..93ec9e3 100644
--- a/samples/Consumer/App_Code/TracePageAppender.cs
+++ b/samples/Consumer/App_Code/TracePageAppender.cs
@@ -1,11 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Web;
-
-public class TracePageAppender : log4net.Appender.AppenderSkeleton {
- protected override void Append(log4net.Core.LoggingEvent loggingEvent) {
- StringWriter sw = new StringWriter(Logging.LogMessages);
- Layout.Format(sw, loggingEvent);
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Web;
+
+public class TracePageAppender : log4net.Appender.AppenderSkeleton {
+ protected override void Append(log4net.Core.LoggingEvent loggingEvent) {
+ StringWriter sw = new StringWriter(Logging.LogMessages);
+ Layout.Format(sw, loggingEvent);
+ }
+}
diff --git a/samples/Consumer/TracePage.aspx.cs b/samples/Consumer/TracePage.aspx.cs
index 47e217b..526ab69 100644
--- a/samples/Consumer/TracePage.aspx.cs
+++ b/samples/Consumer/TracePage.aspx.cs
@@ -1,21 +1,21 @@
-using System;
-using System.Collections.Generic;
-using System.Web;
-using System.Web.UI;
-using System.Web.UI.WebControls;
-
-/// <summary>
-/// A page to display recent log messages.
-/// </summary>
-public partial class TracePage : System.Web.UI.Page {
- protected void Page_Load(object sender, EventArgs e) {
- placeHolder1.Controls.Add(new Label { Text = Logging.LogMessages.ToString() });
- }
-
- protected void clearLogButton_Click(object sender, EventArgs e) {
- Logging.LogMessages.Length = 0;
-
- // clear the page immediately, and allow for F5 without a Postback warning.
- Response.Redirect(Request.Url.AbsoluteUri);
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+
+/// <summary>
+/// A page to display recent log messages.
+/// </summary>
+public partial class TracePage : System.Web.UI.Page {
+ protected void Page_Load(object sender, EventArgs e) {
+ placeHolder1.Controls.Add(new Label { Text = Logging.LogMessages.ToString() });
+ }
+
+ protected void clearLogButton_Click(object sender, EventArgs e) {
+ Logging.LogMessages.Length = 0;
+
+ // clear the page immediately, and allow for F5 without a Postback warning.
+ Response.Redirect(Request.Url.AbsoluteUri);
+ }
+}
diff --git a/samples/ConsumerWpf/App.xaml.cs b/samples/ConsumerWpf/App.xaml.cs
index 075d6f6..337cd37 100644
--- a/samples/ConsumerWpf/App.xaml.cs
+++ b/samples/ConsumerWpf/App.xaml.cs
@@ -1,14 +1,14 @@
-namespace ConsumerWpf {
- using System;
- using System.Collections.Generic;
- using System.Configuration;
- using System.Data;
- using System.Linq;
- using System.Windows;
-
- /// <summary>
- /// Interaction logic for App.xaml
- /// </summary>
- public partial class App : Application {
- }
-}
+namespace ConsumerWpf {
+ using System;
+ using System.Collections.Generic;
+ using System.Configuration;
+ using System.Data;
+ using System.Linq;
+ using System.Windows;
+
+ /// <summary>
+ /// Interaction logic for App.xaml
+ /// </summary>
+ public partial class App : Application {
+ }
+}
diff --git a/samples/ConsumerWpf/Properties/AssemblyInfo.cs b/samples/ConsumerWpf/Properties/AssemblyInfo.cs
index d54cc97..85e6239 100644
--- a/samples/ConsumerWpf/Properties/AssemblyInfo.cs
+++ b/samples/ConsumerWpf/Properties/AssemblyInfo.cs
@@ -1,46 +1,46 @@
-using System.Reflection;
-using System.Resources;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Windows;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("ConsumerWpf")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("ConsumerWpf")]
-[assembly: AssemblyCopyright("Copyright © 2008")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// In order to begin building localizable applications, set
-// <UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
-// inside a <PropertyGroup>. For example, if you are using US english
-// in your source files, set the <UICulture> to en-US. Then uncomment
-// the NeutralResourceLanguage attribute below. Update the "en-US" in
-// the line below to match the UICulture setting in the project file.
-
-////[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
-
-[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ConsumerWpf")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ConsumerWpf")]
+[assembly: AssemblyCopyright("Copyright © 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// In order to begin building localizable applications, set
+// <UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
+// inside a <PropertyGroup>. For example, if you are using US english
+// in your source files, set the <UICulture> to en-US. Then uncomment
+// the NeutralResourceLanguage attribute below. Update the "en-US" in
+// the line below to match the UICulture setting in the project file.
+
+////[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/samples/ServiceProvider/App_Code/DataApi.cs b/samples/ServiceProvider/App_Code/DataApi.cs
index ecd3618..a765159 100644
--- a/samples/ServiceProvider/App_Code/DataApi.cs
+++ b/samples/ServiceProvider/App_Code/DataApi.cs
@@ -1,20 +1,20 @@
-using System.Linq;
-using System.ServiceModel;
-
-public class DataApi : IDataApi {
- private static OAuthToken AccessToken {
- get { return OperationContext.Current.IncomingMessageProperties["OAuthAccessToken"] as OAuthToken; }
- }
-
- public int? GetAge() {
- return AccessToken.User.Age;
- }
-
- public string GetName() {
- return AccessToken.User.FullName;
- }
-
- public string[] GetFavoriteSites() {
- return AccessToken.User.FavoriteSites.Select(site => site.SiteUrl).ToArray();
- }
-}
+using System.Linq;
+using System.ServiceModel;
+
+public class DataApi : IDataApi {
+ private static OAuthToken AccessToken {
+ get { return OperationContext.Current.IncomingMessageProperties["OAuthAccessToken"] as OAuthToken; }
+ }
+
+ public int? GetAge() {
+ return AccessToken.User.Age;
+ }
+
+ public string GetName() {
+ return AccessToken.User.FullName;
+ }
+
+ public string[] GetFavoriteSites() {
+ return AccessToken.User.FavoriteSites.Select(site => site.SiteUrl).ToArray();
+ }
+}
diff --git a/samples/ServiceProvider/App_Code/DataClasses.designer.cs b/samples/ServiceProvider/App_Code/DataClasses.designer.cs
index 612cd75..2fc532e 100644
--- a/samples/ServiceProvider/App_Code/DataClasses.designer.cs
+++ b/samples/ServiceProvider/App_Code/DataClasses.designer.cs
@@ -1,923 +1,923 @@
-#pragma warning disable 1591
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:2.0.50727.3053
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Data.Linq;
-using System.Data.Linq.Mapping;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-
-
-
-[System.Data.Linq.Mapping.DatabaseAttribute(Name="Database")]
-public partial class DataClassesDataContext : System.Data.Linq.DataContext
-{
-
- private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
-
- #region Extensibility Method Definitions
- partial void OnCreated();
- partial void InsertUser(User instance);
- partial void UpdateUser(User instance);
- partial void DeleteUser(User instance);
- partial void InsertFavoriteSite(FavoriteSite instance);
- partial void UpdateFavoriteSite(FavoriteSite instance);
- partial void DeleteFavoriteSite(FavoriteSite instance);
- partial void InsertOAuthConsumer(OAuthConsumer instance);
- partial void UpdateOAuthConsumer(OAuthConsumer instance);
- partial void DeleteOAuthConsumer(OAuthConsumer instance);
- partial void InsertOAuthToken(OAuthToken instance);
- partial void UpdateOAuthToken(OAuthToken instance);
- partial void DeleteOAuthToken(OAuthToken instance);
- #endregion
-
- public DataClassesDataContext() :
- base(global::System.Configuration.ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString, mappingSource)
- {
- OnCreated();
- }
-
- public DataClassesDataContext(string connection) :
- base(connection, mappingSource)
- {
- OnCreated();
- }
-
- public DataClassesDataContext(System.Data.IDbConnection connection) :
- base(connection, mappingSource)
- {
- OnCreated();
- }
-
- public DataClassesDataContext(string connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
- base(connection, mappingSource)
- {
- OnCreated();
- }
-
- public DataClassesDataContext(System.Data.IDbConnection connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
- base(connection, mappingSource)
- {
- OnCreated();
- }
-
- public System.Data.Linq.Table<User> Users
- {
- get
- {
- return this.GetTable<User>();
- }
- }
-
- public System.Data.Linq.Table<FavoriteSite> FavoriteSites
- {
- get
- {
- return this.GetTable<FavoriteSite>();
- }
- }
-
- public System.Data.Linq.Table<OAuthConsumer> OAuthConsumers
- {
- get
- {
- return this.GetTable<OAuthConsumer>();
- }
- }
-
- public System.Data.Linq.Table<OAuthToken> OAuthTokens
- {
- get
- {
- return this.GetTable<OAuthToken>();
- }
- }
-}
-
-[Table(Name="dbo.[User]")]
-public partial class User : INotifyPropertyChanging, INotifyPropertyChanged
-{
-
- private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
-
- private int _UserId;
-
- private string _OpenIDClaimedIdentifier;
-
- private string _OpenIDFriendlyIdentifier;
-
- private string _FullName;
-
- private System.Nullable<int> _Age;
-
- private EntitySet<FavoriteSite> _FavoriteSites;
-
- private EntitySet<OAuthToken> _OAuthTokens;
-
- #region Extensibility Method Definitions
- partial void OnLoaded();
- partial void OnValidate(System.Data.Linq.ChangeAction action);
- partial void OnCreated();
- partial void OnUserIdChanging(int value);
- partial void OnUserIdChanged();
- partial void OnOpenIDClaimedIdentifierChanging(string value);
- partial void OnOpenIDClaimedIdentifierChanged();
- partial void OnOpenIDFriendlyIdentifierChanging(string value);
- partial void OnOpenIDFriendlyIdentifierChanged();
- partial void OnFullNameChanging(string value);
- partial void OnFullNameChanged();
- partial void OnAgeChanging(System.Nullable<int> value);
- partial void OnAgeChanged();
- #endregion
-
- public User()
- {
- this._FavoriteSites = new EntitySet<FavoriteSite>(new Action<FavoriteSite>(this.attach_FavoriteSites), new Action<FavoriteSite>(this.detach_FavoriteSites));
- this._OAuthTokens = new EntitySet<OAuthToken>(new Action<OAuthToken>(this.attach_OAuthTokens), new Action<OAuthToken>(this.detach_OAuthTokens));
- OnCreated();
- }
-
- [Column(Storage="_UserId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
- public int UserId
- {
- get
- {
- return this._UserId;
- }
- set
- {
- if ((this._UserId != value))
- {
- this.OnUserIdChanging(value);
- this.SendPropertyChanging();
- this._UserId = value;
- this.SendPropertyChanged("UserId");
- this.OnUserIdChanged();
- }
- }
- }
-
- [Column(Storage="_OpenIDClaimedIdentifier", DbType="NVarChar(150) NOT NULL", CanBeNull=false)]
- public string OpenIDClaimedIdentifier
- {
- get
- {
- return this._OpenIDClaimedIdentifier;
- }
- set
- {
- if ((this._OpenIDClaimedIdentifier != value))
- {
- this.OnOpenIDClaimedIdentifierChanging(value);
- this.SendPropertyChanging();
- this._OpenIDClaimedIdentifier = value;
- this.SendPropertyChanged("OpenIDClaimedIdentifier");
- this.OnOpenIDClaimedIdentifierChanged();
- }
- }
- }
-
- [Column(Storage="_OpenIDFriendlyIdentifier", DbType="NVarChar(150)")]
- public string OpenIDFriendlyIdentifier
- {
- get
- {
- return this._OpenIDFriendlyIdentifier;
- }
- set
- {
- if ((this._OpenIDFriendlyIdentifier != value))
- {
- this.OnOpenIDFriendlyIdentifierChanging(value);
- this.SendPropertyChanging();
- this._OpenIDFriendlyIdentifier = value;
- this.SendPropertyChanged("OpenIDFriendlyIdentifier");
- this.OnOpenIDFriendlyIdentifierChanged();
- }
- }
- }
-
- [Column(Storage="_FullName", DbType="NVarChar(150)", CanBeNull=false)]
- public string FullName
- {
- get
- {
- return this._FullName;
- }
- set
- {
- if ((this._FullName != value))
- {
- this.OnFullNameChanging(value);
- this.SendPropertyChanging();
- this._FullName = value;
- this.SendPropertyChanged("FullName");
- this.OnFullNameChanged();
- }
- }
- }
-
- [Column(Storage="_Age", DbType="int")]
- public System.Nullable<int> Age
- {
- get
- {
- return this._Age;
- }
- set
- {
- if ((this._Age != value))
- {
- this.OnAgeChanging(value);
- this.SendPropertyChanging();
- this._Age = value;
- this.SendPropertyChanged("Age");
- this.OnAgeChanged();
- }
- }
- }
-
- [Association(Name="User_FavoriteSite", Storage="_FavoriteSites", ThisKey="UserId", OtherKey="UserId")]
- public EntitySet<FavoriteSite> FavoriteSites
- {
- get
- {
- return this._FavoriteSites;
- }
- set
- {
- this._FavoriteSites.Assign(value);
- }
- }
-
- [Association(Name="User_OAuthToken", Storage="_OAuthTokens", ThisKey="UserId", OtherKey="UserId")]
- public EntitySet<OAuthToken> OAuthTokens
- {
- get
- {
- return this._OAuthTokens;
- }
- set
- {
- this._OAuthTokens.Assign(value);
- }
- }
-
- public event PropertyChangingEventHandler PropertyChanging;
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- protected virtual void SendPropertyChanging()
- {
- if ((this.PropertyChanging != null))
- {
- this.PropertyChanging(this, emptyChangingEventArgs);
- }
- }
-
- protected virtual void SendPropertyChanged(String propertyName)
- {
- if ((this.PropertyChanged != null))
- {
- this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
-
- private void attach_FavoriteSites(FavoriteSite entity)
- {
- this.SendPropertyChanging();
- entity.User = this;
- }
-
- private void detach_FavoriteSites(FavoriteSite entity)
- {
- this.SendPropertyChanging();
- entity.User = null;
- }
-
- private void attach_OAuthTokens(OAuthToken entity)
- {
- this.SendPropertyChanging();
- entity.User = this;
- }
-
- private void detach_OAuthTokens(OAuthToken entity)
- {
- this.SendPropertyChanging();
- entity.User = null;
- }
-}
-
-[Table(Name="dbo.FavoriteSite")]
-public partial class FavoriteSite : INotifyPropertyChanging, INotifyPropertyChanged
-{
-
- private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
-
- private int _FavoriteSiteId;
-
- private int _UserId;
-
- private string _SiteUrl;
-
- private EntityRef<User> _User;
-
- #region Extensibility Method Definitions
- partial void OnLoaded();
- partial void OnValidate(System.Data.Linq.ChangeAction action);
- partial void OnCreated();
- partial void OnFavoriteSiteIdChanging(int value);
- partial void OnFavoriteSiteIdChanged();
- partial void OnUserIdChanging(int value);
- partial void OnUserIdChanged();
- partial void OnSiteUrlChanging(string value);
- partial void OnSiteUrlChanged();
- #endregion
-
- public FavoriteSite()
- {
- this._User = default(EntityRef<User>);
- OnCreated();
- }
-
- [Column(Storage="_FavoriteSiteId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
- public int FavoriteSiteId
- {
- get
- {
- return this._FavoriteSiteId;
- }
- set
- {
- if ((this._FavoriteSiteId != value))
- {
- this.OnFavoriteSiteIdChanging(value);
- this.SendPropertyChanging();
- this._FavoriteSiteId = value;
- this.SendPropertyChanged("FavoriteSiteId");
- this.OnFavoriteSiteIdChanged();
- }
- }
- }
-
- [Column(Storage="_UserId", DbType="Int NOT NULL")]
- public int UserId
- {
- get
- {
- return this._UserId;
- }
- set
- {
- if ((this._UserId != value))
- {
- if (this._User.HasLoadedOrAssignedValue)
- {
- throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
- }
- this.OnUserIdChanging(value);
- this.SendPropertyChanging();
- this._UserId = value;
- this.SendPropertyChanged("UserId");
- this.OnUserIdChanged();
- }
- }
- }
-
- [Column(Storage="_SiteUrl", DbType="NVarChar(255) NOT NULL", CanBeNull=false)]
- public string SiteUrl
- {
- get
- {
- return this._SiteUrl;
- }
- set
- {
- if ((this._SiteUrl != value))
- {
- this.OnSiteUrlChanging(value);
- this.SendPropertyChanging();
- this._SiteUrl = value;
- this.SendPropertyChanged("SiteUrl");
- this.OnSiteUrlChanged();
- }
- }
- }
-
- [Association(Name="User_FavoriteSite", Storage="_User", ThisKey="UserId", OtherKey="UserId", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
- public User User
- {
- get
- {
- return this._User.Entity;
- }
- set
- {
- User previousValue = this._User.Entity;
- if (((previousValue != value)
- || (this._User.HasLoadedOrAssignedValue == false)))
- {
- this.SendPropertyChanging();
- if ((previousValue != null))
- {
- this._User.Entity = null;
- previousValue.FavoriteSites.Remove(this);
- }
- this._User.Entity = value;
- if ((value != null))
- {
- value.FavoriteSites.Add(this);
- this._UserId = value.UserId;
- }
- else
- {
- this._UserId = default(int);
- }
- this.SendPropertyChanged("User");
- }
- }
- }
-
- public event PropertyChangingEventHandler PropertyChanging;
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- protected virtual void SendPropertyChanging()
- {
- if ((this.PropertyChanging != null))
- {
- this.PropertyChanging(this, emptyChangingEventArgs);
- }
- }
-
- protected virtual void SendPropertyChanged(String propertyName)
- {
- if ((this.PropertyChanged != null))
- {
- this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
-}
-
-[Table(Name="dbo.OAuthConsumer")]
-public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyChanged
-{
-
- private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
-
- private int _ConsumerId;
-
- private string _ConsumerKey;
-
- private string _ConsumerSecret;
-
- private EntitySet<OAuthToken> _OAuthTokens;
-
- #region Extensibility Method Definitions
- partial void OnLoaded();
- partial void OnValidate(System.Data.Linq.ChangeAction action);
- partial void OnCreated();
- partial void OnConsumerIdChanging(int value);
- partial void OnConsumerIdChanged();
- partial void OnConsumerKeyChanging(string value);
- partial void OnConsumerKeyChanged();
- partial void OnConsumerSecretChanging(string value);
- partial void OnConsumerSecretChanged();
- #endregion
-
- public OAuthConsumer()
- {
- this._OAuthTokens = new EntitySet<OAuthToken>(new Action<OAuthToken>(this.attach_OAuthTokens), new Action<OAuthToken>(this.detach_OAuthTokens));
- OnCreated();
- }
-
- [Column(Storage="_ConsumerId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
- public int ConsumerId
- {
- get
- {
- return this._ConsumerId;
- }
- set
- {
- if ((this._ConsumerId != value))
- {
- this.OnConsumerIdChanging(value);
- this.SendPropertyChanging();
- this._ConsumerId = value;
- this.SendPropertyChanged("ConsumerId");
- this.OnConsumerIdChanged();
- }
- }
- }
-
- [Column(Storage="_ConsumerKey", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
- public string ConsumerKey
- {
- get
- {
- return this._ConsumerKey;
- }
- set
- {
- if ((this._ConsumerKey != value))
- {
- this.OnConsumerKeyChanging(value);
- this.SendPropertyChanging();
- this._ConsumerKey = value;
- this.SendPropertyChanged("ConsumerKey");
- this.OnConsumerKeyChanged();
- }
- }
- }
-
- [Column(Storage="_ConsumerSecret", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
- public string ConsumerSecret
- {
- get
- {
- return this._ConsumerSecret;
- }
- set
- {
- if ((this._ConsumerSecret != value))
- {
- this.OnConsumerSecretChanging(value);
- this.SendPropertyChanging();
- this._ConsumerSecret = value;
- this.SendPropertyChanged("ConsumerSecret");
- this.OnConsumerSecretChanged();
- }
- }
- }
-
- [Association(Name="OAuthConsumer_OAuthToken", Storage="_OAuthTokens", ThisKey="ConsumerId", OtherKey="ConsumerId")]
- public EntitySet<OAuthToken> OAuthTokens
- {
- get
- {
- return this._OAuthTokens;
- }
- set
- {
- this._OAuthTokens.Assign(value);
- }
- }
-
- public event PropertyChangingEventHandler PropertyChanging;
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- protected virtual void SendPropertyChanging()
- {
- if ((this.PropertyChanging != null))
- {
- this.PropertyChanging(this, emptyChangingEventArgs);
- }
- }
-
- protected virtual void SendPropertyChanged(String propertyName)
- {
- if ((this.PropertyChanged != null))
- {
- this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
-
- private void attach_OAuthTokens(OAuthToken entity)
- {
- this.SendPropertyChanging();
- entity.OAuthConsumer = this;
- }
-
- private void detach_OAuthTokens(OAuthToken entity)
- {
- this.SendPropertyChanging();
- entity.OAuthConsumer = null;
- }
-}
-
-[Table(Name="dbo.OAuthToken")]
-public partial class OAuthToken : INotifyPropertyChanging, INotifyPropertyChanged
-{
-
- private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
-
- private int _TokenId;
-
- private string _Token;
-
- private string _TokenSecret;
-
- private TokenAuthorizationState _State;
-
- private System.DateTime _IssueDate;
-
- private int _ConsumerId;
-
- private System.Nullable<int> _UserId;
-
- private string _Scope;
-
- private EntityRef<OAuthConsumer> _OAuthConsumer;
-
- private EntityRef<User> _User;
-
- #region Extensibility Method Definitions
- partial void OnLoaded();
- partial void OnValidate(System.Data.Linq.ChangeAction action);
- partial void OnCreated();
- partial void OnTokenIdChanging(int value);
- partial void OnTokenIdChanged();
- partial void OnTokenChanging(string value);
- partial void OnTokenChanged();
- partial void OnTokenSecretChanging(string value);
- partial void OnTokenSecretChanged();
- partial void OnStateChanging(TokenAuthorizationState value);
- partial void OnStateChanged();
- partial void OnIssueDateChanging(System.DateTime value);
- partial void OnIssueDateChanged();
- partial void OnConsumerIdChanging(int value);
- partial void OnConsumerIdChanged();
- partial void OnUserIdChanging(System.Nullable<int> value);
- partial void OnUserIdChanged();
- partial void OnScopeChanging(string value);
- partial void OnScopeChanged();
- #endregion
-
- public OAuthToken()
- {
- this._OAuthConsumer = default(EntityRef<OAuthConsumer>);
- this._User = default(EntityRef<User>);
- OnCreated();
- }
-
- [Column(Storage="_TokenId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
- public int TokenId
- {
- get
- {
- return this._TokenId;
- }
- set
- {
- if ((this._TokenId != value))
- {
- this.OnTokenIdChanging(value);
- this.SendPropertyChanging();
- this._TokenId = value;
- this.SendPropertyChanged("TokenId");
- this.OnTokenIdChanged();
- }
- }
- }
-
- [Column(Storage="_Token", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
- public string Token
- {
- get
- {
- return this._Token;
- }
- set
- {
- if ((this._Token != value))
- {
- this.OnTokenChanging(value);
- this.SendPropertyChanging();
- this._Token = value;
- this.SendPropertyChanged("Token");
- this.OnTokenChanged();
- }
- }
- }
-
- [Column(Storage="_TokenSecret", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
- public string TokenSecret
- {
- get
- {
- return this._TokenSecret;
- }
- set
- {
- if ((this._TokenSecret != value))
- {
- this.OnTokenSecretChanging(value);
- this.SendPropertyChanging();
- this._TokenSecret = value;
- this.SendPropertyChanged("TokenSecret");
- this.OnTokenSecretChanged();
- }
- }
- }
-
- [Column(Storage="_State", DbType="Int NOT NULL", CanBeNull=false)]
- public TokenAuthorizationState State
- {
- get
- {
- return this._State;
- }
- set
- {
- if ((this._State != value))
- {
- this.OnStateChanging(value);
- this.SendPropertyChanging();
- this._State = value;
- this.SendPropertyChanged("State");
- this.OnStateChanged();
- }
- }
- }
-
- [Column(Storage="_IssueDate", DbType="DateTime NOT NULL")]
- public System.DateTime IssueDate
- {
- get
- {
- return this._IssueDate;
- }
- set
- {
- if ((this._IssueDate != value))
- {
- this.OnIssueDateChanging(value);
- this.SendPropertyChanging();
- this._IssueDate = value;
- this.SendPropertyChanged("IssueDate");
- this.OnIssueDateChanged();
- }
- }
- }
-
- [Column(Storage="_ConsumerId", DbType="Int NOT NULL")]
- public int ConsumerId
- {
- get
- {
- return this._ConsumerId;
- }
- set
- {
- if ((this._ConsumerId != value))
- {
- if (this._OAuthConsumer.HasLoadedOrAssignedValue)
- {
- throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
- }
- this.OnConsumerIdChanging(value);
- this.SendPropertyChanging();
- this._ConsumerId = value;
- this.SendPropertyChanged("ConsumerId");
- this.OnConsumerIdChanged();
- }
- }
- }
-
- [Column(Storage="_UserId", DbType="Int")]
- public System.Nullable<int> UserId
- {
- get
- {
- return this._UserId;
- }
- set
- {
- if ((this._UserId != value))
- {
- if (this._User.HasLoadedOrAssignedValue)
- {
- throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
- }
- this.OnUserIdChanging(value);
- this.SendPropertyChanging();
- this._UserId = value;
- this.SendPropertyChanged("UserId");
- this.OnUserIdChanged();
- }
- }
- }
-
- [Column(Storage="_Scope", DbType="nvarchar(MAX)", CanBeNull=false)]
- public string Scope
- {
- get
- {
- return this._Scope;
- }
- set
- {
- if ((this._Scope != value))
- {
- this.OnScopeChanging(value);
- this.SendPropertyChanging();
- this._Scope = value;
- this.SendPropertyChanged("Scope");
- this.OnScopeChanged();
- }
- }
- }
-
- [Association(Name="OAuthConsumer_OAuthToken", Storage="_OAuthConsumer", ThisKey="ConsumerId", OtherKey="ConsumerId", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
- public OAuthConsumer OAuthConsumer
- {
- get
- {
- return this._OAuthConsumer.Entity;
- }
- set
- {
- OAuthConsumer previousValue = this._OAuthConsumer.Entity;
- if (((previousValue != value)
- || (this._OAuthConsumer.HasLoadedOrAssignedValue == false)))
- {
- this.SendPropertyChanging();
- if ((previousValue != null))
- {
- this._OAuthConsumer.Entity = null;
- previousValue.OAuthTokens.Remove(this);
- }
- this._OAuthConsumer.Entity = value;
- if ((value != null))
- {
- value.OAuthTokens.Add(this);
- this._ConsumerId = value.ConsumerId;
- }
- else
- {
- this._ConsumerId = default(int);
- }
- this.SendPropertyChanged("OAuthConsumer");
- }
- }
- }
-
- [Association(Name="User_OAuthToken", Storage="_User", ThisKey="UserId", OtherKey="UserId", IsForeignKey=true, DeleteRule="CASCADE")]
- public User User
- {
- get
- {
- return this._User.Entity;
- }
- set
- {
- User previousValue = this._User.Entity;
- if (((previousValue != value)
- || (this._User.HasLoadedOrAssignedValue == false)))
- {
- this.SendPropertyChanging();
- if ((previousValue != null))
- {
- this._User.Entity = null;
- previousValue.OAuthTokens.Remove(this);
- }
- this._User.Entity = value;
- if ((value != null))
- {
- value.OAuthTokens.Add(this);
- this._UserId = value.UserId;
- }
- else
- {
- this._UserId = default(Nullable<int>);
- }
- this.SendPropertyChanged("User");
- }
- }
- }
-
- public event PropertyChangingEventHandler PropertyChanging;
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- protected virtual void SendPropertyChanging()
- {
- if ((this.PropertyChanging != null))
- {
- this.PropertyChanging(this, emptyChangingEventArgs);
- }
- }
-
- protected virtual void SendPropertyChanged(String propertyName)
- {
- if ((this.PropertyChanged != null))
- {
- this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
-}
-#pragma warning restore 1591
+#pragma warning disable 1591
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3053
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Data.Linq;
+using System.Data.Linq.Mapping;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+
+
+[System.Data.Linq.Mapping.DatabaseAttribute(Name="Database")]
+public partial class DataClassesDataContext : System.Data.Linq.DataContext
+{
+
+ private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
+
+ #region Extensibility Method Definitions
+ partial void OnCreated();
+ partial void InsertUser(User instance);
+ partial void UpdateUser(User instance);
+ partial void DeleteUser(User instance);
+ partial void InsertFavoriteSite(FavoriteSite instance);
+ partial void UpdateFavoriteSite(FavoriteSite instance);
+ partial void DeleteFavoriteSite(FavoriteSite instance);
+ partial void InsertOAuthConsumer(OAuthConsumer instance);
+ partial void UpdateOAuthConsumer(OAuthConsumer instance);
+ partial void DeleteOAuthConsumer(OAuthConsumer instance);
+ partial void InsertOAuthToken(OAuthToken instance);
+ partial void UpdateOAuthToken(OAuthToken instance);
+ partial void DeleteOAuthToken(OAuthToken instance);
+ #endregion
+
+ public DataClassesDataContext() :
+ base(global::System.Configuration.ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString, mappingSource)
+ {
+ OnCreated();
+ }
+
+ public DataClassesDataContext(string connection) :
+ base(connection, mappingSource)
+ {
+ OnCreated();
+ }
+
+ public DataClassesDataContext(System.Data.IDbConnection connection) :
+ base(connection, mappingSource)
+ {
+ OnCreated();
+ }
+
+ public DataClassesDataContext(string connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
+ base(connection, mappingSource)
+ {
+ OnCreated();
+ }
+
+ public DataClassesDataContext(System.Data.IDbConnection connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
+ base(connection, mappingSource)
+ {
+ OnCreated();
+ }
+
+ public System.Data.Linq.Table<User> Users
+ {
+ get
+ {
+ return this.GetTable<User>();
+ }
+ }
+
+ public System.Data.Linq.Table<FavoriteSite> FavoriteSites
+ {
+ get
+ {
+ return this.GetTable<FavoriteSite>();
+ }
+ }
+
+ public System.Data.Linq.Table<OAuthConsumer> OAuthConsumers
+ {
+ get
+ {
+ return this.GetTable<OAuthConsumer>();
+ }
+ }
+
+ public System.Data.Linq.Table<OAuthToken> OAuthTokens
+ {
+ get
+ {
+ return this.GetTable<OAuthToken>();
+ }
+ }
+}
+
+[Table(Name="dbo.[User]")]
+public partial class User : INotifyPropertyChanging, INotifyPropertyChanged
+{
+
+ private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+
+ private int _UserId;
+
+ private string _OpenIDClaimedIdentifier;
+
+ private string _OpenIDFriendlyIdentifier;
+
+ private string _FullName;
+
+ private System.Nullable<int> _Age;
+
+ private EntitySet<FavoriteSite> _FavoriteSites;
+
+ private EntitySet<OAuthToken> _OAuthTokens;
+
+ #region Extensibility Method Definitions
+ partial void OnLoaded();
+ partial void OnValidate(System.Data.Linq.ChangeAction action);
+ partial void OnCreated();
+ partial void OnUserIdChanging(int value);
+ partial void OnUserIdChanged();
+ partial void OnOpenIDClaimedIdentifierChanging(string value);
+ partial void OnOpenIDClaimedIdentifierChanged();
+ partial void OnOpenIDFriendlyIdentifierChanging(string value);
+ partial void OnOpenIDFriendlyIdentifierChanged();
+ partial void OnFullNameChanging(string value);
+ partial void OnFullNameChanged();
+ partial void OnAgeChanging(System.Nullable<int> value);
+ partial void OnAgeChanged();
+ #endregion
+
+ public User()
+ {
+ this._FavoriteSites = new EntitySet<FavoriteSite>(new Action<FavoriteSite>(this.attach_FavoriteSites), new Action<FavoriteSite>(this.detach_FavoriteSites));
+ this._OAuthTokens = new EntitySet<OAuthToken>(new Action<OAuthToken>(this.attach_OAuthTokens), new Action<OAuthToken>(this.detach_OAuthTokens));
+ OnCreated();
+ }
+
+ [Column(Storage="_UserId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
+ public int UserId
+ {
+ get
+ {
+ return this._UserId;
+ }
+ set
+ {
+ if ((this._UserId != value))
+ {
+ this.OnUserIdChanging(value);
+ this.SendPropertyChanging();
+ this._UserId = value;
+ this.SendPropertyChanged("UserId");
+ this.OnUserIdChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_OpenIDClaimedIdentifier", DbType="NVarChar(150) NOT NULL", CanBeNull=false)]
+ public string OpenIDClaimedIdentifier
+ {
+ get
+ {
+ return this._OpenIDClaimedIdentifier;
+ }
+ set
+ {
+ if ((this._OpenIDClaimedIdentifier != value))
+ {
+ this.OnOpenIDClaimedIdentifierChanging(value);
+ this.SendPropertyChanging();
+ this._OpenIDClaimedIdentifier = value;
+ this.SendPropertyChanged("OpenIDClaimedIdentifier");
+ this.OnOpenIDClaimedIdentifierChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_OpenIDFriendlyIdentifier", DbType="NVarChar(150)")]
+ public string OpenIDFriendlyIdentifier
+ {
+ get
+ {
+ return this._OpenIDFriendlyIdentifier;
+ }
+ set
+ {
+ if ((this._OpenIDFriendlyIdentifier != value))
+ {
+ this.OnOpenIDFriendlyIdentifierChanging(value);
+ this.SendPropertyChanging();
+ this._OpenIDFriendlyIdentifier = value;
+ this.SendPropertyChanged("OpenIDFriendlyIdentifier");
+ this.OnOpenIDFriendlyIdentifierChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_FullName", DbType="NVarChar(150)", CanBeNull=false)]
+ public string FullName
+ {
+ get
+ {
+ return this._FullName;
+ }
+ set
+ {
+ if ((this._FullName != value))
+ {
+ this.OnFullNameChanging(value);
+ this.SendPropertyChanging();
+ this._FullName = value;
+ this.SendPropertyChanged("FullName");
+ this.OnFullNameChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_Age", DbType="int")]
+ public System.Nullable<int> Age
+ {
+ get
+ {
+ return this._Age;
+ }
+ set
+ {
+ if ((this._Age != value))
+ {
+ this.OnAgeChanging(value);
+ this.SendPropertyChanging();
+ this._Age = value;
+ this.SendPropertyChanged("Age");
+ this.OnAgeChanged();
+ }
+ }
+ }
+
+ [Association(Name="User_FavoriteSite", Storage="_FavoriteSites", ThisKey="UserId", OtherKey="UserId")]
+ public EntitySet<FavoriteSite> FavoriteSites
+ {
+ get
+ {
+ return this._FavoriteSites;
+ }
+ set
+ {
+ this._FavoriteSites.Assign(value);
+ }
+ }
+
+ [Association(Name="User_OAuthToken", Storage="_OAuthTokens", ThisKey="UserId", OtherKey="UserId")]
+ public EntitySet<OAuthToken> OAuthTokens
+ {
+ get
+ {
+ return this._OAuthTokens;
+ }
+ set
+ {
+ this._OAuthTokens.Assign(value);
+ }
+ }
+
+ public event PropertyChangingEventHandler PropertyChanging;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void SendPropertyChanging()
+ {
+ if ((this.PropertyChanging != null))
+ {
+ this.PropertyChanging(this, emptyChangingEventArgs);
+ }
+ }
+
+ protected virtual void SendPropertyChanged(String propertyName)
+ {
+ if ((this.PropertyChanged != null))
+ {
+ this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+ private void attach_FavoriteSites(FavoriteSite entity)
+ {
+ this.SendPropertyChanging();
+ entity.User = this;
+ }
+
+ private void detach_FavoriteSites(FavoriteSite entity)
+ {
+ this.SendPropertyChanging();
+ entity.User = null;
+ }
+
+ private void attach_OAuthTokens(OAuthToken entity)
+ {
+ this.SendPropertyChanging();
+ entity.User = this;
+ }
+
+ private void detach_OAuthTokens(OAuthToken entity)
+ {
+ this.SendPropertyChanging();
+ entity.User = null;
+ }
+}
+
+[Table(Name="dbo.FavoriteSite")]
+public partial class FavoriteSite : INotifyPropertyChanging, INotifyPropertyChanged
+{
+
+ private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+
+ private int _FavoriteSiteId;
+
+ private int _UserId;
+
+ private string _SiteUrl;
+
+ private EntityRef<User> _User;
+
+ #region Extensibility Method Definitions
+ partial void OnLoaded();
+ partial void OnValidate(System.Data.Linq.ChangeAction action);
+ partial void OnCreated();
+ partial void OnFavoriteSiteIdChanging(int value);
+ partial void OnFavoriteSiteIdChanged();
+ partial void OnUserIdChanging(int value);
+ partial void OnUserIdChanged();
+ partial void OnSiteUrlChanging(string value);
+ partial void OnSiteUrlChanged();
+ #endregion
+
+ public FavoriteSite()
+ {
+ this._User = default(EntityRef<User>);
+ OnCreated();
+ }
+
+ [Column(Storage="_FavoriteSiteId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
+ public int FavoriteSiteId
+ {
+ get
+ {
+ return this._FavoriteSiteId;
+ }
+ set
+ {
+ if ((this._FavoriteSiteId != value))
+ {
+ this.OnFavoriteSiteIdChanging(value);
+ this.SendPropertyChanging();
+ this._FavoriteSiteId = value;
+ this.SendPropertyChanged("FavoriteSiteId");
+ this.OnFavoriteSiteIdChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_UserId", DbType="Int NOT NULL")]
+ public int UserId
+ {
+ get
+ {
+ return this._UserId;
+ }
+ set
+ {
+ if ((this._UserId != value))
+ {
+ if (this._User.HasLoadedOrAssignedValue)
+ {
+ throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+ }
+ this.OnUserIdChanging(value);
+ this.SendPropertyChanging();
+ this._UserId = value;
+ this.SendPropertyChanged("UserId");
+ this.OnUserIdChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_SiteUrl", DbType="NVarChar(255) NOT NULL", CanBeNull=false)]
+ public string SiteUrl
+ {
+ get
+ {
+ return this._SiteUrl;
+ }
+ set
+ {
+ if ((this._SiteUrl != value))
+ {
+ this.OnSiteUrlChanging(value);
+ this.SendPropertyChanging();
+ this._SiteUrl = value;
+ this.SendPropertyChanged("SiteUrl");
+ this.OnSiteUrlChanged();
+ }
+ }
+ }
+
+ [Association(Name="User_FavoriteSite", Storage="_User", ThisKey="UserId", OtherKey="UserId", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
+ public User User
+ {
+ get
+ {
+ return this._User.Entity;
+ }
+ set
+ {
+ User previousValue = this._User.Entity;
+ if (((previousValue != value)
+ || (this._User.HasLoadedOrAssignedValue == false)))
+ {
+ this.SendPropertyChanging();
+ if ((previousValue != null))
+ {
+ this._User.Entity = null;
+ previousValue.FavoriteSites.Remove(this);
+ }
+ this._User.Entity = value;
+ if ((value != null))
+ {
+ value.FavoriteSites.Add(this);
+ this._UserId = value.UserId;
+ }
+ else
+ {
+ this._UserId = default(int);
+ }
+ this.SendPropertyChanged("User");
+ }
+ }
+ }
+
+ public event PropertyChangingEventHandler PropertyChanging;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void SendPropertyChanging()
+ {
+ if ((this.PropertyChanging != null))
+ {
+ this.PropertyChanging(this, emptyChangingEventArgs);
+ }
+ }
+
+ protected virtual void SendPropertyChanged(String propertyName)
+ {
+ if ((this.PropertyChanged != null))
+ {
+ this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
+
+[Table(Name="dbo.OAuthConsumer")]
+public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyChanged
+{
+
+ private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+
+ private int _ConsumerId;
+
+ private string _ConsumerKey;
+
+ private string _ConsumerSecret;
+
+ private EntitySet<OAuthToken> _OAuthTokens;
+
+ #region Extensibility Method Definitions
+ partial void OnLoaded();
+ partial void OnValidate(System.Data.Linq.ChangeAction action);
+ partial void OnCreated();
+ partial void OnConsumerIdChanging(int value);
+ partial void OnConsumerIdChanged();
+ partial void OnConsumerKeyChanging(string value);
+ partial void OnConsumerKeyChanged();
+ partial void OnConsumerSecretChanging(string value);
+ partial void OnConsumerSecretChanged();
+ #endregion
+
+ public OAuthConsumer()
+ {
+ this._OAuthTokens = new EntitySet<OAuthToken>(new Action<OAuthToken>(this.attach_OAuthTokens), new Action<OAuthToken>(this.detach_OAuthTokens));
+ OnCreated();
+ }
+
+ [Column(Storage="_ConsumerId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
+ public int ConsumerId
+ {
+ get
+ {
+ return this._ConsumerId;
+ }
+ set
+ {
+ if ((this._ConsumerId != value))
+ {
+ this.OnConsumerIdChanging(value);
+ this.SendPropertyChanging();
+ this._ConsumerId = value;
+ this.SendPropertyChanged("ConsumerId");
+ this.OnConsumerIdChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_ConsumerKey", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
+ public string ConsumerKey
+ {
+ get
+ {
+ return this._ConsumerKey;
+ }
+ set
+ {
+ if ((this._ConsumerKey != value))
+ {
+ this.OnConsumerKeyChanging(value);
+ this.SendPropertyChanging();
+ this._ConsumerKey = value;
+ this.SendPropertyChanged("ConsumerKey");
+ this.OnConsumerKeyChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_ConsumerSecret", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
+ public string ConsumerSecret
+ {
+ get
+ {
+ return this._ConsumerSecret;
+ }
+ set
+ {
+ if ((this._ConsumerSecret != value))
+ {
+ this.OnConsumerSecretChanging(value);
+ this.SendPropertyChanging();
+ this._ConsumerSecret = value;
+ this.SendPropertyChanged("ConsumerSecret");
+ this.OnConsumerSecretChanged();
+ }
+ }
+ }
+
+ [Association(Name="OAuthConsumer_OAuthToken", Storage="_OAuthTokens", ThisKey="ConsumerId", OtherKey="ConsumerId")]
+ public EntitySet<OAuthToken> OAuthTokens
+ {
+ get
+ {
+ return this._OAuthTokens;
+ }
+ set
+ {
+ this._OAuthTokens.Assign(value);
+ }
+ }
+
+ public event PropertyChangingEventHandler PropertyChanging;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void SendPropertyChanging()
+ {
+ if ((this.PropertyChanging != null))
+ {
+ this.PropertyChanging(this, emptyChangingEventArgs);
+ }
+ }
+
+ protected virtual void SendPropertyChanged(String propertyName)
+ {
+ if ((this.PropertyChanged != null))
+ {
+ this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+ private void attach_OAuthTokens(OAuthToken entity)
+ {
+ this.SendPropertyChanging();
+ entity.OAuthConsumer = this;
+ }
+
+ private void detach_OAuthTokens(OAuthToken entity)
+ {
+ this.SendPropertyChanging();
+ entity.OAuthConsumer = null;
+ }
+}
+
+[Table(Name="dbo.OAuthToken")]
+public partial class OAuthToken : INotifyPropertyChanging, INotifyPropertyChanged
+{
+
+ private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+
+ private int _TokenId;
+
+ private string _Token;
+
+ private string _TokenSecret;
+
+ private TokenAuthorizationState _State;
+
+ private System.DateTime _IssueDate;
+
+ private int _ConsumerId;
+
+ private System.Nullable<int> _UserId;
+
+ private string _Scope;
+
+ private EntityRef<OAuthConsumer> _OAuthConsumer;
+
+ private EntityRef<User> _User;
+
+ #region Extensibility Method Definitions
+ partial void OnLoaded();
+ partial void OnValidate(System.Data.Linq.ChangeAction action);
+ partial void OnCreated();
+ partial void OnTokenIdChanging(int value);
+ partial void OnTokenIdChanged();
+ partial void OnTokenChanging(string value);
+ partial void OnTokenChanged();
+ partial void OnTokenSecretChanging(string value);
+ partial void OnTokenSecretChanged();
+ partial void OnStateChanging(TokenAuthorizationState value);
+ partial void OnStateChanged();
+ partial void OnIssueDateChanging(System.DateTime value);
+ partial void OnIssueDateChanged();
+ partial void OnConsumerIdChanging(int value);
+ partial void OnConsumerIdChanged();
+ partial void OnUserIdChanging(System.Nullable<int> value);
+ partial void OnUserIdChanged();
+ partial void OnScopeChanging(string value);
+ partial void OnScopeChanged();
+ #endregion
+
+ public OAuthToken()
+ {
+ this._OAuthConsumer = default(EntityRef<OAuthConsumer>);
+ this._User = default(EntityRef<User>);
+ OnCreated();
+ }
+
+ [Column(Storage="_TokenId", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
+ public int TokenId
+ {
+ get
+ {
+ return this._TokenId;
+ }
+ set
+ {
+ if ((this._TokenId != value))
+ {
+ this.OnTokenIdChanging(value);
+ this.SendPropertyChanging();
+ this._TokenId = value;
+ this.SendPropertyChanged("TokenId");
+ this.OnTokenIdChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_Token", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
+ public string Token
+ {
+ get
+ {
+ return this._Token;
+ }
+ set
+ {
+ if ((this._Token != value))
+ {
+ this.OnTokenChanging(value);
+ this.SendPropertyChanging();
+ this._Token = value;
+ this.SendPropertyChanged("Token");
+ this.OnTokenChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_TokenSecret", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
+ public string TokenSecret
+ {
+ get
+ {
+ return this._TokenSecret;
+ }
+ set
+ {
+ if ((this._TokenSecret != value))
+ {
+ this.OnTokenSecretChanging(value);
+ this.SendPropertyChanging();
+ this._TokenSecret = value;
+ this.SendPropertyChanged("TokenSecret");
+ this.OnTokenSecretChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_State", DbType="Int NOT NULL", CanBeNull=false)]
+ public TokenAuthorizationState State
+ {
+ get
+ {
+ return this._State;
+ }
+ set
+ {
+ if ((this._State != value))
+ {
+ this.OnStateChanging(value);
+ this.SendPropertyChanging();
+ this._State = value;
+ this.SendPropertyChanged("State");
+ this.OnStateChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_IssueDate", DbType="DateTime NOT NULL")]
+ public System.DateTime IssueDate
+ {
+ get
+ {
+ return this._IssueDate;
+ }
+ set
+ {
+ if ((this._IssueDate != value))
+ {
+ this.OnIssueDateChanging(value);
+ this.SendPropertyChanging();
+ this._IssueDate = value;
+ this.SendPropertyChanged("IssueDate");
+ this.OnIssueDateChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_ConsumerId", DbType="Int NOT NULL")]
+ public int ConsumerId
+ {
+ get
+ {
+ return this._ConsumerId;
+ }
+ set
+ {
+ if ((this._ConsumerId != value))
+ {
+ if (this._OAuthConsumer.HasLoadedOrAssignedValue)
+ {
+ throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+ }
+ this.OnConsumerIdChanging(value);
+ this.SendPropertyChanging();
+ this._ConsumerId = value;
+ this.SendPropertyChanged("ConsumerId");
+ this.OnConsumerIdChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_UserId", DbType="Int")]
+ public System.Nullable<int> UserId
+ {
+ get
+ {
+ return this._UserId;
+ }
+ set
+ {
+ if ((this._UserId != value))
+ {
+ if (this._User.HasLoadedOrAssignedValue)
+ {
+ throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+ }
+ this.OnUserIdChanging(value);
+ this.SendPropertyChanging();
+ this._UserId = value;
+ this.SendPropertyChanged("UserId");
+ this.OnUserIdChanged();
+ }
+ }
+ }
+
+ [Column(Storage="_Scope", DbType="nvarchar(MAX)", CanBeNull=false)]
+ public string Scope
+ {
+ get
+ {
+ return this._Scope;
+ }
+ set
+ {
+ if ((this._Scope != value))
+ {
+ this.OnScopeChanging(value);
+ this.SendPropertyChanging();
+ this._Scope = value;
+ this.SendPropertyChanged("Scope");
+ this.OnScopeChanged();
+ }
+ }
+ }
+
+ [Association(Name="OAuthConsumer_OAuthToken", Storage="_OAuthConsumer", ThisKey="ConsumerId", OtherKey="ConsumerId", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
+ public OAuthConsumer OAuthConsumer
+ {
+ get
+ {
+ return this._OAuthConsumer.Entity;
+ }
+ set
+ {
+ OAuthConsumer previousValue = this._OAuthConsumer.Entity;
+ if (((previousValue != value)
+ || (this._OAuthConsumer.HasLoadedOrAssignedValue == false)))
+ {
+ this.SendPropertyChanging();
+ if ((previousValue != null))
+ {
+ this._OAuthConsumer.Entity = null;
+ previousValue.OAuthTokens.Remove(this);
+ }
+ this._OAuthConsumer.Entity = value;
+ if ((value != null))
+ {
+ value.OAuthTokens.Add(this);
+ this._ConsumerId = value.ConsumerId;
+ }
+ else
+ {
+ this._ConsumerId = default(int);
+ }
+ this.SendPropertyChanged("OAuthConsumer");
+ }
+ }
+ }
+
+ [Association(Name="User_OAuthToken", Storage="_User", ThisKey="UserId", OtherKey="UserId", IsForeignKey=true, DeleteRule="CASCADE")]
+ public User User
+ {
+ get
+ {
+ return this._User.Entity;
+ }
+ set
+ {
+ User previousValue = this._User.Entity;
+ if (((previousValue != value)
+ || (this._User.HasLoadedOrAssignedValue == false)))
+ {
+ this.SendPropertyChanging();
+ if ((previousValue != null))
+ {
+ this._User.Entity = null;
+ previousValue.OAuthTokens.Remove(this);
+ }
+ this._User.Entity = value;
+ if ((value != null))
+ {
+ value.OAuthTokens.Add(this);
+ this._UserId = value.UserId;
+ }
+ else
+ {
+ this._UserId = default(Nullable<int>);
+ }
+ this.SendPropertyChanged("User");
+ }
+ }
+ }
+
+ public event PropertyChangingEventHandler PropertyChanging;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void SendPropertyChanging()
+ {
+ if ((this.PropertyChanging != null))
+ {
+ this.PropertyChanging(this, emptyChangingEventArgs);
+ }
+ }
+
+ protected virtual void SendPropertyChanged(String propertyName)
+ {
+ if ((this.PropertyChanged != null))
+ {
+ this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
+#pragma warning restore 1591
diff --git a/samples/ServiceProvider/App_Code/IDataApi.cs b/samples/ServiceProvider/App_Code/IDataApi.cs
index ce9dafe..350df35 100644
--- a/samples/ServiceProvider/App_Code/IDataApi.cs
+++ b/samples/ServiceProvider/App_Code/IDataApi.cs
@@ -1,18 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.Serialization;
-using System.ServiceModel;
-using System.Text;
-
-[ServiceContract]
-public interface IDataApi {
- [OperationContract]
- int? GetAge();
-
- [OperationContract]
- string GetName();
-
- [OperationContract]
- string[] GetFavoriteSites();
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.ServiceModel;
+using System.Text;
+
+[ServiceContract]
+public interface IDataApi {
+ [OperationContract]
+ int? GetAge();
+
+ [OperationContract]
+ string GetName();
+
+ [OperationContract]
+ string[] GetFavoriteSites();
+}
diff --git a/samples/ServiceProvider/App_Code/TokenAuthorizationState.cs b/samples/ServiceProvider/App_Code/TokenAuthorizationState.cs
index ff93684..8d3c8ac 100644
--- a/samples/ServiceProvider/App_Code/TokenAuthorizationState.cs
+++ b/samples/ServiceProvider/App_Code/TokenAuthorizationState.cs
@@ -1,24 +1,24 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-
-/// <summary>
-/// Various states an OAuth token can be in.
-/// </summary>
-public enum TokenAuthorizationState : int {
- /// <summary>
- /// An unauthorized request token.
- /// </summary>
- UnauthorizedRequestToken = 0,
-
- /// <summary>
- /// An authorized request token.
- /// </summary>
- AuthorizedRequestToken = 1,
-
- /// <summary>
- /// An authorized access token.
- /// </summary>
- AccessToken = 2,
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+/// <summary>
+/// Various states an OAuth token can be in.
+/// </summary>
+public enum TokenAuthorizationState : int {
+ /// <summary>
+ /// An unauthorized request token.
+ /// </summary>
+ UnauthorizedRequestToken = 0,
+
+ /// <summary>
+ /// An authorized request token.
+ /// </summary>
+ AuthorizedRequestToken = 1,
+
+ /// <summary>
+ /// An authorized access token.
+ /// </summary>
+ AccessToken = 2,
+}
diff --git a/samples/ServiceProvider/App_Code/TracePageAppender.cs b/samples/ServiceProvider/App_Code/TracePageAppender.cs
index 9f536cd..7490f3d 100644
--- a/samples/ServiceProvider/App_Code/TracePageAppender.cs
+++ b/samples/ServiceProvider/App_Code/TracePageAppender.cs
@@ -1,11 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Web;
-
-public class TracePageAppender : log4net.Appender.AppenderSkeleton {
- protected override void Append(log4net.Core.LoggingEvent loggingEvent) {
- StringWriter sw = new StringWriter(Global.LogMessages);
- Layout.Format(sw, loggingEvent);
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Web;
+
+public class TracePageAppender : log4net.Appender.AppenderSkeleton {
+ protected override void Append(log4net.Core.LoggingEvent loggingEvent) {
+ StringWriter sw = new StringWriter(Global.LogMessages);
+ Layout.Format(sw, loggingEvent);
+ }
+}
diff --git a/samples/ServiceProvider/Members/AuthorizedConsumers.aspx.cs b/samples/ServiceProvider/Members/AuthorizedConsumers.aspx.cs
index 1fb4e95..e7af629 100644
--- a/samples/ServiceProvider/Members/AuthorizedConsumers.aspx.cs
+++ b/samples/ServiceProvider/Members/AuthorizedConsumers.aspx.cs
@@ -1,15 +1,15 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.Web.UI;
-using System.Web.UI.WebControls;
-
-/// <summary>
-/// Lists the consumers that have active request or access tokens
-/// and provides a mechanism for the user to revoke permissions.
-/// </summary>
-public partial class AuthorizedConsumers : System.Web.UI.Page {
- protected void Page_Load(object sender, EventArgs e) {
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+
+/// <summary>
+/// Lists the consumers that have active request or access tokens
+/// and provides a mechanism for the user to revoke permissions.
+/// </summary>
+public partial class AuthorizedConsumers : System.Web.UI.Page {
+ protected void Page_Load(object sender, EventArgs e) {
+ }
+}
diff --git a/samples/ServiceProvider/TracePage.aspx.cs b/samples/ServiceProvider/TracePage.aspx.cs
index d1b0231..6f259a7 100644
--- a/samples/ServiceProvider/TracePage.aspx.cs
+++ b/samples/ServiceProvider/TracePage.aspx.cs
@@ -1,21 +1,21 @@
-using System;
-using System.Collections.Generic;
-using System.Web;
-using System.Web.UI;
-using System.Web.UI.WebControls;
-
-/// <summary>
-/// A page to display recent log messages.
-/// </summary>
-public partial class TracePage : System.Web.UI.Page {
- protected void Page_Load(object sender, EventArgs e) {
- placeHolder1.Controls.Add(new Label { Text = Global.LogMessages.ToString() });
- }
-
- protected void clearLogButton_Click(object sender, EventArgs e) {
- Global.LogMessages.Length = 0;
-
- // clear the page immediately, and allow for F5 without a Postback warning.
- Response.Redirect(Request.Url.AbsoluteUri);
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+
+/// <summary>
+/// A page to display recent log messages.
+/// </summary>
+public partial class TracePage : System.Web.UI.Page {
+ protected void Page_Load(object sender, EventArgs e) {
+ placeHolder1.Controls.Add(new Label { Text = Global.LogMessages.ToString() });
+ }
+
+ protected void clearLogButton_Click(object sender, EventArgs e) {
+ Global.LogMessages.Length = 0;
+
+ // clear the page immediately, and allow for F5 without a Postback warning.
+ Response.Redirect(Request.Url.AbsoluteUri);
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
index 6bf7130..29154ed 100644
--- a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
@@ -1,292 +1,292 @@
-//-----------------------------------------------------------------------
-// <copyright file="ChannelTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Messaging {
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Web;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.Test.Mocks;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ChannelTests : MessagingTestBase {
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void CtorNull() {
- // This bad channel is deliberately constructed to pass null to
- // its protected base class' constructor.
- new TestBadChannel(true);
- }
-
- [TestMethod]
- public void ReadFromRequestQueryString() {
- this.ParameterizedReceiveTest("GET");
- }
-
- [TestMethod]
- public void ReadFromRequestForm() {
- this.ParameterizedReceiveTest("POST");
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendNull() {
- this.Channel.Send(null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendIndirectedUndirectedMessage() {
- IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendDirectedNoRecipientMessage() {
- IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendInvalidMessageTransport() {
- IProtocolMessage message = new TestDirectedMessage((MessageTransport)100);
- this.Channel.Send(message);
- }
-
- [TestMethod]
- public void SendIndirectMessage301Get() {
- TestDirectedMessage message = new TestDirectedMessage(MessageTransport.Indirect);
- GetStandardTestMessage(FieldFill.CompleteBeforeBindings, message);
- message.Recipient = new Uri("http://provider/path");
- var expected = GetStandardTestFields(FieldFill.CompleteBeforeBindings);
-
- UserAgentResponse response = this.Channel.Send(message);
- Assert.AreEqual(HttpStatusCode.Redirect, response.Status);
- StringAssert.StartsWith(response.Headers[HttpResponseHeader.Location], "http://provider/path");
- foreach (var pair in expected) {
- string key = HttpUtility.UrlEncode(pair.Key);
- string value = HttpUtility.UrlEncode(pair.Value);
- string substring = string.Format("{0}={1}", key, value);
- StringAssert.Contains(response.Headers[HttpResponseHeader.Location], substring);
- }
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessage301GetNullMessage() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.Create301RedirectResponse(null, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendIndirectMessage301GetEmptyRecipient() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- badChannel.Create301RedirectResponse(message, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessage301GetNullFields() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- message.Recipient = new Uri("http://someserver");
- badChannel.Create301RedirectResponse(message, null);
- }
-
- [TestMethod]
- public void SendIndirectMessageFormPost() {
- // We craft a very large message to force fallback to form POST.
- // We'll also stick some HTML reserved characters in the string value
- // to test proper character escaping.
- var message = new TestDirectedMessage(MessageTransport.Indirect) {
- Age = 15,
- Name = "c<b" + new string('a', 10 * 1024),
- Location = new Uri("http://host/path"),
- Recipient = new Uri("http://provider/path"),
- };
- UserAgentResponse response = this.Channel.Send(message);
- Assert.AreEqual(HttpStatusCode.OK, response.Status, "A form redirect should be an HTTP successful response.");
- Assert.IsNull(response.Headers[HttpResponseHeader.Location], "There should not be a redirection header in the response.");
- string body = response.Body;
- StringAssert.Contains(body, "<form ");
- StringAssert.Contains(body, "action=\"http://provider/path\"");
- StringAssert.Contains(body, "method=\"post\"");
- StringAssert.Contains(body, "<input type=\"hidden\" name=\"age\" value=\"15\" />");
- StringAssert.Contains(body, "<input type=\"hidden\" name=\"Location\" value=\"http://host/path\" />");
- StringAssert.Contains(body, "<input type=\"hidden\" name=\"Name\" value=\"" + HttpUtility.HtmlEncode(message.Name) + "\" />");
- StringAssert.Contains(body, ".submit()", "There should be some javascript to automate form submission.");
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessageFormPostNullMessage() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.CreateFormPostResponse(null, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendIndirectMessageFormPostEmptyRecipient() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- badChannel.CreateFormPostResponse(message, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessageFormPostNullFields() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- message.Recipient = new Uri("http://someserver");
- badChannel.CreateFormPostResponse(message, null);
- }
-
- /// <summary>
- /// Tests that a direct message is sent when the appropriate message type is provided.
- /// </summary>
- /// <remarks>
- /// Since this is a mock channel that doesn't actually formulate a direct message response,
- /// we just check that the right method was called.
- /// </remarks>
- [TestMethod, ExpectedException(typeof(NotImplementedException), "SendDirectMessageResponse")]
- public void SendDirectMessageResponse() {
- IProtocolMessage message = new TestDirectedMessage {
- Age = 15,
- Name = "Andrew",
- Location = new Uri("http://host/path"),
- };
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessageNull() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.SendIndirectMessage(null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void ReceiveNull() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.Receive(null, null);
- }
-
- [TestMethod]
- public void ReceiveUnrecognizedMessage() {
- TestBadChannel badChannel = new TestBadChannel(false);
- Assert.IsNull(badChannel.Receive(new Dictionary<string, string>(), null));
- }
-
- [TestMethod]
- public void ReadFromRequestWithContext() {
- var fields = GetStandardTestFields(FieldFill.AllRequired);
- TestMessage expectedMessage = GetStandardTestMessage(FieldFill.AllRequired);
- HttpRequest request = new HttpRequest("somefile", "http://someurl", MessagingUtilities.CreateQueryString(fields));
- HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter()));
- IProtocolMessage message = this.Channel.ReadFromRequest();
- Assert.IsNotNull(message);
- Assert.IsInstanceOfType(message, typeof(TestMessage));
- Assert.AreEqual(expectedMessage.Age, ((TestMessage)message).Age);
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void ReadFromRequestNoContext() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.ReadFromRequest();
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void ReadFromRequestNull() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.ReadFromRequest(null);
- }
-
- [TestMethod]
- public void SendReplayProtectedMessageSetsNonce() {
- TestReplayProtectedMessage message = new TestReplayProtectedMessage(MessageTransport.Indirect);
- message.Recipient = new Uri("http://localtest");
-
- this.Channel = CreateChannel(MessageProtections.ReplayProtection);
- this.Channel.Send(message);
- Assert.IsNotNull(((IReplayProtectedProtocolMessage)message).Nonce);
- }
-
- [TestMethod, ExpectedException(typeof(InvalidSignatureException))]
- public void ReceivedInvalidSignature() {
- this.Channel = CreateChannel(MessageProtections.TamperProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, true);
- }
-
- [TestMethod]
- public void ReceivedReplayProtectedMessageJustOnce() {
- this.Channel = CreateChannel(MessageProtections.ReplayProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
- }
-
- [TestMethod, ExpectedException(typeof(ReplayedMessageException))]
- public void ReceivedReplayProtectedMessageTwice() {
- this.Channel = CreateChannel(MessageProtections.ReplayProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void MessageExpirationWithoutTamperResistance() {
- new TestChannel(
- new TestMessageFactory(),
- new StandardExpirationBindingElement());
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void TooManyBindingElementsProvidingSameProtection() {
- Channel channel = new TestChannel(
- new TestMessageFactory(),
- new MockSigningBindingElement(),
- new MockSigningBindingElement());
- Channel_Accessor accessor = Channel_Accessor.AttachShadow(channel);
- accessor.PrepareMessageForSending(new TestSignedDirectedMessage());
- }
-
- [TestMethod]
- public void BindingElementsOrdering() {
- IChannelBindingElement transformA = new MockTransformationBindingElement("a");
- IChannelBindingElement transformB = new MockTransformationBindingElement("b");
- IChannelBindingElement sign = new MockSigningBindingElement();
- IChannelBindingElement replay = new MockReplayProtectionBindingElement();
- IChannelBindingElement expire = new StandardExpirationBindingElement();
-
- Channel channel = new TestChannel(
- new TestMessageFactory(),
- sign,
- replay,
- expire,
- transformB,
- transformA);
-
- Assert.AreEqual(5, channel.BindingElements.Count);
- Assert.AreSame(transformB, channel.BindingElements[0]);
- Assert.AreSame(transformA, channel.BindingElements[1]);
- Assert.AreSame(replay, channel.BindingElements[2]);
- Assert.AreSame(expire, channel.BindingElements[3]);
- Assert.AreSame(sign, channel.BindingElements[4]);
- }
-
- [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
- public void InsufficientlyProtectedMessageSent() {
- var message = new TestSignedDirectedMessage(MessageTransport.Direct);
- message.Recipient = new Uri("http://localtest");
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
- public void InsufficientlyProtectedMessageReceived() {
- this.Channel = CreateChannel(MessageProtections.None, MessageProtections.TamperProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.Now, false);
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void IncomingMessageMissingRequiredParameters() {
- var fields = GetStandardTestFields(FieldFill.IdentifiableButNotAllRequired);
- this.Channel.ReadFromRequest(CreateHttpRequestInfo("GET", fields));
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ChannelTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Net;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Test.Mocks;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ChannelTests : MessagingTestBase {
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorNull() {
+ // This bad channel is deliberately constructed to pass null to
+ // its protected base class' constructor.
+ new TestBadChannel(true);
+ }
+
+ [TestMethod]
+ public void ReadFromRequestQueryString() {
+ this.ParameterizedReceiveTest("GET");
+ }
+
+ [TestMethod]
+ public void ReadFromRequestForm() {
+ this.ParameterizedReceiveTest("POST");
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendNull() {
+ this.Channel.Send(null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendIndirectedUndirectedMessage() {
+ IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendDirectedNoRecipientMessage() {
+ IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendInvalidMessageTransport() {
+ IProtocolMessage message = new TestDirectedMessage((MessageTransport)100);
+ this.Channel.Send(message);
+ }
+
+ [TestMethod]
+ public void SendIndirectMessage301Get() {
+ TestDirectedMessage message = new TestDirectedMessage(MessageTransport.Indirect);
+ GetStandardTestMessage(FieldFill.CompleteBeforeBindings, message);
+ message.Recipient = new Uri("http://provider/path");
+ var expected = GetStandardTestFields(FieldFill.CompleteBeforeBindings);
+
+ UserAgentResponse response = this.Channel.Send(message);
+ Assert.AreEqual(HttpStatusCode.Redirect, response.Status);
+ StringAssert.StartsWith(response.Headers[HttpResponseHeader.Location], "http://provider/path");
+ foreach (var pair in expected) {
+ string key = HttpUtility.UrlEncode(pair.Key);
+ string value = HttpUtility.UrlEncode(pair.Value);
+ string substring = string.Format("{0}={1}", key, value);
+ StringAssert.Contains(response.Headers[HttpResponseHeader.Location], substring);
+ }
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessage301GetNullMessage() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.Create301RedirectResponse(null, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendIndirectMessage301GetEmptyRecipient() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ badChannel.Create301RedirectResponse(message, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessage301GetNullFields() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ message.Recipient = new Uri("http://someserver");
+ badChannel.Create301RedirectResponse(message, null);
+ }
+
+ [TestMethod]
+ public void SendIndirectMessageFormPost() {
+ // We craft a very large message to force fallback to form POST.
+ // We'll also stick some HTML reserved characters in the string value
+ // to test proper character escaping.
+ var message = new TestDirectedMessage(MessageTransport.Indirect) {
+ Age = 15,
+ Name = "c<b" + new string('a', 10 * 1024),
+ Location = new Uri("http://host/path"),
+ Recipient = new Uri("http://provider/path"),
+ };
+ UserAgentResponse response = this.Channel.Send(message);
+ Assert.AreEqual(HttpStatusCode.OK, response.Status, "A form redirect should be an HTTP successful response.");
+ Assert.IsNull(response.Headers[HttpResponseHeader.Location], "There should not be a redirection header in the response.");
+ string body = response.Body;
+ StringAssert.Contains(body, "<form ");
+ StringAssert.Contains(body, "action=\"http://provider/path\"");
+ StringAssert.Contains(body, "method=\"post\"");
+ StringAssert.Contains(body, "<input type=\"hidden\" name=\"age\" value=\"15\" />");
+ StringAssert.Contains(body, "<input type=\"hidden\" name=\"Location\" value=\"http://host/path\" />");
+ StringAssert.Contains(body, "<input type=\"hidden\" name=\"Name\" value=\"" + HttpUtility.HtmlEncode(message.Name) + "\" />");
+ StringAssert.Contains(body, ".submit()", "There should be some javascript to automate form submission.");
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessageFormPostNullMessage() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.CreateFormPostResponse(null, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendIndirectMessageFormPostEmptyRecipient() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ badChannel.CreateFormPostResponse(message, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessageFormPostNullFields() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ message.Recipient = new Uri("http://someserver");
+ badChannel.CreateFormPostResponse(message, null);
+ }
+
+ /// <summary>
+ /// Tests that a direct message is sent when the appropriate message type is provided.
+ /// </summary>
+ /// <remarks>
+ /// Since this is a mock channel that doesn't actually formulate a direct message response,
+ /// we just check that the right method was called.
+ /// </remarks>
+ [TestMethod, ExpectedException(typeof(NotImplementedException), "SendDirectMessageResponse")]
+ public void SendDirectMessageResponse() {
+ IProtocolMessage message = new TestDirectedMessage {
+ Age = 15,
+ Name = "Andrew",
+ Location = new Uri("http://host/path"),
+ };
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessageNull() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.SendIndirectMessage(null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void ReceiveNull() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.Receive(null, null);
+ }
+
+ [TestMethod]
+ public void ReceiveUnrecognizedMessage() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ Assert.IsNull(badChannel.Receive(new Dictionary<string, string>(), null));
+ }
+
+ [TestMethod]
+ public void ReadFromRequestWithContext() {
+ var fields = GetStandardTestFields(FieldFill.AllRequired);
+ TestMessage expectedMessage = GetStandardTestMessage(FieldFill.AllRequired);
+ HttpRequest request = new HttpRequest("somefile", "http://someurl", MessagingUtilities.CreateQueryString(fields));
+ HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter()));
+ IProtocolMessage message = this.Channel.ReadFromRequest();
+ Assert.IsNotNull(message);
+ Assert.IsInstanceOfType(message, typeof(TestMessage));
+ Assert.AreEqual(expectedMessage.Age, ((TestMessage)message).Age);
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void ReadFromRequestNoContext() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.ReadFromRequest();
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void ReadFromRequestNull() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.ReadFromRequest(null);
+ }
+
+ [TestMethod]
+ public void SendReplayProtectedMessageSetsNonce() {
+ TestReplayProtectedMessage message = new TestReplayProtectedMessage(MessageTransport.Indirect);
+ message.Recipient = new Uri("http://localtest");
+
+ this.Channel = CreateChannel(MessageProtections.ReplayProtection);
+ this.Channel.Send(message);
+ Assert.IsNotNull(((IReplayProtectedProtocolMessage)message).Nonce);
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidSignatureException))]
+ public void ReceivedInvalidSignature() {
+ this.Channel = CreateChannel(MessageProtections.TamperProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, true);
+ }
+
+ [TestMethod]
+ public void ReceivedReplayProtectedMessageJustOnce() {
+ this.Channel = CreateChannel(MessageProtections.ReplayProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
+ }
+
+ [TestMethod, ExpectedException(typeof(ReplayedMessageException))]
+ public void ReceivedReplayProtectedMessageTwice() {
+ this.Channel = CreateChannel(MessageProtections.ReplayProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void MessageExpirationWithoutTamperResistance() {
+ new TestChannel(
+ new TestMessageFactory(),
+ new StandardExpirationBindingElement());
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void TooManyBindingElementsProvidingSameProtection() {
+ Channel channel = new TestChannel(
+ new TestMessageFactory(),
+ new MockSigningBindingElement(),
+ new MockSigningBindingElement());
+ Channel_Accessor accessor = Channel_Accessor.AttachShadow(channel);
+ accessor.PrepareMessageForSending(new TestSignedDirectedMessage());
+ }
+
+ [TestMethod]
+ public void BindingElementsOrdering() {
+ IChannelBindingElement transformA = new MockTransformationBindingElement("a");
+ IChannelBindingElement transformB = new MockTransformationBindingElement("b");
+ IChannelBindingElement sign = new MockSigningBindingElement();
+ IChannelBindingElement replay = new MockReplayProtectionBindingElement();
+ IChannelBindingElement expire = new StandardExpirationBindingElement();
+
+ Channel channel = new TestChannel(
+ new TestMessageFactory(),
+ sign,
+ replay,
+ expire,
+ transformB,
+ transformA);
+
+ Assert.AreEqual(5, channel.BindingElements.Count);
+ Assert.AreSame(transformB, channel.BindingElements[0]);
+ Assert.AreSame(transformA, channel.BindingElements[1]);
+ Assert.AreSame(replay, channel.BindingElements[2]);
+ Assert.AreSame(expire, channel.BindingElements[3]);
+ Assert.AreSame(sign, channel.BindingElements[4]);
+ }
+
+ [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
+ public void InsufficientlyProtectedMessageSent() {
+ var message = new TestSignedDirectedMessage(MessageTransport.Direct);
+ message.Recipient = new Uri("http://localtest");
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
+ public void InsufficientlyProtectedMessageReceived() {
+ this.Channel = CreateChannel(MessageProtections.None, MessageProtections.TamperProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.Now, false);
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void IncomingMessageMissingRequiredParameters() {
+ var fields = GetStandardTestFields(FieldFill.IdentifiableButNotAllRequired);
+ this.Channel.ReadFromRequest(CreateHttpRequestInfo("GET", fields));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs b/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs
index 1cdefdb..c3273e8 100644
--- a/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs
@@ -1,26 +1,26 @@
-//-----------------------------------------------------------------------
-// <copyright file="CollectionAssert.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Messaging {
- using System.Collections;
- using System.Collections.Generic;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- internal class CollectionAssert<T> {
- internal static void AreEquivalent(ICollection<T> expected, ICollection<T> actual) {
- ICollection expectedNonGeneric = new List<T>(expected);
- ICollection actualNonGeneric = new List<T>(actual);
- CollectionAssert.AreEquivalent(expectedNonGeneric, actualNonGeneric);
- }
-
- internal static void AreEquivalentByEquality(ICollection<T> expected, ICollection<T> actual) {
- Assert.AreEqual(expected.Count, actual.Count);
- foreach (T value in expected) {
- Assert.IsTrue(actual.Contains(value));
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="CollectionAssert.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System.Collections;
+ using System.Collections.Generic;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ internal class CollectionAssert<T> {
+ internal static void AreEquivalent(ICollection<T> expected, ICollection<T> actual) {
+ ICollection expectedNonGeneric = new List<T>(expected);
+ ICollection actualNonGeneric = new List<T>(actual);
+ CollectionAssert.AreEquivalent(expectedNonGeneric, actualNonGeneric);
+ }
+
+ internal static void AreEquivalentByEquality(ICollection<T> expected, ICollection<T> actual) {
+ Assert.AreEqual(expected.Count, actual.Count);
+ foreach (T value in expected) {
+ Assert.IsTrue(actual.Contains(value));
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs b/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs
index f245702..21f2928 100644
--- a/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs
@@ -1,97 +1,97 @@
-//-----------------------------------------------------------------------
-// <copyright file="ProtocolExceptionTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Messaging {
- using System;
- using DotNetOpenAuth.Messaging;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ProtocolExceptionTests : TestBase {
- [TestMethod]
- public void CtorDefault() {
- ProtocolException ex = new ProtocolException();
- }
-
- [TestMethod]
- public void CtorWithTextMessage() {
- ProtocolException ex = new ProtocolException("message");
- Assert.AreEqual("message", ex.Message);
- }
-
- [TestMethod]
- public void CtorWithTextMessageAndInnerException() {
- Exception innerException = new Exception();
- ProtocolException ex = new ProtocolException("message", innerException);
- Assert.AreEqual("message", ex.Message);
- Assert.AreSame(innerException, ex.InnerException);
- }
-
- [TestMethod]
- public void CtorWithProtocolMessage() {
- IProtocolMessage request = new Mocks.TestDirectedMessage();
- Uri receiver = new Uri("http://receiver");
- ProtocolException ex = new ProtocolException("some error occurred", request, receiver);
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- Assert.AreEqual("some error occurred", ex.Message);
- Assert.AreSame(receiver, msg.Recipient);
- Assert.AreEqual(request.Version, msg.Version);
- Assert.AreEqual(request.Transport, msg.Transport);
- msg.EnsureValidMessage();
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void CtorWithNullProtocolMessage() {
- new ProtocolException("message", null, new Uri("http://receiver"));
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void CtorWithNullReceiver() {
- new ProtocolException("message", new Mocks.TestDirectedMessage(MessageTransport.Indirect), null);
- }
-
- /// <summary>
- /// Tests that exceptions being sent as direct responses do not need an explicit receiver.
- /// </summary>
- [TestMethod]
- public void CtorUndirectedMessageWithNullReceiver() {
- IProtocolMessage request = new Mocks.TestDirectedMessage(MessageTransport.Direct);
- ProtocolException ex = new ProtocolException("message", request, null);
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- Assert.IsNull(msg.Recipient);
- Assert.AreEqual(request.Version, msg.Version);
- Assert.AreEqual(request.Transport, msg.Transport);
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void ProtocolVersionWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- var temp = msg.Version;
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void TransportWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- var temp = msg.Transport;
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void RecipientWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- var temp = msg.Recipient;
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void EnsureValidMessageWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- msg.EnsureValidMessage();
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ProtocolExceptionTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System;
+ using DotNetOpenAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ProtocolExceptionTests : TestBase {
+ [TestMethod]
+ public void CtorDefault() {
+ ProtocolException ex = new ProtocolException();
+ }
+
+ [TestMethod]
+ public void CtorWithTextMessage() {
+ ProtocolException ex = new ProtocolException("message");
+ Assert.AreEqual("message", ex.Message);
+ }
+
+ [TestMethod]
+ public void CtorWithTextMessageAndInnerException() {
+ Exception innerException = new Exception();
+ ProtocolException ex = new ProtocolException("message", innerException);
+ Assert.AreEqual("message", ex.Message);
+ Assert.AreSame(innerException, ex.InnerException);
+ }
+
+ [TestMethod]
+ public void CtorWithProtocolMessage() {
+ IProtocolMessage request = new Mocks.TestDirectedMessage();
+ Uri receiver = new Uri("http://receiver");
+ ProtocolException ex = new ProtocolException("some error occurred", request, receiver);
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ Assert.AreEqual("some error occurred", ex.Message);
+ Assert.AreSame(receiver, msg.Recipient);
+ Assert.AreEqual(request.Version, msg.Version);
+ Assert.AreEqual(request.Transport, msg.Transport);
+ msg.EnsureValidMessage();
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorWithNullProtocolMessage() {
+ new ProtocolException("message", null, new Uri("http://receiver"));
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorWithNullReceiver() {
+ new ProtocolException("message", new Mocks.TestDirectedMessage(MessageTransport.Indirect), null);
+ }
+
+ /// <summary>
+ /// Tests that exceptions being sent as direct responses do not need an explicit receiver.
+ /// </summary>
+ [TestMethod]
+ public void CtorUndirectedMessageWithNullReceiver() {
+ IProtocolMessage request = new Mocks.TestDirectedMessage(MessageTransport.Direct);
+ ProtocolException ex = new ProtocolException("message", request, null);
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ Assert.IsNull(msg.Recipient);
+ Assert.AreEqual(request.Version, msg.Version);
+ Assert.AreEqual(request.Transport, msg.Transport);
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void ProtocolVersionWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ var temp = msg.Version;
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void TransportWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ var temp = msg.Transport;
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void RecipientWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ var temp = msg.Recipient;
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void EnsureValidMessageWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ msg.EnsureValidMessage();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs b/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs
index 2f9830a..d04e504 100644
--- a/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs
@@ -1,101 +1,101 @@
-//-----------------------------------------------------------------------
-// <copyright file="MockOpenIdExtension.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Mocks {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.Extensions;
- using DotNetOpenAuth.OpenId.Messages;
-
- internal class MockOpenIdExtension : IOpenIdMessageExtension {
- internal const string MockTypeUri = "http://mockextension";
-
- internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
- if (typeUri == MockTypeUri) {
- return new MockOpenIdExtension();
- }
-
- return null;
- };
-
- private IDictionary<string, string> extraData = new Dictionary<string, string>();
-
- internal MockOpenIdExtension() {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MockOpenIdExtension"/> class.
- /// </summary>
- /// <param name="partValue">The value of the 'Part' parameter.</param>
- /// <param name="extraValue">The value of the 'data' parameter.</param>
- internal MockOpenIdExtension(string partValue, string extraValue) {
- this.Part = partValue;
- this.Data = extraValue;
- }
-
- #region IOpenIdMessageExtension Members
-
- public string TypeUri {
- get { return MockTypeUri; }
- }
-
- public IEnumerable<string> AdditionalSupportedTypeUris {
- get { return Enumerable.Empty<string>(); }
- }
-
- #endregion
-
- #region IMessage Properties
-
- public Version Version {
- get { return new Version(1, 0); }
- }
-
- public IDictionary<string, string> ExtraData {
- get { return this.extraData; }
- }
-
- #endregion
-
- [MessagePart]
- internal string Part { get; set; }
-
- internal string Data {
- get {
- string data;
- this.extraData.TryGetValue("data", out data);
- return data;
- }
-
- set {
- this.extraData["data"] = value;
- }
- }
-
- public override bool Equals(object obj) {
- MockOpenIdExtension other = obj as MockOpenIdExtension;
- if (other == null) {
- return false;
- }
-
- return this.Part.EqualsNullSafe(other.Part) &&
- this.Data.EqualsNullSafe(other.Data);
- }
-
- public override int GetHashCode() {
- return 1; // mock doesn't need a good hash code algorithm
- }
-
- #region IMessage methods
-
- public void EnsureValidMessage() {
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MockOpenIdExtension.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ internal class MockOpenIdExtension : IOpenIdMessageExtension {
+ internal const string MockTypeUri = "http://mockextension";
+
+ internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
+ if (typeUri == MockTypeUri) {
+ return new MockOpenIdExtension();
+ }
+
+ return null;
+ };
+
+ private IDictionary<string, string> extraData = new Dictionary<string, string>();
+
+ internal MockOpenIdExtension() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MockOpenIdExtension"/> class.
+ /// </summary>
+ /// <param name="partValue">The value of the 'Part' parameter.</param>
+ /// <param name="extraValue">The value of the 'data' parameter.</param>
+ internal MockOpenIdExtension(string partValue, string extraValue) {
+ this.Part = partValue;
+ this.Data = extraValue;
+ }
+
+ #region IOpenIdMessageExtension Members
+
+ public string TypeUri {
+ get { return MockTypeUri; }
+ }
+
+ public IEnumerable<string> AdditionalSupportedTypeUris {
+ get { return Enumerable.Empty<string>(); }
+ }
+
+ #endregion
+
+ #region IMessage Properties
+
+ public Version Version {
+ get { return new Version(1, 0); }
+ }
+
+ public IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ #endregion
+
+ [MessagePart]
+ internal string Part { get; set; }
+
+ internal string Data {
+ get {
+ string data;
+ this.extraData.TryGetValue("data", out data);
+ return data;
+ }
+
+ set {
+ this.extraData["data"] = value;
+ }
+ }
+
+ public override bool Equals(object obj) {
+ MockOpenIdExtension other = obj as MockOpenIdExtension;
+ if (other == null) {
+ return false;
+ }
+
+ return this.Part.EqualsNullSafe(other.Part) &&
+ this.Data.EqualsNullSafe(other.Data);
+ }
+
+ public override int GetHashCode() {
+ return 1; // mock doesn't need a good hash code algorithm
+ }
+
+ #region IMessage methods
+
+ public void EnsureValidMessage() {
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs
index a706d3e..96eeb8d 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs
@@ -1,56 +1,56 @@
-//-----------------------------------------------------------------------
-// <copyright file="TestBaseMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Mocks {
- using System;
- using System.Collections.Generic;
- using System.Runtime.Serialization;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
-
- internal interface IBaseMessageExplicitMembers {
- string ExplicitProperty { get; set; }
- }
-
- internal class TestBaseMessage : IProtocolMessage, IBaseMessageExplicitMembers {
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- [MessagePart("age", IsRequired = true)]
- public int Age { get; set; }
-
- [MessagePart]
- public string Name { get; set; }
-
- [MessagePart("explicit")]
- string IBaseMessageExplicitMembers.ExplicitProperty { get; set; }
-
- Version IMessage.Version {
- get { return new Version(1, 0); }
- }
-
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- MessageTransport IProtocolMessage.Transport {
- get { return MessageTransport.Indirect; }
- }
-
- IDictionary<string, string> IMessage.ExtraData {
- get { return this.extraData; }
- }
-
- internal string PrivatePropertyAccessor {
- get { return this.PrivateProperty; }
- set { this.PrivateProperty = value; }
- }
-
- [MessagePart("private")]
- private string PrivateProperty { get; set; }
-
- void IMessage.EnsureValidMessage() { }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="TestBaseMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ internal interface IBaseMessageExplicitMembers {
+ string ExplicitProperty { get; set; }
+ }
+
+ internal class TestBaseMessage : IProtocolMessage, IBaseMessageExplicitMembers {
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ [MessagePart("age", IsRequired = true)]
+ public int Age { get; set; }
+
+ [MessagePart]
+ public string Name { get; set; }
+
+ [MessagePart("explicit")]
+ string IBaseMessageExplicitMembers.ExplicitProperty { get; set; }
+
+ Version IMessage.Version {
+ get { return new Version(1, 0); }
+ }
+
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ MessageTransport IProtocolMessage.Transport {
+ get { return MessageTransport.Indirect; }
+ }
+
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.extraData; }
+ }
+
+ internal string PrivatePropertyAccessor {
+ get { return this.PrivateProperty; }
+ set { this.PrivateProperty = value; }
+ }
+
+ [MessagePart("private")]
+ private string PrivateProperty { get; set; }
+
+ void IMessage.EnsureValidMessage() { }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs
index 68df8bb..7da6ff3 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs
@@ -1,73 +1,73 @@
-//-----------------------------------------------------------------------
-// <copyright file="TestMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Mocks {
- using System;
- using System.Collections.Generic;
- using System.Runtime.Serialization;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
-
- internal abstract class TestMessage : IDirectResponseProtocolMessage {
- private MessageTransport transport;
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- protected TestMessage()
- : this(MessageTransport.Direct) {
- }
-
- protected TestMessage(MessageTransport transport) {
- this.transport = transport;
- }
-
- [MessagePart("age", IsRequired = true)]
- public int Age { get; set; }
- [MessagePart("Name")]
- public string Name { get; set; }
- [MessagePart]
- public string EmptyMember { get; set; }
- [MessagePart(null)] // null name tests that Location is still the name.
- public Uri Location { get; set; }
- [MessagePart(IsRequired = true)]
- public DateTime Timestamp { get; set; }
-
- #region IProtocolMessage Properties
-
- Version IMessage.Version {
- get { return new Version(1, 0); }
- }
-
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- MessageTransport IProtocolMessage.Transport {
- get { return this.transport; }
- }
-
- IDictionary<string, string> IMessage.ExtraData {
- get { return this.extraData; }
- }
-
- #endregion
-
- #region IDirectResponseProtocolMessage Members
-
- public IDirectedProtocolMessage OriginatingRequest { get; set; }
-
- #endregion
-
- #region IMessage Methods
-
- void IMessage.EnsureValidMessage() {
- if (this.EmptyMember != null || this.Age < 0) {
- throw new ProtocolException();
- }
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="TestMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ internal abstract class TestMessage : IDirectResponseProtocolMessage {
+ private MessageTransport transport;
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ protected TestMessage()
+ : this(MessageTransport.Direct) {
+ }
+
+ protected TestMessage(MessageTransport transport) {
+ this.transport = transport;
+ }
+
+ [MessagePart("age", IsRequired = true)]
+ public int Age { get; set; }
+ [MessagePart("Name")]
+ public string Name { get; set; }
+ [MessagePart]
+ public string EmptyMember { get; set; }
+ [MessagePart(null)] // null name tests that Location is still the name.
+ public Uri Location { get; set; }
+ [MessagePart(IsRequired = true)]
+ public DateTime Timestamp { get; set; }
+
+ #region IProtocolMessage Properties
+
+ Version IMessage.Version {
+ get { return new Version(1, 0); }
+ }
+
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ MessageTransport IProtocolMessage.Transport {
+ get { return this.transport; }
+ }
+
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.extraData; }
+ }
+
+ #endregion
+
+ #region IDirectResponseProtocolMessage Members
+
+ public IDirectedProtocolMessage OriginatingRequest { get; set; }
+
+ #endregion
+
+ #region IMessage Methods
+
+ void IMessage.EnsureValidMessage() {
+ if (this.EmptyMember != null || this.Age < 0) {
+ throw new ProtocolException();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs
index a548969..60cbe3e 100644
--- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs
@@ -1,102 +1,102 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdChannelTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.ChannelElements;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class OpenIdChannelTests : TestBase {
- private static readonly TimeSpan maximumMessageAge = TimeSpan.FromHours(3); // good for tests, too long for production
- private OpenIdChannel channel;
- private OpenIdChannel_Accessor accessor;
- private Mocks.TestWebRequestHandler webHandler;
-
- [TestInitialize]
- public void Setup() {
- this.webHandler = new Mocks.TestWebRequestHandler();
- this.channel = new OpenIdChannel(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(maximumMessageAge), new PrivateSecretMemoryStore());
- this.accessor = OpenIdChannel_Accessor.AttachShadow(this.channel);
- this.channel.WebRequestHandler = this.webHandler;
- }
-
- [TestMethod]
- public void Ctor() {
- // Verify that the channel stack includes the expected types.
- // While other binding elements may be substituted for these, we'd then have
- // to test them. Since we're not testing them in the OpenID battery of tests,
- // we make sure they are the standard ones so that we trust they are tested
- // elsewhere by the testing library.
- var replayElement = (StandardReplayProtectionBindingElement)this.channel.BindingElements.SingleOrDefault(el => el is StandardReplayProtectionBindingElement);
- Assert.IsTrue(this.channel.BindingElements.Any(el => el is StandardExpirationBindingElement));
- Assert.IsNotNull(replayElement);
-
- // Verify that empty nonces are allowed, since OpenID 2.0 allows this.
- Assert.IsTrue(replayElement.AllowZeroLengthNonce);
- }
-
- /// <summary>
- /// Verifies that the channel sends direct message requests as HTTP POST requests.
- /// </summary>
- [TestMethod]
- public void DirectRequestsUsePost() {
- IDirectedProtocolMessage requestMessage = new Mocks.TestDirectedMessage(MessageTransport.Direct) {
- Recipient = new Uri("http://host"),
- Name = "Andrew",
- };
- HttpWebRequest httpRequest = this.accessor.CreateHttpRequest(requestMessage);
- Assert.AreEqual("POST", httpRequest.Method);
- StringAssert.Contains(this.webHandler.RequestEntityAsString, "Name=Andrew");
- }
-
- /// <summary>
- /// Verifies that direct response messages are encoded using Key Value Form.
- /// </summary>
- /// <remarks>
- /// The validity of the actual KVF encoding is not checked here. We assume that the KVF encoding
- /// class is verified elsewhere. We're only checking that the KVF class is being used by the
- /// <see cref="OpenIdChannel.SendDirectMessageResponse"/> method.
- /// </remarks>
- [TestMethod]
- public void DirectResponsesSentUsingKeyValueForm() {
- IProtocolMessage message = MessagingTestBase.GetStandardTestMessage(MessagingTestBase.FieldFill.AllRequired);
- MessageDictionary messageFields = new MessageDictionary(message);
- byte[] expectedBytes = KeyValueFormEncoding.GetBytes(messageFields);
- string expectedContentType = OpenIdChannel_Accessor.KeyValueFormContentType;
-
- UserAgentResponse directResponse = this.accessor.SendDirectMessageResponse(message);
- Assert.AreEqual(expectedContentType, directResponse.Headers[HttpResponseHeader.ContentType]);
- byte[] actualBytes = new byte[directResponse.ResponseStream.Length];
- directResponse.ResponseStream.Read(actualBytes, 0, actualBytes.Length);
- Assert.IsTrue(MessagingUtilities.AreEquivalent(expectedBytes, actualBytes));
- }
-
- /// <summary>
- /// Verifies that direct message responses are read in using the Key Value Form decoder.
- /// </summary>
- [TestMethod]
- public void DirectResponsesReceivedAsKeyValueForm() {
- var fields = new Dictionary<string, string> {
- { "var1", "value1" },
- { "var2", "value2" },
- };
- var response = new DirectWebResponse {
- ResponseStream = new MemoryStream(KeyValueFormEncoding.GetBytes(fields)),
- };
- Assert.IsTrue(MessagingUtilities.AreEquivalent(fields, this.accessor.ReadFromResponseInternal(response)));
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdChannelTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OpenIdChannelTests : TestBase {
+ private static readonly TimeSpan maximumMessageAge = TimeSpan.FromHours(3); // good for tests, too long for production
+ private OpenIdChannel channel;
+ private OpenIdChannel_Accessor accessor;
+ private Mocks.TestWebRequestHandler webHandler;
+
+ [TestInitialize]
+ public void Setup() {
+ this.webHandler = new Mocks.TestWebRequestHandler();
+ this.channel = new OpenIdChannel(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(maximumMessageAge), new PrivateSecretMemoryStore());
+ this.accessor = OpenIdChannel_Accessor.AttachShadow(this.channel);
+ this.channel.WebRequestHandler = this.webHandler;
+ }
+
+ [TestMethod]
+ public void Ctor() {
+ // Verify that the channel stack includes the expected types.
+ // While other binding elements may be substituted for these, we'd then have
+ // to test them. Since we're not testing them in the OpenID battery of tests,
+ // we make sure they are the standard ones so that we trust they are tested
+ // elsewhere by the testing library.
+ var replayElement = (StandardReplayProtectionBindingElement)this.channel.BindingElements.SingleOrDefault(el => el is StandardReplayProtectionBindingElement);
+ Assert.IsTrue(this.channel.BindingElements.Any(el => el is StandardExpirationBindingElement));
+ Assert.IsNotNull(replayElement);
+
+ // Verify that empty nonces are allowed, since OpenID 2.0 allows this.
+ Assert.IsTrue(replayElement.AllowZeroLengthNonce);
+ }
+
+ /// <summary>
+ /// Verifies that the channel sends direct message requests as HTTP POST requests.
+ /// </summary>
+ [TestMethod]
+ public void DirectRequestsUsePost() {
+ IDirectedProtocolMessage requestMessage = new Mocks.TestDirectedMessage(MessageTransport.Direct) {
+ Recipient = new Uri("http://host"),
+ Name = "Andrew",
+ };
+ HttpWebRequest httpRequest = this.accessor.CreateHttpRequest(requestMessage);
+ Assert.AreEqual("POST", httpRequest.Method);
+ StringAssert.Contains(this.webHandler.RequestEntityAsString, "Name=Andrew");
+ }
+
+ /// <summary>
+ /// Verifies that direct response messages are encoded using Key Value Form.
+ /// </summary>
+ /// <remarks>
+ /// The validity of the actual KVF encoding is not checked here. We assume that the KVF encoding
+ /// class is verified elsewhere. We're only checking that the KVF class is being used by the
+ /// <see cref="OpenIdChannel.SendDirectMessageResponse"/> method.
+ /// </remarks>
+ [TestMethod]
+ public void DirectResponsesSentUsingKeyValueForm() {
+ IProtocolMessage message = MessagingTestBase.GetStandardTestMessage(MessagingTestBase.FieldFill.AllRequired);
+ MessageDictionary messageFields = new MessageDictionary(message);
+ byte[] expectedBytes = KeyValueFormEncoding.GetBytes(messageFields);
+ string expectedContentType = OpenIdChannel_Accessor.KeyValueFormContentType;
+
+ UserAgentResponse directResponse = this.accessor.SendDirectMessageResponse(message);
+ Assert.AreEqual(expectedContentType, directResponse.Headers[HttpResponseHeader.ContentType]);
+ byte[] actualBytes = new byte[directResponse.ResponseStream.Length];
+ directResponse.ResponseStream.Read(actualBytes, 0, actualBytes.Length);
+ Assert.IsTrue(MessagingUtilities.AreEquivalent(expectedBytes, actualBytes));
+ }
+
+ /// <summary>
+ /// Verifies that direct message responses are read in using the Key Value Form decoder.
+ /// </summary>
+ [TestMethod]
+ public void DirectResponsesReceivedAsKeyValueForm() {
+ var fields = new Dictionary<string, string> {
+ { "var1", "value1" },
+ { "var2", "value2" },
+ };
+ var response = new DirectWebResponse {
+ ResponseStream = new MemoryStream(KeyValueFormEncoding.GetBytes(fields)),
+ };
+ Assert.IsTrue(MessagingUtilities.AreEquivalent(fields, this.accessor.ReadFromResponseInternal(response)));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs
index b60631b..7ffaf1b 100644
--- a/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs
@@ -1,64 +1,64 @@
-//-----------------------------------------------------------------------
-// <copyright file="DiffieHellmanTests.cs" company="Jason Alexander, Andrew Arnott">
-// Copyright (c) Jason Alexander, Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId {
- using System;
- using System.IO;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.Messages;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using Org.Mentalis.Security.Cryptography;
-
- [TestClass]
- public class DiffieHellmanTests {
- [TestMethod]
- public void Test() {
- string s1 = Test1();
- string s2 = Test1();
-
- Assert.AreNotEqual(s1, s2, "Secret keys should NOT be the same.");
- }
-
- [TestMethod]
- public void TestPublic() {
- TextReader reader = new StringReader(TestSupport.LoadEmbeddedFile("dhpriv.txt"));
-
- try {
- string line;
- while ((line = reader.ReadLine()) != null) {
- string[] parts = line.Trim().Split(' ');
- byte[] x = Convert.FromBase64String(parts[0]);
- DiffieHellmanManaged dh = new DiffieHellmanManaged(AssociateDiffieHellmanRequest.DefaultMod, AssociateDiffieHellmanRequest.DefaultGen, x);
- byte[] pub = dh.CreateKeyExchange();
- byte[] y = Convert.FromBase64String(parts[1]);
-
- if (y[0] == 0 && y[1] <= 127) {
- y.CopyTo(y, 1);
- }
-
- Assert.AreEqual(
- Convert.ToBase64String(y),
- Convert.ToBase64String(DiffieHellmanUtilities.EnsurePositive(pub)),
- line);
- }
- } finally {
- reader.Close();
- }
- }
-
- private static string Test1() {
- DiffieHellman dh1 = new DiffieHellmanManaged();
- DiffieHellman dh2 = new DiffieHellmanManaged();
-
- string secret1 = Convert.ToBase64String(dh1.DecryptKeyExchange(dh2.CreateKeyExchange()));
- string secret2 = Convert.ToBase64String(dh2.DecryptKeyExchange(dh1.CreateKeyExchange()));
-
- Assert.AreEqual(secret1, secret2, "Secret keys do not match for some reason.");
-
- return secret1;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="DiffieHellmanTests.cs" company="Jason Alexander, Andrew Arnott">
+// Copyright (c) Jason Alexander, Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId {
+ using System;
+ using System.IO;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+ using Org.Mentalis.Security.Cryptography;
+
+ [TestClass]
+ public class DiffieHellmanTests {
+ [TestMethod]
+ public void Test() {
+ string s1 = Test1();
+ string s2 = Test1();
+
+ Assert.AreNotEqual(s1, s2, "Secret keys should NOT be the same.");
+ }
+
+ [TestMethod]
+ public void TestPublic() {
+ TextReader reader = new StringReader(TestSupport.LoadEmbeddedFile("dhpriv.txt"));
+
+ try {
+ string line;
+ while ((line = reader.ReadLine()) != null) {
+ string[] parts = line.Trim().Split(' ');
+ byte[] x = Convert.FromBase64String(parts[0]);
+ DiffieHellmanManaged dh = new DiffieHellmanManaged(AssociateDiffieHellmanRequest.DefaultMod, AssociateDiffieHellmanRequest.DefaultGen, x);
+ byte[] pub = dh.CreateKeyExchange();
+ byte[] y = Convert.FromBase64String(parts[1]);
+
+ if (y[0] == 0 && y[1] <= 127) {
+ y.CopyTo(y, 1);
+ }
+
+ Assert.AreEqual(
+ Convert.ToBase64String(y),
+ Convert.ToBase64String(DiffieHellmanUtilities.EnsurePositive(pub)),
+ line);
+ }
+ } finally {
+ reader.Close();
+ }
+ }
+
+ private static string Test1() {
+ DiffieHellman dh1 = new DiffieHellmanManaged();
+ DiffieHellman dh2 = new DiffieHellmanManaged();
+
+ string secret1 = Convert.ToBase64String(dh1.DecryptKeyExchange(dh2.CreateKeyExchange()));
+ string secret2 = Convert.ToBase64String(dh2.DecryptKeyExchange(dh1.CreateKeyExchange()));
+
+ Assert.AreEqual(secret1, secret2, "Secret keys do not match for some reason.");
+
+ return secret1;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs
index 8da2c7a..feb1450 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs
@@ -1,62 +1,62 @@
-//-----------------------------------------------------------------------
-// <copyright file="ClaimsRequestTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.Extensions {
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
- using DotNetOpenAuth.OpenId.Messages;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ClaimsRequestTests : OpenIdTestBase {
- [TestMethod]
- public void CreateResponse() {
- // some unofficial type URIs...
- this.ParameterizedTypeUriPreservedTest("http://openid.net/sreg/1.0");
- this.ParameterizedTypeUriPreservedTest("http://openid.net/sreg/1.1");
- // and the official one.
- this.ParameterizedTypeUriPreservedTest("http://openid.net/extensions/sreg/1.1");
- }
-
- [TestMethod]
- public void RequiredOptionalLists() {
- ClaimsRequest req = new ClaimsRequest();
- MessageDictionary dictionary = new MessageDictionary(req);
- Assert.AreEqual(string.Empty, dictionary["required"]);
- Assert.AreEqual(string.Empty, dictionary["optional"]);
-
- req.BirthDate = DemandLevel.Request;
- req.Nickname = DemandLevel.Require;
- Assert.AreEqual("dob", dictionary["optional"]);
- Assert.AreEqual("nickname", dictionary["required"]);
-
- req.PostalCode = DemandLevel.Require;
- req.Gender = DemandLevel.Request;
- Assert.AreEqual("dob,gender", dictionary["optional"]);
- Assert.AreEqual("nickname,postcode", dictionary["required"]);
- }
-
- [TestMethod]
- public void EqualityTests() {
- ClaimsRequest req1 = new ClaimsRequest();
- ClaimsRequest req2 = new ClaimsRequest();
- Assert.AreEqual(req1, req2);
-
- req1.BirthDate = DemandLevel.Request;
- Assert.AreNotEqual(req1, req2);
-
- req2.BirthDate = DemandLevel.Request;
- req1.Country = DemandLevel.Request;
- Assert.AreNotEqual(req1, req2);
- }
-
- private void ParameterizedTypeUriPreservedTest(string typeUri) {
- ClaimsRequest request = new ClaimsRequest(typeUri);
- ClaimsResponse response = request.CreateResponse();
- Assert.AreEqual(typeUri, ((IOpenIdMessageExtension)response).TypeUri);
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ClaimsRequestTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.Extensions {
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ClaimsRequestTests : OpenIdTestBase {
+ [TestMethod]
+ public void CreateResponse() {
+ // some unofficial type URIs...
+ this.ParameterizedTypeUriPreservedTest("http://openid.net/sreg/1.0");
+ this.ParameterizedTypeUriPreservedTest("http://openid.net/sreg/1.1");
+ // and the official one.
+ this.ParameterizedTypeUriPreservedTest("http://openid.net/extensions/sreg/1.1");
+ }
+
+ [TestMethod]
+ public void RequiredOptionalLists() {
+ ClaimsRequest req = new ClaimsRequest();
+ MessageDictionary dictionary = new MessageDictionary(req);
+ Assert.AreEqual(string.Empty, dictionary["required"]);
+ Assert.AreEqual(string.Empty, dictionary["optional"]);
+
+ req.BirthDate = DemandLevel.Request;
+ req.Nickname = DemandLevel.Require;
+ Assert.AreEqual("dob", dictionary["optional"]);
+ Assert.AreEqual("nickname", dictionary["required"]);
+
+ req.PostalCode = DemandLevel.Require;
+ req.Gender = DemandLevel.Request;
+ Assert.AreEqual("dob,gender", dictionary["optional"]);
+ Assert.AreEqual("nickname,postcode", dictionary["required"]);
+ }
+
+ [TestMethod]
+ public void EqualityTests() {
+ ClaimsRequest req1 = new ClaimsRequest();
+ ClaimsRequest req2 = new ClaimsRequest();
+ Assert.AreEqual(req1, req2);
+
+ req1.BirthDate = DemandLevel.Request;
+ Assert.AreNotEqual(req1, req2);
+
+ req2.BirthDate = DemandLevel.Request;
+ req1.Country = DemandLevel.Request;
+ Assert.AreNotEqual(req1, req2);
+ }
+
+ private void ParameterizedTypeUriPreservedTest(string typeUri) {
+ ClaimsRequest request = new ClaimsRequest(typeUri);
+ ClaimsResponse response = request.CreateResponse();
+ Assert.AreEqual(typeUri, ((IOpenIdMessageExtension)response).TypeUri);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs
index 94cf55b..6dbfa4f 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs
@@ -1,146 +1,146 @@
-//-----------------------------------------------------------------------
-// <copyright file="ClaimsResponseTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.Extensions {
- using System;
- using System.Globalization;
- using System.IO;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.Xml.Serialization;
- using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ClaimsResponseTests {
- [TestMethod]
- public void EmptyMailAddress() {
- ClaimsResponse response = new ClaimsResponse(Constants.sreg_ns);
- response.Email = string.Empty;
- Assert.IsNull(response.MailAddress);
- }
-
- [TestMethod, Ignore] // serialization no longer supported
- public void BinarySerialization() {
- ClaimsResponse fields = this.GetFilledData();
- MemoryStream ms = new MemoryStream();
- IFormatter formatter = new BinaryFormatter();
- formatter.Serialize(ms, fields);
-
- ms.Position = 0;
- ClaimsResponse fields2 = (ClaimsResponse)formatter.Deserialize(ms);
- Assert.AreEqual(fields, fields2);
- }
-
- [TestMethod, Ignore] // serialization no longer supported
- public void XmlSerialization() {
- ClaimsResponse fields = this.GetFilledData();
- MemoryStream ms = new MemoryStream();
- XmlSerializer xs = new XmlSerializer(typeof(ClaimsResponse));
- xs.Serialize(ms, fields);
-
- ms.Position = 0;
- ClaimsResponse fields2 = (ClaimsResponse)xs.Deserialize(ms);
- Assert.AreEqual(fields, fields2);
- }
-
- [TestMethod]
- public void EqualityTest() {
- ClaimsResponse fields1 = this.GetFilledData();
-
- Assert.AreNotEqual(fields1, null);
- Assert.AreNotEqual(fields1, "string");
-
- ClaimsResponse fields2 = this.GetFilledData();
- Assert.AreNotSame(fields1, fields2, "Test sanity check.");
- Assert.AreEqual(fields1, fields2);
-
- // go through each property and change it slightly and make sure it causes inequality.
- fields2.Email += "q";
- Assert.AreNotEqual(fields1, fields2);
- fields1.Email = fields2.Email;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.BirthDate = DateTime.Now;
- Assert.AreNotEqual(fields1, fields2);
- fields2.BirthDate = fields1.BirthDate;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.Country += "q";
- Assert.AreNotEqual(fields1, fields2);
- fields2.Country = fields1.Country;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.FullName += "q";
- Assert.AreNotEqual(fields1, fields2);
- fields2.FullName = fields1.FullName;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.Gender = Gender.Female;
- Assert.AreNotEqual(fields1, fields2);
- fields2.Gender = fields1.Gender;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.Language = "gb";
- Assert.AreNotEqual(fields1, fields2);
- fields2.Language = fields1.Language;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.Nickname += "q";
- Assert.AreNotEqual(fields1, fields2);
- fields2.Nickname = fields1.Nickname;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.PostalCode += "q";
- Assert.AreNotEqual(fields1, fields2);
- fields2.PostalCode = fields1.PostalCode;
- Assert.AreEqual(fields1, fields2, "Test sanity check.");
- fields2.TimeZone += "q";
- Assert.AreNotEqual(fields1, fields2);
- }
-
- [TestMethod]
- public void Birthdates() {
- var response = new ClaimsResponse();
- // Verify that they both start out as null
- Assert.IsNull(response.BirthDateRaw);
- Assert.IsFalse(response.BirthDate.HasValue);
-
- // Verify that null can be set.
- response.BirthDate = null;
- response.BirthDateRaw = null;
- Assert.IsNull(response.BirthDateRaw);
- Assert.IsFalse(response.BirthDate.HasValue);
-
- // Verify that the strong-typed BirthDate property can be set and that it affects the raw property.
- response.BirthDate = DateTime.Parse("April 4, 1984");
- Assert.AreEqual(4, response.BirthDate.Value.Month);
- Assert.AreEqual("1984-04-04", response.BirthDateRaw);
-
- // Verify that the raw property can be set with a complete birthdate and that it affects the strong-typed property.
- response.BirthDateRaw = "1998-05-08";
- Assert.AreEqual("1998-05-08", response.BirthDateRaw);
- Assert.AreEqual(DateTime.Parse("May 8, 1998", CultureInfo.InvariantCulture), response.BirthDate);
-
- // Verify that an partial raw birthdate works, and sets the strong-typed property to null since it cannot be represented.
- response.BirthDateRaw = "2000-00-00";
- Assert.AreEqual("2000-00-00", response.BirthDateRaw);
- Assert.IsFalse(response.BirthDate.HasValue);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void InvalidRawBirthdate() {
- var response = new ClaimsResponse();
- response.BirthDateRaw = "2008";
- }
-
- private ClaimsResponse GetFilledData() {
- return new ClaimsResponse(Constants.sreg_ns) {
- BirthDate = new DateTime(2005, 2, 3),
- Culture = new System.Globalization.CultureInfo("en-US"),
- Email = "a@b.com",
- FullName = "Jimmy buffet",
- Gender = Gender.Male,
- Nickname = "Jimbo",
- PostalCode = "12345",
- TimeZone = "PST",
- };
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ClaimsResponseTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.Extensions {
+ using System;
+ using System.Globalization;
+ using System.IO;
+ using System.Runtime.Serialization;
+ using System.Runtime.Serialization.Formatters.Binary;
+ using System.Xml.Serialization;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ClaimsResponseTests {
+ [TestMethod]
+ public void EmptyMailAddress() {
+ ClaimsResponse response = new ClaimsResponse(Constants.sreg_ns);
+ response.Email = string.Empty;
+ Assert.IsNull(response.MailAddress);
+ }
+
+ [TestMethod, Ignore] // serialization no longer supported
+ public void BinarySerialization() {
+ ClaimsResponse fields = this.GetFilledData();
+ MemoryStream ms = new MemoryStream();
+ IFormatter formatter = new BinaryFormatter();
+ formatter.Serialize(ms, fields);
+
+ ms.Position = 0;
+ ClaimsResponse fields2 = (ClaimsResponse)formatter.Deserialize(ms);
+ Assert.AreEqual(fields, fields2);
+ }
+
+ [TestMethod, Ignore] // serialization no longer supported
+ public void XmlSerialization() {
+ ClaimsResponse fields = this.GetFilledData();
+ MemoryStream ms = new MemoryStream();
+ XmlSerializer xs = new XmlSerializer(typeof(ClaimsResponse));
+ xs.Serialize(ms, fields);
+
+ ms.Position = 0;
+ ClaimsResponse fields2 = (ClaimsResponse)xs.Deserialize(ms);
+ Assert.AreEqual(fields, fields2);
+ }
+
+ [TestMethod]
+ public void EqualityTest() {
+ ClaimsResponse fields1 = this.GetFilledData();
+
+ Assert.AreNotEqual(fields1, null);
+ Assert.AreNotEqual(fields1, "string");
+
+ ClaimsResponse fields2 = this.GetFilledData();
+ Assert.AreNotSame(fields1, fields2, "Test sanity check.");
+ Assert.AreEqual(fields1, fields2);
+
+ // go through each property and change it slightly and make sure it causes inequality.
+ fields2.Email += "q";
+ Assert.AreNotEqual(fields1, fields2);
+ fields1.Email = fields2.Email;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.BirthDate = DateTime.Now;
+ Assert.AreNotEqual(fields1, fields2);
+ fields2.BirthDate = fields1.BirthDate;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.Country += "q";
+ Assert.AreNotEqual(fields1, fields2);
+ fields2.Country = fields1.Country;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.FullName += "q";
+ Assert.AreNotEqual(fields1, fields2);
+ fields2.FullName = fields1.FullName;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.Gender = Gender.Female;
+ Assert.AreNotEqual(fields1, fields2);
+ fields2.Gender = fields1.Gender;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.Language = "gb";
+ Assert.AreNotEqual(fields1, fields2);
+ fields2.Language = fields1.Language;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.Nickname += "q";
+ Assert.AreNotEqual(fields1, fields2);
+ fields2.Nickname = fields1.Nickname;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.PostalCode += "q";
+ Assert.AreNotEqual(fields1, fields2);
+ fields2.PostalCode = fields1.PostalCode;
+ Assert.AreEqual(fields1, fields2, "Test sanity check.");
+ fields2.TimeZone += "q";
+ Assert.AreNotEqual(fields1, fields2);
+ }
+
+ [TestMethod]
+ public void Birthdates() {
+ var response = new ClaimsResponse();
+ // Verify that they both start out as null
+ Assert.IsNull(response.BirthDateRaw);
+ Assert.IsFalse(response.BirthDate.HasValue);
+
+ // Verify that null can be set.
+ response.BirthDate = null;
+ response.BirthDateRaw = null;
+ Assert.IsNull(response.BirthDateRaw);
+ Assert.IsFalse(response.BirthDate.HasValue);
+
+ // Verify that the strong-typed BirthDate property can be set and that it affects the raw property.
+ response.BirthDate = DateTime.Parse("April 4, 1984");
+ Assert.AreEqual(4, response.BirthDate.Value.Month);
+ Assert.AreEqual("1984-04-04", response.BirthDateRaw);
+
+ // Verify that the raw property can be set with a complete birthdate and that it affects the strong-typed property.
+ response.BirthDateRaw = "1998-05-08";
+ Assert.AreEqual("1998-05-08", response.BirthDateRaw);
+ Assert.AreEqual(DateTime.Parse("May 8, 1998", CultureInfo.InvariantCulture), response.BirthDate);
+
+ // Verify that an partial raw birthdate works, and sets the strong-typed property to null since it cannot be represented.
+ response.BirthDateRaw = "2000-00-00";
+ Assert.AreEqual("2000-00-00", response.BirthDateRaw);
+ Assert.IsFalse(response.BirthDate.HasValue);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void InvalidRawBirthdate() {
+ var response = new ClaimsResponse();
+ response.BirthDateRaw = "2008";
+ }
+
+ private ClaimsResponse GetFilledData() {
+ return new ClaimsResponse(Constants.sreg_ns) {
+ BirthDate = new DateTime(2005, 2, 3),
+ Culture = new System.Globalization.CultureInfo("en-US"),
+ Email = "a@b.com",
+ FullName = "Jimmy buffet",
+ Gender = Gender.Male,
+ Nickname = "Jimbo",
+ PostalCode = "12345",
+ TimeZone = "PST",
+ };
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
index d53810a..3f8ff22 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
@@ -1,35 +1,35 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdRelyingPartyTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.RelyingParty;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class OpenIdRelyingPartyTests : OpenIdTestBase {
- [TestInitialize]
- public override void SetUp() {
- base.SetUp();
- }
-
- [TestMethod, Ignore] // ignored, pending work to make dumb mode a supported scenario.
- public void CtorNullAssociationStore() {
- new OpenIdRelyingParty(null, null, null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SecuritySettingsSetNull() {
- var rp = new OpenIdRelyingParty(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(TimeSpan.FromMinutes(5)), new PrivateSecretMemoryStore());
- rp.SecuritySettings = null;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdRelyingPartyTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OpenIdRelyingPartyTests : OpenIdTestBase {
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+ }
+
+ [TestMethod, Ignore] // ignored, pending work to make dumb mode a supported scenario.
+ public void CtorNullAssociationStore() {
+ new OpenIdRelyingParty(null, null, null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SecuritySettingsSetNull() {
+ var rp = new OpenIdRelyingParty(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(TimeSpan.FromMinutes(5)), new PrivateSecretMemoryStore());
+ rp.SecuritySettings = null;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs
index 7a194b7..048deba 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs
@@ -1,60 +1,60 @@
-//-----------------------------------------------------------------------
-// <copyright file="PositiveAuthenticationResponseTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
- using System;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.Messages;
- using DotNetOpenAuth.OpenId.RelyingParty;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class PositiveAuthenticationResponseTests : OpenIdTestBase {
- private readonly Realm realm = new Realm(TestSupport.GetFullUrl(TestSupport.ConsumerPage).AbsoluteUri);
- private readonly Uri returnTo = TestSupport.GetFullUrl(TestSupport.ConsumerPage);
-
- [TestInitialize]
- public override void SetUp() {
- base.SetUp();
- }
-
- /// <summary>
- /// Verifies good, positive assertions are accepted.
- /// </summary>
- [TestMethod]
- public void Valid() {
- PositiveAssertionResponse assertion = this.GetPositiveAssertion();
- var rp = CreateRelyingParty();
- var authResponse = new PositiveAuthenticationResponse(assertion, rp);
- Assert.AreEqual(AuthenticationStatus.Authenticated, authResponse.Status);
- }
-
- /// <summary>
- /// Verifies that the RP rejects signed solicited assertions by an OP that
- /// makes up a claimed Id that was not part of the original request, and
- /// that the OP has no authority to assert positively regarding.
- /// </summary>
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void SpoofedClaimedIdDetectionSolicited() {
- PositiveAssertionResponse assertion = this.GetPositiveAssertion();
- assertion.ProviderEndpoint = new Uri("http://rogueOP");
- var rp = CreateRelyingParty();
- var authResponse = new PositiveAuthenticationResponse(assertion, rp);
- Assert.AreEqual(AuthenticationStatus.Failed, authResponse.Status);
- }
-
- private PositiveAssertionResponse GetPositiveAssertion() {
- Protocol protocol = Protocol.Default;
- PositiveAssertionResponse assertion = new PositiveAssertionResponse(protocol.Version, this.returnTo);
- assertion.ClaimedIdentifier = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, this.MockResponder, protocol.ProtocolVersion);
- assertion.LocalIdentifier = TestSupport.GetDelegateUrl(TestSupport.Scenarios.AutoApproval);
- assertion.ReturnTo = this.returnTo;
- assertion.ProviderEndpoint = TestSupport.GetFullUrl("/" + TestSupport.ProviderPage, null, false);
- return assertion;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="PositiveAuthenticationResponseTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
+ using System;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class PositiveAuthenticationResponseTests : OpenIdTestBase {
+ private readonly Realm realm = new Realm(TestSupport.GetFullUrl(TestSupport.ConsumerPage).AbsoluteUri);
+ private readonly Uri returnTo = TestSupport.GetFullUrl(TestSupport.ConsumerPage);
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+ }
+
+ /// <summary>
+ /// Verifies good, positive assertions are accepted.
+ /// </summary>
+ [TestMethod]
+ public void Valid() {
+ PositiveAssertionResponse assertion = this.GetPositiveAssertion();
+ var rp = CreateRelyingParty();
+ var authResponse = new PositiveAuthenticationResponse(assertion, rp);
+ Assert.AreEqual(AuthenticationStatus.Authenticated, authResponse.Status);
+ }
+
+ /// <summary>
+ /// Verifies that the RP rejects signed solicited assertions by an OP that
+ /// makes up a claimed Id that was not part of the original request, and
+ /// that the OP has no authority to assert positively regarding.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void SpoofedClaimedIdDetectionSolicited() {
+ PositiveAssertionResponse assertion = this.GetPositiveAssertion();
+ assertion.ProviderEndpoint = new Uri("http://rogueOP");
+ var rp = CreateRelyingParty();
+ var authResponse = new PositiveAuthenticationResponse(assertion, rp);
+ Assert.AreEqual(AuthenticationStatus.Failed, authResponse.Status);
+ }
+
+ private PositiveAssertionResponse GetPositiveAssertion() {
+ Protocol protocol = Protocol.Default;
+ PositiveAssertionResponse assertion = new PositiveAssertionResponse(protocol.Version, this.returnTo);
+ assertion.ClaimedIdentifier = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, this.MockResponder, protocol.ProtocolVersion);
+ assertion.LocalIdentifier = TestSupport.GetDelegateUrl(TestSupport.Scenarios.AutoApproval);
+ assertion.ReturnTo = this.returnTo;
+ assertion.ProviderEndpoint = TestSupport.GetFullUrl("/" + TestSupport.ProviderPage, null, false);
+ return assertion;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
index 897904c..3d41623 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
@@ -1,174 +1,174 @@
-//-----------------------------------------------------------------------
-// <copyright file="ServiceEndpointTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.RelyingParty;
- using DotNetOpenAuth.Test.Messaging;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ServiceEndpointTests : OpenIdTestBase {
- private UriIdentifier claimedId = new UriIdentifier("http://claimedid.justatest.com");
- private XriIdentifier claimedXri = new XriIdentifier("=!9B72.7DD1.50A9.5CCD");
- private XriIdentifier userSuppliedXri = new XriIdentifier("=Arnot");
- private Uri providerEndpoint = new Uri("http://someprovider.com");
- private Identifier localId = "http://localid.someprovider.com";
- private string[] v20TypeUris = { Protocol.V20.ClaimedIdentifierServiceTypeURI };
- private string[] v11TypeUris = { Protocol.V11.ClaimedIdentifierServiceTypeURI };
- private int servicePriority = 10;
- private int uriPriority = 10;
-
- [TestMethod]
- public void Ctor() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreSame(this.claimedId, se.ClaimedIdentifier);
- Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
- Assert.AreSame(this.localId, se.ProviderLocalIdentifier);
- CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.ProviderSupportedServiceTypeUris);
- Assert.AreEqual(this.servicePriority, ((IXrdsProviderEndpoint)se).ServicePriority);
- }
-
- [TestMethod]
- public void CtorImpliedLocalIdentifier() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, null, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreSame(this.claimedId, se.ClaimedIdentifier);
- Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
- Assert.AreSame(this.claimedId, se.ProviderLocalIdentifier);
- CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.ProviderSupportedServiceTypeUris);
- }
-
- [TestMethod]
- public void ProtocolDetection() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreSame(Protocol.V20, se.Protocol);
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- this.claimedId,
- this.localId,
- new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.OPIdentifierServiceTypeURI }),
- this.servicePriority,
- this.uriPriority);
- Assert.AreSame(Protocol.V20, se.Protocol);
- se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreSame(Protocol.V11, se.Protocol);
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void ProtocolDetectionWithoutClues() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(
- this.claimedId,
- this.localId,
- new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.HtmlDiscoveryLocalIdKey }), // random type URI irrelevant to detection
- this.servicePriority,
- this.uriPriority);
- }
-
- [TestMethod]
- public void SerializationWithUri() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- StringBuilder sb = new StringBuilder();
- using (StringWriter sw = new StringWriter(sb)) {
- se.Serialize(sw);
- }
- using (StringReader sr = new StringReader(sb.ToString())) {
- ServiceEndpoint se2 = ServiceEndpoint.Deserialize(sr);
- Assert.AreEqual(se, se2);
- Assert.AreEqual(se.Protocol.Version, se2.Protocol.Version, "Particularly interested in this, since type URIs are not serialized but version info is.");
- Assert.AreEqual(se.UserSuppliedIdentifier, se2.UserSuppliedIdentifier);
- Assert.AreEqual(se.FriendlyIdentifierForDisplay, se2.FriendlyIdentifierForDisplay);
- }
- }
-
- [TestMethod]
- public void SerializationWithXri() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- StringBuilder sb = new StringBuilder();
- using (StringWriter sw = new StringWriter(sb)) {
- se.Serialize(sw);
- }
- using (StringReader sr = new StringReader(sb.ToString())) {
- ServiceEndpoint se2 = ServiceEndpoint.Deserialize(sr);
- Assert.AreEqual(se, se2);
- Assert.AreEqual(se.Protocol.Version, se2.Protocol.Version, "Particularly interested in this, since type URIs are not serialized but version info is.");
- Assert.AreEqual(se.UserSuppliedIdentifier, se2.UserSuppliedIdentifier);
- Assert.AreEqual(se.FriendlyIdentifierForDisplay, se2.FriendlyIdentifierForDisplay);
- }
- }
-
- [TestMethod]
- public void EqualsTests() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- ServiceEndpoint se2 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), (int?)null, (int?)null);
- Assert.AreEqual(se2, se);
- Assert.AreNotEqual(se, null);
- Assert.AreNotEqual(null, se);
-
- ServiceEndpoint se3 = ServiceEndpoint.CreateForClaimedIdentifier(new UriIdentifier(this.claimedId + "a"), this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
- se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(new Uri(this.providerEndpoint.AbsoluteUri + "a"), this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
- se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId + "a", new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
- se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
-
- // make sure that Collection<T>.Contains works as desired.
- List<ServiceEndpoint> list = new List<ServiceEndpoint>();
- list.Add(se);
- Assert.IsTrue(list.Contains(se2));
- }
-
- [TestMethod]
- public void FriendlyIdentifierForDisplay() {
- Uri providerEndpoint = new Uri("http://someprovider");
- Identifier localId = "someuser";
- string[] serviceTypeUris = new string[] {
- Protocol.V20.ClaimedIdentifierServiceTypeURI,
- };
- ServiceEndpoint se;
-
- // strip of protocol and fragment
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- "http://someprovider.somedomain.com:79/someuser#frag",
- localId,
- new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
- null,
- null);
- Assert.AreEqual("someprovider.somedomain.com:79/someuser", se.FriendlyIdentifierForDisplay);
-
- // unescape characters
- Uri foreignUri = new Uri("http://server崎/村");
- se = ServiceEndpoint.CreateForClaimedIdentifier(foreignUri, localId, new ProviderEndpointDescription(providerEndpoint, serviceTypeUris), null, null);
- Assert.AreEqual("server崎/村", se.FriendlyIdentifierForDisplay);
-
- // restore user supplied identifier to XRIs
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
- new XriIdentifier("=Arnott崎村"),
- localId,
- new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
- null,
- null);
- Assert.AreEqual("=Arnott崎村", se.FriendlyIdentifierForDisplay);
-
- // If UserSuppliedIdentifier is the same as the ClaimedIdentifier, don't display it twice...
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
- new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
- localId,
- new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
- null,
- null);
- Assert.AreEqual("=!9B72.7DD1.50A9.5CCD", se.FriendlyIdentifierForDisplay);
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ServiceEndpointTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Test.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ServiceEndpointTests : OpenIdTestBase {
+ private UriIdentifier claimedId = new UriIdentifier("http://claimedid.justatest.com");
+ private XriIdentifier claimedXri = new XriIdentifier("=!9B72.7DD1.50A9.5CCD");
+ private XriIdentifier userSuppliedXri = new XriIdentifier("=Arnot");
+ private Uri providerEndpoint = new Uri("http://someprovider.com");
+ private Identifier localId = "http://localid.someprovider.com";
+ private string[] v20TypeUris = { Protocol.V20.ClaimedIdentifierServiceTypeURI };
+ private string[] v11TypeUris = { Protocol.V11.ClaimedIdentifierServiceTypeURI };
+ private int servicePriority = 10;
+ private int uriPriority = 10;
+
+ [TestMethod]
+ public void Ctor() {
+ ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreSame(this.claimedId, se.ClaimedIdentifier);
+ Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
+ Assert.AreSame(this.localId, se.ProviderLocalIdentifier);
+ CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.ProviderSupportedServiceTypeUris);
+ Assert.AreEqual(this.servicePriority, ((IXrdsProviderEndpoint)se).ServicePriority);
+ }
+
+ [TestMethod]
+ public void CtorImpliedLocalIdentifier() {
+ ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, null, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreSame(this.claimedId, se.ClaimedIdentifier);
+ Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
+ Assert.AreSame(this.claimedId, se.ProviderLocalIdentifier);
+ CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.ProviderSupportedServiceTypeUris);
+ }
+
+ [TestMethod]
+ public void ProtocolDetection() {
+ ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreSame(Protocol.V20, se.Protocol);
+ se = ServiceEndpoint.CreateForClaimedIdentifier(
+ this.claimedId,
+ this.localId,
+ new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.OPIdentifierServiceTypeURI }),
+ this.servicePriority,
+ this.uriPriority);
+ Assert.AreSame(Protocol.V20, se.Protocol);
+ se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreSame(Protocol.V11, se.Protocol);
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void ProtocolDetectionWithoutClues() {
+ ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(
+ this.claimedId,
+ this.localId,
+ new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.HtmlDiscoveryLocalIdKey }), // random type URI irrelevant to detection
+ this.servicePriority,
+ this.uriPriority);
+ }
+
+ [TestMethod]
+ public void SerializationWithUri() {
+ ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ StringBuilder sb = new StringBuilder();
+ using (StringWriter sw = new StringWriter(sb)) {
+ se.Serialize(sw);
+ }
+ using (StringReader sr = new StringReader(sb.ToString())) {
+ ServiceEndpoint se2 = ServiceEndpoint.Deserialize(sr);
+ Assert.AreEqual(se, se2);
+ Assert.AreEqual(se.Protocol.Version, se2.Protocol.Version, "Particularly interested in this, since type URIs are not serialized but version info is.");
+ Assert.AreEqual(se.UserSuppliedIdentifier, se2.UserSuppliedIdentifier);
+ Assert.AreEqual(se.FriendlyIdentifierForDisplay, se2.FriendlyIdentifierForDisplay);
+ }
+ }
+
+ [TestMethod]
+ public void SerializationWithXri() {
+ ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ StringBuilder sb = new StringBuilder();
+ using (StringWriter sw = new StringWriter(sb)) {
+ se.Serialize(sw);
+ }
+ using (StringReader sr = new StringReader(sb.ToString())) {
+ ServiceEndpoint se2 = ServiceEndpoint.Deserialize(sr);
+ Assert.AreEqual(se, se2);
+ Assert.AreEqual(se.Protocol.Version, se2.Protocol.Version, "Particularly interested in this, since type URIs are not serialized but version info is.");
+ Assert.AreEqual(se.UserSuppliedIdentifier, se2.UserSuppliedIdentifier);
+ Assert.AreEqual(se.FriendlyIdentifierForDisplay, se2.FriendlyIdentifierForDisplay);
+ }
+ }
+
+ [TestMethod]
+ public void EqualsTests() {
+ ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ ServiceEndpoint se2 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), (int?)null, (int?)null);
+ Assert.AreEqual(se2, se);
+ Assert.AreNotEqual(se, null);
+ Assert.AreNotEqual(null, se);
+
+ ServiceEndpoint se3 = ServiceEndpoint.CreateForClaimedIdentifier(new UriIdentifier(this.claimedId + "a"), this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+ se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(new Uri(this.providerEndpoint.AbsoluteUri + "a"), this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+ se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId + "a", new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+ se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+
+ // make sure that Collection<T>.Contains works as desired.
+ List<ServiceEndpoint> list = new List<ServiceEndpoint>();
+ list.Add(se);
+ Assert.IsTrue(list.Contains(se2));
+ }
+
+ [TestMethod]
+ public void FriendlyIdentifierForDisplay() {
+ Uri providerEndpoint = new Uri("http://someprovider");
+ Identifier localId = "someuser";
+ string[] serviceTypeUris = new string[] {
+ Protocol.V20.ClaimedIdentifierServiceTypeURI,
+ };
+ ServiceEndpoint se;
+
+ // strip of protocol and fragment
+ se = ServiceEndpoint.CreateForClaimedIdentifier(
+ "http://someprovider.somedomain.com:79/someuser#frag",
+ localId,
+ new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
+ null,
+ null);
+ Assert.AreEqual("someprovider.somedomain.com:79/someuser", se.FriendlyIdentifierForDisplay);
+
+ // unescape characters
+ Uri foreignUri = new Uri("http://server崎/村");
+ se = ServiceEndpoint.CreateForClaimedIdentifier(foreignUri, localId, new ProviderEndpointDescription(providerEndpoint, serviceTypeUris), null, null);
+ Assert.AreEqual("server崎/村", se.FriendlyIdentifierForDisplay);
+
+ // restore user supplied identifier to XRIs
+ se = ServiceEndpoint.CreateForClaimedIdentifier(
+ new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
+ new XriIdentifier("=Arnott崎村"),
+ localId,
+ new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
+ null,
+ null);
+ Assert.AreEqual("=Arnott崎村", se.FriendlyIdentifierForDisplay);
+
+ // If UserSuppliedIdentifier is the same as the ClaimedIdentifier, don't display it twice...
+ se = ServiceEndpoint.CreateForClaimedIdentifier(
+ new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
+ new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
+ localId,
+ new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
+ null,
+ null);
+ Assert.AreEqual("=!9B72.7DD1.50A9.5CCD", se.FriendlyIdentifierForDisplay);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs b/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
index 57d781d..417c98e 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
@@ -1,37 +1,37 @@
-//-----------------------------------------------------------------------
-// <copyright file="ExpiredMessageException.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Bindings {
- using System;
- using System.Globalization;
-
- /// <summary>
- /// An exception thrown when a message is received that exceeds the maximum message age limit.
- /// </summary>
- [Serializable]
- internal class ExpiredMessageException : ProtocolException {
- /// <summary>
- /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
- /// </summary>
- /// <param name="utcExpirationDate">The date the message expired.</param>
- /// <param name="faultedMessage">The expired message.</param>
- public ExpiredMessageException(DateTime utcExpirationDate, IProtocolMessage faultedMessage)
- : base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.ExpiredMessage, utcExpirationDate.ToLocalTime(), DateTime.Now), faultedMessage) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
- /// </summary>
- /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
- /// that holds the serialized object data about the exception being thrown.</param>
- /// <param name="context">The System.Runtime.Serialization.StreamingContext
- /// that contains contextual information about the source or destination.</param>
- protected ExpiredMessageException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context)
- : base(info, context) { }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ExpiredMessageException.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+ using System.Globalization;
+
+ /// <summary>
+ /// An exception thrown when a message is received that exceeds the maximum message age limit.
+ /// </summary>
+ [Serializable]
+ internal class ExpiredMessageException : ProtocolException {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
+ /// </summary>
+ /// <param name="utcExpirationDate">The date the message expired.</param>
+ /// <param name="faultedMessage">The expired message.</param>
+ public ExpiredMessageException(DateTime utcExpirationDate, IProtocolMessage faultedMessage)
+ : base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.ExpiredMessage, utcExpirationDate.ToLocalTime(), DateTime.Now), faultedMessage) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
+ /// </summary>
+ /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
+ /// that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The System.Runtime.Serialization.StreamingContext
+ /// that contains contextual information about the source or destination.</param>
+ protected ExpiredMessageException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) { }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
index a00faf0..4e9cab9 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
@@ -1,124 +1,124 @@
-//-----------------------------------------------------------------------
-// <copyright file="StandardExpirationBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Bindings {
- using System;
-
- /// <summary>
- /// A message expiration enforcing binding element that supports messages
- /// implementing the <see cref="IExpiringProtocolMessage"/> interface.
- /// </summary>
- internal class StandardExpirationBindingElement : IChannelBindingElement {
- /// <summary>
- /// The default maximum message age to use if the default constructor is called.
- /// </summary>
- internal static readonly TimeSpan DefaultMaximumMessageAge = TimeSpan.FromMinutes(13);
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class
- /// with a default maximum message lifetime of 13 minutes.
- /// </summary>
- internal StandardExpirationBindingElement()
- : this(DefaultMaximumMessageAge) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class.
- /// </summary>
- /// <param name="maximumAge">
- /// <para>The maximum age a message implementing the
- /// <see cref="IExpiringProtocolMessage"/> interface can be before
- /// being discarded as too old.</para>
- /// <para>This time limit should take into account expected time skew for servers
- /// across the Internet. For example, if a server could conceivably have its
- /// clock d = 5 minutes off UTC time, then any two servers could have
- /// their clocks disagree by as much as 2*d = 10 minutes.
- /// If a message should live for at least t = 3 minutes,
- /// this property should be set to (2*d + t) = 13 minutes.</para>
- /// </param>
- internal StandardExpirationBindingElement(TimeSpan maximumAge) {
- this.MaximumMessageAge = maximumAge;
- }
-
- #region IChannelBindingElement Properties
-
- /// <summary>
- /// Gets the protection offered by this binding element.
- /// </summary>
- /// <value><see cref="MessageProtections.Expiration"/></value>
- MessageProtections IChannelBindingElement.Protection {
- get { return MessageProtections.Expiration; }
- }
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- public Channel Channel { get; set; }
-
- #endregion
-
- /// <summary>
- /// Gets the maximum age a message implementing the
- /// <see cref="IExpiringProtocolMessage"/> interface can be before
- /// being discarded as too old.
- /// </summary>
- protected internal TimeSpan MaximumMessageAge {
- get;
- private set;
- }
-
- #region IChannelBindingElement Methods
-
- /// <summary>
- /// Sets the timestamp on an outgoing message.
- /// </summary>
- /// <param name="message">The outgoing message.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False otherwise.
- /// </returns>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
- if (expiringMessage != null) {
- expiringMessage.UtcCreationDate = DateTime.UtcNow;
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Reads the timestamp on a message and throws an exception if the message is too old.
- /// </summary>
- /// <param name="message">The incoming message.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False if the operation did not apply to this message.
- /// </returns>
- /// <exception cref="ExpiredMessageException">Thrown if the given message has already expired.</exception>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
- if (expiringMessage != null) {
- // Yes the UtcCreationDate is supposed to always be in UTC already,
- // but just in case a given message failed to guarantee that, we do it here.
- DateTime expirationDate = expiringMessage.UtcCreationDate.ToUniversalTime() + this.MaximumMessageAge;
- if (expirationDate < DateTime.UtcNow) {
- throw new ExpiredMessageException(expirationDate, expiringMessage);
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="StandardExpirationBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+
+ /// <summary>
+ /// A message expiration enforcing binding element that supports messages
+ /// implementing the <see cref="IExpiringProtocolMessage"/> interface.
+ /// </summary>
+ internal class StandardExpirationBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The default maximum message age to use if the default constructor is called.
+ /// </summary>
+ internal static readonly TimeSpan DefaultMaximumMessageAge = TimeSpan.FromMinutes(13);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class
+ /// with a default maximum message lifetime of 13 minutes.
+ /// </summary>
+ internal StandardExpirationBindingElement()
+ : this(DefaultMaximumMessageAge) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class.
+ /// </summary>
+ /// <param name="maximumAge">
+ /// <para>The maximum age a message implementing the
+ /// <see cref="IExpiringProtocolMessage"/> interface can be before
+ /// being discarded as too old.</para>
+ /// <para>This time limit should take into account expected time skew for servers
+ /// across the Internet. For example, if a server could conceivably have its
+ /// clock d = 5 minutes off UTC time, then any two servers could have
+ /// their clocks disagree by as much as 2*d = 10 minutes.
+ /// If a message should live for at least t = 3 minutes,
+ /// this property should be set to (2*d + t) = 13 minutes.</para>
+ /// </param>
+ internal StandardExpirationBindingElement(TimeSpan maximumAge) {
+ this.MaximumMessageAge = maximumAge;
+ }
+
+ #region IChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets the protection offered by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.Expiration"/></value>
+ MessageProtections IChannelBindingElement.Protection {
+ get { return MessageProtections.Expiration; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ public Channel Channel { get; set; }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the maximum age a message implementing the
+ /// <see cref="IExpiringProtocolMessage"/> interface can be before
+ /// being discarded as too old.
+ /// </summary>
+ protected internal TimeSpan MaximumMessageAge {
+ get;
+ private set;
+ }
+
+ #region IChannelBindingElement Methods
+
+ /// <summary>
+ /// Sets the timestamp on an outgoing message.
+ /// </summary>
+ /// <param name="message">The outgoing message.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
+ if (expiringMessage != null) {
+ expiringMessage.UtcCreationDate = DateTime.UtcNow;
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Reads the timestamp on a message and throws an exception if the message is too old.
+ /// </summary>
+ /// <param name="message">The incoming message.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ExpiredMessageException">Thrown if the given message has already expired.</exception>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
+ if (expiringMessage != null) {
+ // Yes the UtcCreationDate is supposed to always be in UTC already,
+ // but just in case a given message failed to guarantee that, we do it here.
+ DateTime expirationDate = expiringMessage.UtcCreationDate.ToUniversalTime() + this.MaximumMessageAge;
+ if (expirationDate < DateTime.UtcNow) {
+ throw new ExpiredMessageException(expirationDate, expiringMessage);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
index 6748a9c..8061a25 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
@@ -1,152 +1,152 @@
-//-----------------------------------------------------------------------
-// <copyright file="StandardReplayProtectionBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Bindings {
- using System;
- using System.Diagnostics;
-
- /// <summary>
- /// A binding element that checks/verifies a nonce message part.
- /// </summary>
- internal class StandardReplayProtectionBindingElement : IChannelBindingElement {
- /// <summary>
- /// These are the characters that may be chosen from when forming a random nonce.
- /// </summary>
- private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
- /// <summary>
- /// The persistent store for nonces received.
- /// </summary>
- private INonceStore nonceStore;
-
- /// <summary>
- /// The length of generated nonces.
- /// </summary>
- private int nonceLength = 8;
-
- /// <summary>
- /// A random number generator.
- /// </summary>
- private Random generator = new Random();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
- /// </summary>
- /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
- internal StandardReplayProtectionBindingElement(INonceStore nonceStore)
- : this(nonceStore, false) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
- /// </summary>
- /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
- /// <param name="allowEmptyNonces">A value indicating whether zero-length nonces will be allowed.</param>
- internal StandardReplayProtectionBindingElement(INonceStore nonceStore, bool allowEmptyNonces) {
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
-
- this.nonceStore = nonceStore;
- this.AllowZeroLengthNonce = allowEmptyNonces;
- }
-
- #region IChannelBindingElement Properties
-
- /// <summary>
- /// Gets the protection that this binding element provides messages.
- /// </summary>
- public MessageProtections Protection {
- get { return MessageProtections.ReplayProtection; }
- }
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- public Channel Channel { get; set; }
-
- #endregion
-
- /// <summary>
- /// Gets or sets the strength of the nonce, which is measured by the number of
- /// nonces that could theoretically be generated.
- /// </summary>
- /// <remarks>
- /// The strength of the nonce is equal to the number of characters that might appear
- /// in the nonce to the power of the length of the nonce.
- /// </remarks>
- internal double NonceStrength {
- get {
- return Math.Pow(AllowedCharacters.Length, this.nonceLength);
- }
-
- set {
- value = Math.Max(value, AllowedCharacters.Length);
- this.nonceLength = (int)Math.Log(value, AllowedCharacters.Length);
- Debug.Assert(this.nonceLength > 0, "Nonce length calculated to be below 1!");
- }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether empty nonces are allowed.
- /// </summary>
- /// <value>Default is <c>false</c>.</value>
- internal bool AllowZeroLengthNonce { get; set; }
-
- #region IChannelBindingElement Methods
-
- /// <summary>
- /// Applies a nonce to the message.
- /// </summary>
- /// <param name="message">The message to apply replay protection to.</param>
- /// <returns>True if the message protection was applied. False otherwise.</returns>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
- if (nonceMessage != null) {
- nonceMessage.Nonce = this.GenerateUniqueFragment();
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Verifies that the nonce in an incoming message has not been seen before.
- /// </summary>
- /// <param name="message">The incoming message.</param>
- /// <returns>
- /// True if the message nonce passed replay detection checks.
- /// False if the message did not have a nonce that could be checked at all.
- /// </returns>
- /// <exception cref="ReplayedMessageException">Thrown when the nonce check revealed a replayed message.</exception>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
- if (nonceMessage != null && nonceMessage.Nonce != null) {
- ErrorUtilities.VerifyProtocol(nonceMessage.Nonce.Length > 0 || this.AllowZeroLengthNonce, MessagingStrings.InvalidNonceReceived);
-
- if (!this.nonceStore.StoreNonce(nonceMessage.Nonce, nonceMessage.UtcCreationDate)) {
- throw new ReplayedMessageException(message);
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
-
- /// <summary>
- /// Generates a string of random characters for use as a nonce.
- /// </summary>
- /// <returns>The nonce string.</returns>
- private string GenerateUniqueFragment() {
- char[] nonce = new char[this.nonceLength];
- for (int i = 0; i < nonce.Length; i++) {
- nonce[i] = AllowedCharacters[this.generator.Next(AllowedCharacters.Length)];
- }
- return new string(nonce);
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="StandardReplayProtectionBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+ using System.Diagnostics;
+
+ /// <summary>
+ /// A binding element that checks/verifies a nonce message part.
+ /// </summary>
+ internal class StandardReplayProtectionBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// These are the characters that may be chosen from when forming a random nonce.
+ /// </summary>
+ private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+ /// <summary>
+ /// The persistent store for nonces received.
+ /// </summary>
+ private INonceStore nonceStore;
+
+ /// <summary>
+ /// The length of generated nonces.
+ /// </summary>
+ private int nonceLength = 8;
+
+ /// <summary>
+ /// A random number generator.
+ /// </summary>
+ private Random generator = new Random();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
+ /// </summary>
+ /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
+ internal StandardReplayProtectionBindingElement(INonceStore nonceStore)
+ : this(nonceStore, false) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
+ /// </summary>
+ /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
+ /// <param name="allowEmptyNonces">A value indicating whether zero-length nonces will be allowed.</param>
+ internal StandardReplayProtectionBindingElement(INonceStore nonceStore, bool allowEmptyNonces) {
+ ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
+
+ this.nonceStore = nonceStore;
+ this.AllowZeroLengthNonce = allowEmptyNonces;
+ }
+
+ #region IChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets the protection that this binding element provides messages.
+ /// </summary>
+ public MessageProtections Protection {
+ get { return MessageProtections.ReplayProtection; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ public Channel Channel { get; set; }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the strength of the nonce, which is measured by the number of
+ /// nonces that could theoretically be generated.
+ /// </summary>
+ /// <remarks>
+ /// The strength of the nonce is equal to the number of characters that might appear
+ /// in the nonce to the power of the length of the nonce.
+ /// </remarks>
+ internal double NonceStrength {
+ get {
+ return Math.Pow(AllowedCharacters.Length, this.nonceLength);
+ }
+
+ set {
+ value = Math.Max(value, AllowedCharacters.Length);
+ this.nonceLength = (int)Math.Log(value, AllowedCharacters.Length);
+ Debug.Assert(this.nonceLength > 0, "Nonce length calculated to be below 1!");
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether empty nonces are allowed.
+ /// </summary>
+ /// <value>Default is <c>false</c>.</value>
+ internal bool AllowZeroLengthNonce { get; set; }
+
+ #region IChannelBindingElement Methods
+
+ /// <summary>
+ /// Applies a nonce to the message.
+ /// </summary>
+ /// <param name="message">The message to apply replay protection to.</param>
+ /// <returns>True if the message protection was applied. False otherwise.</returns>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
+ if (nonceMessage != null) {
+ nonceMessage.Nonce = this.GenerateUniqueFragment();
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Verifies that the nonce in an incoming message has not been seen before.
+ /// </summary>
+ /// <param name="message">The incoming message.</param>
+ /// <returns>
+ /// True if the message nonce passed replay detection checks.
+ /// False if the message did not have a nonce that could be checked at all.
+ /// </returns>
+ /// <exception cref="ReplayedMessageException">Thrown when the nonce check revealed a replayed message.</exception>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
+ if (nonceMessage != null && nonceMessage.Nonce != null) {
+ ErrorUtilities.VerifyProtocol(nonceMessage.Nonce.Length > 0 || this.AllowZeroLengthNonce, MessagingStrings.InvalidNonceReceived);
+
+ if (!this.nonceStore.StoreNonce(nonceMessage.Nonce, nonceMessage.UtcCreationDate)) {
+ throw new ReplayedMessageException(message);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Generates a string of random characters for use as a nonce.
+ /// </summary>
+ /// <returns>The nonce string.</returns>
+ private string GenerateUniqueFragment() {
+ char[] nonce = new char[this.nonceLength];
+ for (int i = 0; i < nonce.Length; i++) {
+ nonce[i] = AllowedCharacters[this.generator.Next(AllowedCharacters.Length)];
+ }
+ return new string(nonce);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 9e7accf..53465e7 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -1,833 +1,833 @@
-//-----------------------------------------------------------------------
-// <copyright file="Channel.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Web;
- using DotNetOpenAuth.Messaging.Reflection;
-
- /// <summary>
- /// Manages sending direct messages to a remote party and receiving responses.
- /// </summary>
- public abstract class Channel {
- /// <summary>
- /// The maximum allowable size for a 301 Redirect response before we send
- /// a 200 OK response with a scripted form POST with the parameters instead
- /// in order to ensure successfully sending a large payload to another server
- /// that might have a maximum allowable size restriction on its GET request.
- /// </summary>
- private static int indirectMessageGetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group
-
- /// <summary>
- /// The template for indirect messages that require form POST to forward through the user agent.
- /// </summary>
- /// <remarks>
- /// We are intentionally using " instead of the html single quote ' below because
- /// the HtmlEncode'd values that we inject will only escape the double quote, so
- /// only the double-quote used around these values is safe.
- /// </remarks>
- private static string indirectMessageFormPostFormat = @"
-<html>
-<body onload=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; document.getElementById('openid_message').submit()"">
-<form id=""openid_message"" action=""{0}"" method=""post"" accept-charset=""UTF-8"" enctype=""application/x-www-form-urlencoded"" onSubmit=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; return true;"">
-{1}
- <input id=""submit_button"" type=""submit"" value=""Continue"" />
-</form>
-</body>
-</html>
-";
-
- /// <summary>
- /// A tool that can figure out what kind of message is being received
- /// so it can be deserialized.
- /// </summary>
- private IMessageFactory messageTypeProvider;
-
- /// <summary>
- /// A list of binding elements in the order they must be applied to outgoing messages.
- /// </summary>
- [DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private List<IChannelBindingElement> outgoingBindingElements = new List<IChannelBindingElement>();
-
- /// <summary>
- /// A list of binding elements in the order they must be applied to incoming messages.
- /// </summary>
- private List<IChannelBindingElement> incomingBindingElements = new List<IChannelBindingElement>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Channel"/> class.
- /// </summary>
- /// <param name="messageTypeProvider">
- /// A class prepared to analyze incoming messages and indicate what concrete
- /// message types can deserialize from it.
- /// </param>
- /// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param>
- protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) {
- if (messageTypeProvider == null) {
- throw new ArgumentNullException("messageTypeProvider");
- }
-
- this.messageTypeProvider = messageTypeProvider;
- this.WebRequestHandler = new StandardWebRequestHandler();
- this.outgoingBindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
- this.incomingBindingElements = new List<IChannelBindingElement>(this.outgoingBindingElements);
- this.incomingBindingElements.Reverse();
-
- foreach (var element in this.outgoingBindingElements) {
- element.Channel = this;
- }
- }
-
- /// <summary>
- /// An event fired whenever a message is about to be encoded and sent.
- /// </summary>
- internal event EventHandler<ChannelEventArgs> Sending;
-
- /// <summary>
- /// Gets or sets an instance to a <see cref="IDirectWebRequestHandler"/> that will be used when
- /// submitting HTTP requests and waiting for responses.
- /// </summary>
- /// <remarks>
- /// This defaults to a straightforward implementation, but can be set
- /// to a mock object for testing purposes.
- /// </remarks>
- public IDirectWebRequestHandler WebRequestHandler { get; set; }
-
- /// <summary>
- /// Gets the binding elements used by this channel, in no particular guaranteed order.
- /// </summary>
- protected internal ReadOnlyCollection<IChannelBindingElement> BindingElements {
- get { return this.outgoingBindingElements.AsReadOnly(); }
- }
-
- /// <summary>
- /// Gets the binding elements used by this channel, in the order applied to outgoing messages.
- /// </summary>
- protected internal ReadOnlyCollection<IChannelBindingElement> OutgoingBindingElements {
- get { return this.outgoingBindingElements.AsReadOnly(); }
- }
-
- /// <summary>
- /// Gets the binding elements used by this channel, in the order applied to incoming messages.
- /// </summary>
- protected internal ReadOnlyCollection<IChannelBindingElement> IncomingBindingElements {
- get { return this.incomingBindingElements.AsReadOnly(); }
- }
-
- /// <summary>
- /// Gets a tool that can figure out what kind of message is being received
- /// so it can be deserialized.
- /// </summary>
- protected IMessageFactory MessageFactory {
- get { return this.messageTypeProvider; }
- }
-
- /// <summary>
- /// Queues an indirect message (either a request or response)
- /// or direct message response for transmission to a remote party.
- /// </summary>
- /// <param name="message">The one-way message to send</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- public UserAgentResponse Send(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
- this.PrepareMessageForSending(message);
- Logger.DebugFormat("Sending message: {0}", message);
-
- switch (message.Transport) {
- case MessageTransport.Direct:
- // This is a response to a direct message.
- return this.SendDirectMessageResponse(message);
- case MessageTransport.Indirect:
- var directedMessage = message as IDirectedProtocolMessage;
- if (directedMessage == null) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.IndirectMessagesMustImplementIDirectedProtocolMessage,
- typeof(IDirectedProtocolMessage).FullName),
- "message");
- }
- if (directedMessage.Recipient == null) {
- throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
- }
- return this.SendIndirectMessage(directedMessage);
- default:
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnrecognizedEnumValue,
- "Transport",
- message.Transport),
- "message");
- }
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- public IDirectedProtocolMessage ReadFromRequest() {
- return this.ReadFromRequest(this.GetRequestFromContext());
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
- /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
- public bool TryReadFromRequest<TRequest>(out TRequest request)
- where TRequest : class, IProtocolMessage {
- return TryReadFromRequest<TRequest>(this.GetRequestFromContext(), out request);
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
- /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
- public bool TryReadFromRequest<TRequest>(HttpRequestInfo httpRequest, out TRequest request)
- where TRequest : class, IProtocolMessage {
- IProtocolMessage untypedRequest = this.ReadFromRequest(httpRequest);
- if (untypedRequest == null) {
- request = null;
- return false;
- }
-
- request = untypedRequest as TRequest;
- if (request == null) {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedMessageReceived,
- typeof(TRequest),
- untypedRequest.GetType()));
- }
-
- return true;
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <returns>The deserialized message.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TRequest ReadFromRequest<TRequest>()
- where TRequest : class, IProtocolMessage {
- return this.ReadFromRequest<TRequest>(this.GetRequestFromContext());
- }
-
- /// <summary>
- /// Gets the protocol message that may be embedded in the given HTTP request.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest)
- where TRequest : class, IProtocolMessage {
- TRequest request;
- if (this.TryReadFromRequest<TRequest>(httpRequest, out request)) {
- return request;
- } else {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.ExpectedMessageNotReceived,
- typeof(TRequest)));
- }
- }
-
- /// <summary>
- /// Gets the protocol message that may be embedded in the given HTTP request.
- /// </summary>
- /// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- public IDirectedProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) {
- IDirectedProtocolMessage requestMessage = this.ReadFromRequestInternal(httpRequest);
- if (requestMessage != null) {
- Logger.DebugFormat("Incoming request received: {0}", requestMessage);
- this.VerifyMessageAfterReceiving(requestMessage);
- }
-
- return requestMessage;
- }
-
- /// <summary>
- /// Sends a direct message to a remote party and waits for the response.
- /// </summary>
- /// <typeparam name="TResponse">The expected type of the message to be received.</typeparam>
- /// <param name="requestMessage">The message to send.</param>
- /// <returns>The remote party's response.</returns>
- /// <exception cref="ProtocolException">
- /// Thrown if no message is recognized in the response
- /// or an unexpected type of message is received.
- /// </exception>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TResponse Request<TResponse>(IDirectedProtocolMessage requestMessage)
- where TResponse : class, IProtocolMessage {
- IProtocolMessage response = this.Request(requestMessage);
- ErrorUtilities.VerifyProtocol(response != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TResponse));
-
- var expectedResponse = response as TResponse;
- ErrorUtilities.VerifyProtocol(expectedResponse != null, MessagingStrings.UnexpectedMessageReceived, typeof(TResponse), response.GetType());
-
- return expectedResponse;
- }
-
- /// <summary>
- /// Sends a direct message to a remote party and waits for the response.
- /// </summary>
- /// <param name="requestMessage">The message to send.</param>
- /// <returns>The remote party's response. Guaranteed to never be null.</returns>
- /// <exception cref="ProtocolException">Thrown if the response does not include a protocol message.</exception>
- public IProtocolMessage Request(IDirectedProtocolMessage requestMessage) {
- if (requestMessage == null) {
- throw new ArgumentNullException("requestMessage");
- }
-
- this.PrepareMessageForSending(requestMessage);
- Logger.DebugFormat("Sending request: {0}", requestMessage);
- var responseMessage = this.RequestInternal(requestMessage);
- ErrorUtilities.VerifyProtocol(responseMessage != null, MessagingStrings.ExpectedMessageNotReceived, typeof(IProtocolMessage).Name);
-
- Logger.DebugFormat("Received message response: {0}", responseMessage);
- this.VerifyMessageAfterReceiving(responseMessage);
-
- return responseMessage;
- }
-
- /// <summary>
- /// Gets the current HTTP request being processed.
- /// </summary>
- /// <returns>The HttpRequestInfo for the current request.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")]
- protected internal virtual HttpRequestInfo GetRequestFromContext() {
- if (HttpContext.Current == null) {
- throw new InvalidOperationException(MessagingStrings.HttpContextRequired);
- }
-
- return new HttpRequestInfo(HttpContext.Current.Request);
- }
-
- /// <summary>
- /// Fires the <see cref="Sending"/> event.
- /// </summary>
- /// <param name="message">The message about to be encoded and sent.</param>
- protected virtual void OnSending(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- var sending = this.Sending;
- if (sending != null) {
- sending(this, new ChannelEventArgs(message));
- }
- }
-
- /// <summary>
- /// Submits a direct request message to some remote party and blocks waiting for an immediately reply.
- /// </summary>
- /// <param name="request">The request message.</param>
- /// <returns>The response message, or null if the response did not carry a message.</returns>
- /// <remarks>
- /// Typically a deriving channel will override <see cref="CreateHttpRequest"/> to customize this method's
- /// behavior. However in non-HTTP frameworks, such as unit test mocks, it may be appropriate to override
- /// this method to eliminate all use of an HTTP transport.
- /// </remarks>
- protected virtual IProtocolMessage RequestInternal(IDirectedProtocolMessage request) {
- HttpWebRequest webRequest = this.CreateHttpRequest(request);
- IDictionary<string, string> responseFields;
-
- using (DirectWebResponse response = this.WebRequestHandler.GetResponse(webRequest)) {
- if (response.ResponseStream == null) {
- return null;
- }
-
- responseFields = this.ReadFromResponseInternal(response);
- }
-
- IDirectResponseProtocolMessage responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseFields);
- if (responseMessage == null) {
- return null;
- }
-
- var responseSerialize = MessageSerializer.Get(responseMessage.GetType());
- responseSerialize.Deserialize(responseFields, responseMessage);
-
- return responseMessage;
- }
-
- /// <summary>
- /// Gets the protocol message that may be embedded in the given HTTP request.
- /// </summary>
- /// <param name="request">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- protected virtual IDirectedProtocolMessage ReadFromRequestInternal(HttpRequestInfo request) {
- if (request == null) {
- throw new ArgumentNullException("request");
- }
-
- // Search Form data first, and if nothing is there search the QueryString
- var fields = request.Form.ToDictionary();
- if (fields.Count == 0) {
- fields = request.QueryString.ToDictionary();
- }
-
- return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
- }
-
- /// <summary>
- /// Deserializes a dictionary of values into a message.
- /// </summary>
- /// <param name="fields">The dictionary of values that were read from an HTTP request or response.</param>
- /// <param name="recipient">Information about where the message was been directed. Null for direct response messages.</param>
- /// <returns>The deserialized message, or null if no message could be recognized in the provided data.</returns>
- protected virtual IProtocolMessage Receive(Dictionary<string, string> fields, MessageReceivingEndpoint recipient) {
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
-
- IProtocolMessage message = this.MessageFactory.GetNewRequestMessage(recipient, fields);
-
- // If there was no data, or we couldn't recognize it as a message, abort.
- if (message == null) {
- return null;
- }
-
- // We have a message! Assemble it.
- var serializer = MessageSerializer.Get(message.GetType());
- serializer.Deserialize(fields, message);
-
- return message;
- }
-
- /// <summary>
- /// Queues an indirect message for transmittal via the user agent.
- /// </summary>
- /// <param name="message">The message to send.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- protected virtual UserAgentResponse SendIndirectMessage(IDirectedProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- var serializer = MessageSerializer.Get(message.GetType());
- var fields = serializer.Serialize(message);
-
- // First try creating a 301 redirect, and fallback to a form POST
- // if the message is too big.
- UserAgentResponse response = this.Create301RedirectResponse(message, fields);
- if (response.Headers[HttpResponseHeader.Location].Length > indirectMessageGetToPostThreshold) {
- response = this.CreateFormPostResponse(message, fields);
- }
-
- return response;
- }
-
- /// <summary>
- /// Encodes an HTTP response that will instruct the user agent to forward a message to
- /// some remote third party using a 301 Redirect GET method.
- /// </summary>
- /// <param name="message">The message to forward.</param>
- /// <param name="fields">The pre-serialized fields from the message.</param>
- /// <returns>The encoded HTTP response.</returns>
- protected virtual UserAgentResponse Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
- if (message.Recipient == null) {
- throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
- }
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
-
- WebHeaderCollection headers = new WebHeaderCollection();
- UriBuilder builder = new UriBuilder(message.Recipient);
- MessagingUtilities.AppendQueryArgs(builder, fields);
- headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
- Logger.DebugFormat("Redirecting to {0}", builder.Uri.AbsoluteUri);
- UserAgentResponse response = new UserAgentResponse {
- Status = HttpStatusCode.Redirect,
- Headers = headers,
- Body = null,
- OriginalMessage = message
- };
-
- return response;
- }
-
- /// <summary>
- /// Encodes an HTTP response that will instruct the user agent to forward a message to
- /// some remote third party using a form POST method.
- /// </summary>
- /// <param name="message">The message to forward.</param>
- /// <param name="fields">The pre-serialized fields from the message.</param>
- /// <returns>The encoded HTTP response.</returns>
- protected virtual UserAgentResponse CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
- if (message.Recipient == null) {
- throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
- }
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
-
- WebHeaderCollection headers = new WebHeaderCollection();
- StringWriter bodyWriter = new StringWriter(CultureInfo.InvariantCulture);
- StringBuilder hiddenFields = new StringBuilder();
- foreach (var field in fields) {
- hiddenFields.AppendFormat(
- "\t<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />\r\n",
- HttpUtility.HtmlEncode(field.Key),
- HttpUtility.HtmlEncode(field.Value));
- }
- bodyWriter.WriteLine(
- indirectMessageFormPostFormat,
- HttpUtility.HtmlEncode(message.Recipient.AbsoluteUri),
- hiddenFields);
- bodyWriter.Flush();
- UserAgentResponse response = new UserAgentResponse {
- Status = HttpStatusCode.OK,
- Headers = headers,
- Body = bodyWriter.ToString(),
- OriginalMessage = message
- };
-
- return response;
- }
-
- /// <summary>
- /// Gets the protocol message that may be in the given HTTP response.
- /// </summary>
- /// <param name="response">The response that is anticipated to contain an protocol message.</param>
- /// <returns>The deserialized message parts, if found. Null otherwise.</returns>
- protected abstract IDictionary<string, string> ReadFromResponseInternal(DirectWebResponse response);
-
- /// <summary>
- /// Prepares an HTTP request that carries a given message.
- /// </summary>
- /// <param name="request">The message to send.</param>
- /// <returns>The <see cref="HttpWebRequest"/> prepared to send the request.</returns>
- /// <remarks>
- /// This method must be overridden by a derived class, unless the <see cref="RequestInternal"/> method
- /// is overridden and does not require this method.
- /// </remarks>
- protected virtual HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Queues a message for sending in the response stream where the fields
- /// are sent in the response stream in querystring style.
- /// </summary>
- /// <param name="response">The message to send as a response.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- /// <remarks>
- /// This method implements spec V1.0 section 5.3.
- /// </remarks>
- protected abstract UserAgentResponse SendDirectMessageResponse(IProtocolMessage response);
-
- /// <summary>
- /// Prepares a message for transmit by applying signatures, nonces, etc.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <remarks>
- /// This method should NOT be called by derived types
- /// except when sending ONE WAY request messages.
- /// </remarks>
- protected void PrepareMessageForSending(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- Logger.DebugFormat("Preparing to send {0} ({1}) message.", message.GetType().Name, message.Version);
- this.OnSending(message);
-
- MessageProtections appliedProtection = MessageProtections.None;
- foreach (IChannelBindingElement bindingElement in this.outgoingBindingElements) {
- if (bindingElement.PrepareMessageForSending(message)) {
- Logger.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
-
- // Ensure that only one protection binding element applies to this message
- // for each protection type.
- ErrorUtilities.VerifyProtocol((appliedProtection & bindingElement.Protection) == 0, MessagingStrings.TooManyBindingsOfferingSameProtection, bindingElement.Protection);
- appliedProtection |= bindingElement.Protection;
- } else {
- Logger.DebugFormat("Binding element {0} did not apply to message.", bindingElement.GetType().FullName);
- }
- }
-
- // Ensure that the message's protection requirements have been satisfied.
- if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
- throw new UnprotectedMessageException(message, appliedProtection);
- }
-
- EnsureValidMessageParts(message);
- message.EnsureValidMessage();
- }
-
- /// <summary>
- /// Prepares to send a request to the Service Provider as the query string in a GET request.
- /// </summary>
- /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
- /// <returns>The web request ready to send.</returns>
- /// <remarks>
- /// This method is simply a standard HTTP Get request with the message parts serialized to the query string.
- /// This method satisfies OAuth 1.0 section 5.2, item #3.
- /// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
- if (requestMessage == null) {
- throw new ArgumentNullException("requestMessage");
- }
-
- var serializer = MessageSerializer.Get(requestMessage.GetType());
- var fields = serializer.Serialize(requestMessage);
-
- UriBuilder builder = new UriBuilder(requestMessage.Recipient);
- MessagingUtilities.AppendQueryArgs(builder, fields);
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
-
- return httpRequest;
- }
-
- /// <summary>
- /// Prepares to send a request to the Service Provider as the payload of a POST request.
- /// </summary>
- /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
- /// <returns>The web request ready to send.</returns>
- /// <remarks>
- /// This method is simply a standard HTTP POST request with the message parts serialized to the POST entity
- /// with the application/x-www-form-urlencoded content type
- /// This method satisfies OAuth 1.0 section 5.2, item #2 and OpenID 2.0 section 4.1.2.
- /// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
- if (requestMessage == null) {
- throw new ArgumentNullException("requestMessage");
- }
-
- var serializer = MessageSerializer.Get(requestMessage.GetType());
- var fields = serializer.Serialize(requestMessage);
-
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
- httpRequest.Method = "POST";
- httpRequest.ContentType = "application/x-www-form-urlencoded";
- string requestBody = MessagingUtilities.CreateQueryString(fields);
- httpRequest.ContentLength = requestBody.Length;
- using (TextWriter writer = this.WebRequestHandler.GetRequestStream(httpRequest)) {
- writer.Write(requestBody);
- }
-
- return httpRequest;
- }
-
- /// <summary>
- /// Verifies the integrity and applicability of an incoming message.
- /// </summary>
- /// <param name="message">The message just received.</param>
- /// <exception cref="ProtocolException">
- /// Thrown when the message is somehow invalid.
- /// This can be due to tampering, replay attack or expiration, among other things.
- /// </exception>
- protected virtual void VerifyMessageAfterReceiving(IProtocolMessage message) {
- Debug.Assert(message != null, "message == null");
-
- Logger.DebugFormat("Preparing to receive {0} ({1}) message.", message.GetType().Name, message.Version);
-
- MessageProtections appliedProtection = MessageProtections.None;
- foreach (IChannelBindingElement bindingElement in this.incomingBindingElements) {
- if (bindingElement.PrepareMessageForReceiving(message)) {
- Logger.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
-
- // Ensure that only one protection binding element applies to this message
- // for each protection type.
- ErrorUtilities.VerifyInternal((appliedProtection & bindingElement.Protection) == 0, MessagingStrings.TooManyBindingsOfferingSameProtection, bindingElement.Protection);
- appliedProtection |= bindingElement.Protection;
- } else {
- Logger.DebugFormat("Binding element {0} did not apply to message.", bindingElement.GetType().FullName);
- }
- }
-
- // Ensure that the message's protection requirements have been satisfied.
- if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
- throw new UnprotectedMessageException(message, appliedProtection);
- }
-
- // We do NOT verify that all required message parts are present here... the
- // message deserializer did for us. It would be too late to do it here since
- // they might look initialized by the time we have an IProtocolMessage instance.
- message.EnsureValidMessage();
- }
-
- /// <summary>
- /// Customizes the binding element order for outgoing and incoming messages.
- /// </summary>
- /// <param name="outgoingOrder">The outgoing order.</param>
- /// <param name="incomingOrder">The incoming order.</param>
- /// <remarks>
- /// No binding elements can be added or removed from the channel using this method.
- /// Only a customized order is allowed.
- /// </remarks>
- /// <exception cref="ArgumentException">Thrown if a binding element is new or missing in one of the ordered lists.</exception>
- protected void CustomizeBindingElementOrder(IEnumerable<IChannelBindingElement> outgoingOrder, IEnumerable<IChannelBindingElement> incomingOrder) {
- ErrorUtilities.VerifyArgumentNotNull(outgoingOrder, "outgoingOrder");
- ErrorUtilities.VerifyArgumentNotNull(incomingOrder, "incomingOrder");
-
- ErrorUtilities.VerifyArgument(this.IsBindingElementOrderValid(outgoingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
- ErrorUtilities.VerifyArgument(this.IsBindingElementOrderValid(incomingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
-
- this.outgoingBindingElements.Clear();
- this.outgoingBindingElements.AddRange(outgoingOrder);
- this.incomingBindingElements.Clear();
- this.incomingBindingElements.AddRange(incomingOrder);
- }
-
- /// <summary>
- /// Verifies that all required message parts are initialized to values
- /// prior to sending the message to a remote party.
- /// </summary>
- /// <param name="message">The message to verify.</param>
- /// <exception cref="ProtocolException">
- /// Thrown when any required message part does not have a value.
- /// </exception>
- private static void EnsureValidMessageParts(IProtocolMessage message) {
- Debug.Assert(message != null, "message == null");
-
- MessageDictionary dictionary = new MessageDictionary(message);
- MessageDescription description = MessageDescription.Get(message.GetType(), message.Version);
- description.EnsureMessagePartsPassBasicValidation(dictionary);
- }
-
- /// <summary>
- /// Ensures a consistent and secure set of binding elements and
- /// sorts them as necessary for a valid sequence of operations.
- /// </summary>
- /// <param name="elements">The binding elements provided to the channel.</param>
- /// <returns>The properly ordered list of elements.</returns>
- /// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
- private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
- if (elements == null) {
- return new IChannelBindingElement[0];
- }
- if (elements.Contains(null)) {
- throw new ArgumentException(MessagingStrings.SequenceContainsNullElement, "elements");
- }
-
- // Filter the elements between the mere transforming ones and the protection ones.
- var transformationElements = new List<IChannelBindingElement>(
- elements.Where(element => element.Protection == MessageProtections.None));
- var protectionElements = new List<IChannelBindingElement>(
- elements.Where(element => element.Protection != MessageProtections.None));
-
- bool wasLastProtectionPresent = true;
- foreach (MessageProtections protectionKind in Enum.GetValues(typeof(MessageProtections))) {
- if (protectionKind == MessageProtections.None) {
- continue;
- }
-
- int countProtectionsOfThisKind = protectionElements.Count(element => (element.Protection & protectionKind) == protectionKind);
-
- // Each protection binding element is backed by the presence of its dependent protection(s).
- if (countProtectionsOfThisKind > 0 && !wasLastProtectionPresent) {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.RequiredProtectionMissing,
- protectionKind));
- }
-
- wasLastProtectionPresent = countProtectionsOfThisKind > 0;
- }
-
- // Put the binding elements in order so they are correctly applied to outgoing messages.
- // Start with the transforming (non-protecting) binding elements first and preserve their original order.
- var orderedList = new List<IChannelBindingElement>(transformationElements);
-
- // Now sort the protection binding elements among themselves and add them to the list.
- orderedList.AddRange(protectionElements.OrderBy(element => element.Protection, BindingElementOutgoingMessageApplicationOrder));
- return orderedList;
- }
-
- /// <summary>
- /// Puts binding elements in their correct outgoing message processing order.
- /// </summary>
- /// <param name="protection1">The first protection type to compare.</param>
- /// <param name="protection2">The second protection type to compare.</param>
- /// <returns>
- /// -1 if <paramref name="element1"/> should be applied to an outgoing message before <paramref name="element2"/>.
- /// 1 if <paramref name="element2"/> should be applied to an outgoing message before <paramref name="element1"/>.
- /// 0 if it doesn't matter.
- /// </returns>
- private static int BindingElementOutgoingMessageApplicationOrder(MessageProtections protection1, MessageProtections protection2) {
- Debug.Assert(protection1 != MessageProtections.None || protection2 != MessageProtections.None, "This comparison function should only be used to compare protection binding elements. Otherwise we change the order of user-defined message transformations.");
-
- // Now put the protection ones in the right order.
- return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
- }
-
- /// <summary>
- /// Determines whether a given ordered list of binding elements includes every
- /// binding element in this channel exactly once.
- /// </summary>
- /// <param name="order">The list of binding elements to test.</param>
- /// <returns>
- /// <c>true</c> if the given list is a valid description of a binding element ordering; otherwise, <c>false</c>.
- /// </returns>
- private bool IsBindingElementOrderValid(IEnumerable<IChannelBindingElement> order) {
- ErrorUtilities.VerifyArgumentNotNull(order, "order");
-
- // Check that the same number of binding elements are defined.
- if (order.Count() != this.OutgoingBindingElements.Count) {
- return false;
- }
-
- // Check that every binding element appears exactly once.
- if (order.Any(el => !this.OutgoingBindingElements.Contains(el))) {
- return false;
- }
-
- return true;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="Channel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ /// <summary>
+ /// Manages sending direct messages to a remote party and receiving responses.
+ /// </summary>
+ public abstract class Channel {
+ /// <summary>
+ /// The maximum allowable size for a 301 Redirect response before we send
+ /// a 200 OK response with a scripted form POST with the parameters instead
+ /// in order to ensure successfully sending a large payload to another server
+ /// that might have a maximum allowable size restriction on its GET request.
+ /// </summary>
+ private static int indirectMessageGetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group
+
+ /// <summary>
+ /// The template for indirect messages that require form POST to forward through the user agent.
+ /// </summary>
+ /// <remarks>
+ /// We are intentionally using " instead of the html single quote ' below because
+ /// the HtmlEncode'd values that we inject will only escape the double quote, so
+ /// only the double-quote used around these values is safe.
+ /// </remarks>
+ private static string indirectMessageFormPostFormat = @"
+<html>
+<body onload=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; document.getElementById('openid_message').submit()"">
+<form id=""openid_message"" action=""{0}"" method=""post"" accept-charset=""UTF-8"" enctype=""application/x-www-form-urlencoded"" onSubmit=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; return true;"">
+{1}
+ <input id=""submit_button"" type=""submit"" value=""Continue"" />
+</form>
+</body>
+</html>
+";
+
+ /// <summary>
+ /// A tool that can figure out what kind of message is being received
+ /// so it can be deserialized.
+ /// </summary>
+ private IMessageFactory messageTypeProvider;
+
+ /// <summary>
+ /// A list of binding elements in the order they must be applied to outgoing messages.
+ /// </summary>
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private List<IChannelBindingElement> outgoingBindingElements = new List<IChannelBindingElement>();
+
+ /// <summary>
+ /// A list of binding elements in the order they must be applied to incoming messages.
+ /// </summary>
+ private List<IChannelBindingElement> incomingBindingElements = new List<IChannelBindingElement>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Channel"/> class.
+ /// </summary>
+ /// <param name="messageTypeProvider">
+ /// A class prepared to analyze incoming messages and indicate what concrete
+ /// message types can deserialize from it.
+ /// </param>
+ /// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param>
+ protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) {
+ if (messageTypeProvider == null) {
+ throw new ArgumentNullException("messageTypeProvider");
+ }
+
+ this.messageTypeProvider = messageTypeProvider;
+ this.WebRequestHandler = new StandardWebRequestHandler();
+ this.outgoingBindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
+ this.incomingBindingElements = new List<IChannelBindingElement>(this.outgoingBindingElements);
+ this.incomingBindingElements.Reverse();
+
+ foreach (var element in this.outgoingBindingElements) {
+ element.Channel = this;
+ }
+ }
+
+ /// <summary>
+ /// An event fired whenever a message is about to be encoded and sent.
+ /// </summary>
+ internal event EventHandler<ChannelEventArgs> Sending;
+
+ /// <summary>
+ /// Gets or sets an instance to a <see cref="IDirectWebRequestHandler"/> that will be used when
+ /// submitting HTTP requests and waiting for responses.
+ /// </summary>
+ /// <remarks>
+ /// This defaults to a straightforward implementation, but can be set
+ /// to a mock object for testing purposes.
+ /// </remarks>
+ public IDirectWebRequestHandler WebRequestHandler { get; set; }
+
+ /// <summary>
+ /// Gets the binding elements used by this channel, in no particular guaranteed order.
+ /// </summary>
+ protected internal ReadOnlyCollection<IChannelBindingElement> BindingElements {
+ get { return this.outgoingBindingElements.AsReadOnly(); }
+ }
+
+ /// <summary>
+ /// Gets the binding elements used by this channel, in the order applied to outgoing messages.
+ /// </summary>
+ protected internal ReadOnlyCollection<IChannelBindingElement> OutgoingBindingElements {
+ get { return this.outgoingBindingElements.AsReadOnly(); }
+ }
+
+ /// <summary>
+ /// Gets the binding elements used by this channel, in the order applied to incoming messages.
+ /// </summary>
+ protected internal ReadOnlyCollection<IChannelBindingElement> IncomingBindingElements {
+ get { return this.incomingBindingElements.AsReadOnly(); }
+ }
+
+ /// <summary>
+ /// Gets a tool that can figure out what kind of message is being received
+ /// so it can be deserialized.
+ /// </summary>
+ protected IMessageFactory MessageFactory {
+ get { return this.messageTypeProvider; }
+ }
+
+ /// <summary>
+ /// Queues an indirect message (either a request or response)
+ /// or direct message response for transmission to a remote party.
+ /// </summary>
+ /// <param name="message">The one-way message to send</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ public UserAgentResponse Send(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ this.PrepareMessageForSending(message);
+ Logger.DebugFormat("Sending message: {0}", message);
+
+ switch (message.Transport) {
+ case MessageTransport.Direct:
+ // This is a response to a direct message.
+ return this.SendDirectMessageResponse(message);
+ case MessageTransport.Indirect:
+ var directedMessage = message as IDirectedProtocolMessage;
+ if (directedMessage == null) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.IndirectMessagesMustImplementIDirectedProtocolMessage,
+ typeof(IDirectedProtocolMessage).FullName),
+ "message");
+ }
+ if (directedMessage.Recipient == null) {
+ throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
+ }
+ return this.SendIndirectMessage(directedMessage);
+ default:
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnrecognizedEnumValue,
+ "Transport",
+ message.Transport),
+ "message");
+ }
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ public IDirectedProtocolMessage ReadFromRequest() {
+ return this.ReadFromRequest(this.GetRequestFromContext());
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
+ /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
+ public bool TryReadFromRequest<TRequest>(out TRequest request)
+ where TRequest : class, IProtocolMessage {
+ return TryReadFromRequest<TRequest>(this.GetRequestFromContext(), out request);
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <param name="httpRequest">The request to search for an embedded message.</param>
+ /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
+ /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
+ public bool TryReadFromRequest<TRequest>(HttpRequestInfo httpRequest, out TRequest request)
+ where TRequest : class, IProtocolMessage {
+ IProtocolMessage untypedRequest = this.ReadFromRequest(httpRequest);
+ if (untypedRequest == null) {
+ request = null;
+ return false;
+ }
+
+ request = untypedRequest as TRequest;
+ if (request == null) {
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedMessageReceived,
+ typeof(TRequest),
+ untypedRequest.GetType()));
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <returns>The deserialized message.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
+ public TRequest ReadFromRequest<TRequest>()
+ where TRequest : class, IProtocolMessage {
+ return this.ReadFromRequest<TRequest>(this.GetRequestFromContext());
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <param name="httpRequest">The request to search for an embedded message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
+ public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest)
+ where TRequest : class, IProtocolMessage {
+ TRequest request;
+ if (this.TryReadFromRequest<TRequest>(httpRequest, out request)) {
+ return request;
+ } else {
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.ExpectedMessageNotReceived,
+ typeof(TRequest)));
+ }
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// </summary>
+ /// <param name="httpRequest">The request to search for an embedded message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ public IDirectedProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) {
+ IDirectedProtocolMessage requestMessage = this.ReadFromRequestInternal(httpRequest);
+ if (requestMessage != null) {
+ Logger.DebugFormat("Incoming request received: {0}", requestMessage);
+ this.VerifyMessageAfterReceiving(requestMessage);
+ }
+
+ return requestMessage;
+ }
+
+ /// <summary>
+ /// Sends a direct message to a remote party and waits for the response.
+ /// </summary>
+ /// <typeparam name="TResponse">The expected type of the message to be received.</typeparam>
+ /// <param name="requestMessage">The message to send.</param>
+ /// <returns>The remote party's response.</returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown if no message is recognized in the response
+ /// or an unexpected type of message is received.
+ /// </exception>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
+ public TResponse Request<TResponse>(IDirectedProtocolMessage requestMessage)
+ where TResponse : class, IProtocolMessage {
+ IProtocolMessage response = this.Request(requestMessage);
+ ErrorUtilities.VerifyProtocol(response != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TResponse));
+
+ var expectedResponse = response as TResponse;
+ ErrorUtilities.VerifyProtocol(expectedResponse != null, MessagingStrings.UnexpectedMessageReceived, typeof(TResponse), response.GetType());
+
+ return expectedResponse;
+ }
+
+ /// <summary>
+ /// Sends a direct message to a remote party and waits for the response.
+ /// </summary>
+ /// <param name="requestMessage">The message to send.</param>
+ /// <returns>The remote party's response. Guaranteed to never be null.</returns>
+ /// <exception cref="ProtocolException">Thrown if the response does not include a protocol message.</exception>
+ public IProtocolMessage Request(IDirectedProtocolMessage requestMessage) {
+ if (requestMessage == null) {
+ throw new ArgumentNullException("requestMessage");
+ }
+
+ this.PrepareMessageForSending(requestMessage);
+ Logger.DebugFormat("Sending request: {0}", requestMessage);
+ var responseMessage = this.RequestInternal(requestMessage);
+ ErrorUtilities.VerifyProtocol(responseMessage != null, MessagingStrings.ExpectedMessageNotReceived, typeof(IProtocolMessage).Name);
+
+ Logger.DebugFormat("Received message response: {0}", responseMessage);
+ this.VerifyMessageAfterReceiving(responseMessage);
+
+ return responseMessage;
+ }
+
+ /// <summary>
+ /// Gets the current HTTP request being processed.
+ /// </summary>
+ /// <returns>The HttpRequestInfo for the current request.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")]
+ protected internal virtual HttpRequestInfo GetRequestFromContext() {
+ if (HttpContext.Current == null) {
+ throw new InvalidOperationException(MessagingStrings.HttpContextRequired);
+ }
+
+ return new HttpRequestInfo(HttpContext.Current.Request);
+ }
+
+ /// <summary>
+ /// Fires the <see cref="Sending"/> event.
+ /// </summary>
+ /// <param name="message">The message about to be encoded and sent.</param>
+ protected virtual void OnSending(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var sending = this.Sending;
+ if (sending != null) {
+ sending(this, new ChannelEventArgs(message));
+ }
+ }
+
+ /// <summary>
+ /// Submits a direct request message to some remote party and blocks waiting for an immediately reply.
+ /// </summary>
+ /// <param name="request">The request message.</param>
+ /// <returns>The response message, or null if the response did not carry a message.</returns>
+ /// <remarks>
+ /// Typically a deriving channel will override <see cref="CreateHttpRequest"/> to customize this method's
+ /// behavior. However in non-HTTP frameworks, such as unit test mocks, it may be appropriate to override
+ /// this method to eliminate all use of an HTTP transport.
+ /// </remarks>
+ protected virtual IProtocolMessage RequestInternal(IDirectedProtocolMessage request) {
+ HttpWebRequest webRequest = this.CreateHttpRequest(request);
+ IDictionary<string, string> responseFields;
+
+ using (DirectWebResponse response = this.WebRequestHandler.GetResponse(webRequest)) {
+ if (response.ResponseStream == null) {
+ return null;
+ }
+
+ responseFields = this.ReadFromResponseInternal(response);
+ }
+
+ IDirectResponseProtocolMessage responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseFields);
+ if (responseMessage == null) {
+ return null;
+ }
+
+ var responseSerialize = MessageSerializer.Get(responseMessage.GetType());
+ responseSerialize.Deserialize(responseFields, responseMessage);
+
+ return responseMessage;
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// </summary>
+ /// <param name="request">The request to search for an embedded message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ protected virtual IDirectedProtocolMessage ReadFromRequestInternal(HttpRequestInfo request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ // Search Form data first, and if nothing is there search the QueryString
+ var fields = request.Form.ToDictionary();
+ if (fields.Count == 0) {
+ fields = request.QueryString.ToDictionary();
+ }
+
+ return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
+ }
+
+ /// <summary>
+ /// Deserializes a dictionary of values into a message.
+ /// </summary>
+ /// <param name="fields">The dictionary of values that were read from an HTTP request or response.</param>
+ /// <param name="recipient">Information about where the message was been directed. Null for direct response messages.</param>
+ /// <returns>The deserialized message, or null if no message could be recognized in the provided data.</returns>
+ protected virtual IProtocolMessage Receive(Dictionary<string, string> fields, MessageReceivingEndpoint recipient) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ IProtocolMessage message = this.MessageFactory.GetNewRequestMessage(recipient, fields);
+
+ // If there was no data, or we couldn't recognize it as a message, abort.
+ if (message == null) {
+ return null;
+ }
+
+ // We have a message! Assemble it.
+ var serializer = MessageSerializer.Get(message.GetType());
+ serializer.Deserialize(fields, message);
+
+ return message;
+ }
+
+ /// <summary>
+ /// Queues an indirect message for transmittal via the user agent.
+ /// </summary>
+ /// <param name="message">The message to send.</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ protected virtual UserAgentResponse SendIndirectMessage(IDirectedProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var serializer = MessageSerializer.Get(message.GetType());
+ var fields = serializer.Serialize(message);
+
+ // First try creating a 301 redirect, and fallback to a form POST
+ // if the message is too big.
+ UserAgentResponse response = this.Create301RedirectResponse(message, fields);
+ if (response.Headers[HttpResponseHeader.Location].Length > indirectMessageGetToPostThreshold) {
+ response = this.CreateFormPostResponse(message, fields);
+ }
+
+ return response;
+ }
+
+ /// <summary>
+ /// Encodes an HTTP response that will instruct the user agent to forward a message to
+ /// some remote third party using a 301 Redirect GET method.
+ /// </summary>
+ /// <param name="message">The message to forward.</param>
+ /// <param name="fields">The pre-serialized fields from the message.</param>
+ /// <returns>The encoded HTTP response.</returns>
+ protected virtual UserAgentResponse Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ if (message.Recipient == null) {
+ throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ UriBuilder builder = new UriBuilder(message.Recipient);
+ MessagingUtilities.AppendQueryArgs(builder, fields);
+ headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
+ Logger.DebugFormat("Redirecting to {0}", builder.Uri.AbsoluteUri);
+ UserAgentResponse response = new UserAgentResponse {
+ Status = HttpStatusCode.Redirect,
+ Headers = headers,
+ Body = null,
+ OriginalMessage = message
+ };
+
+ return response;
+ }
+
+ /// <summary>
+ /// Encodes an HTTP response that will instruct the user agent to forward a message to
+ /// some remote third party using a form POST method.
+ /// </summary>
+ /// <param name="message">The message to forward.</param>
+ /// <param name="fields">The pre-serialized fields from the message.</param>
+ /// <returns>The encoded HTTP response.</returns>
+ protected virtual UserAgentResponse CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ if (message.Recipient == null) {
+ throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ StringWriter bodyWriter = new StringWriter(CultureInfo.InvariantCulture);
+ StringBuilder hiddenFields = new StringBuilder();
+ foreach (var field in fields) {
+ hiddenFields.AppendFormat(
+ "\t<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />\r\n",
+ HttpUtility.HtmlEncode(field.Key),
+ HttpUtility.HtmlEncode(field.Value));
+ }
+ bodyWriter.WriteLine(
+ indirectMessageFormPostFormat,
+ HttpUtility.HtmlEncode(message.Recipient.AbsoluteUri),
+ hiddenFields);
+ bodyWriter.Flush();
+ UserAgentResponse response = new UserAgentResponse {
+ Status = HttpStatusCode.OK,
+ Headers = headers,
+ Body = bodyWriter.ToString(),
+ OriginalMessage = message
+ };
+
+ return response;
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be in the given HTTP response.
+ /// </summary>
+ /// <param name="response">The response that is anticipated to contain an protocol message.</param>
+ /// <returns>The deserialized message parts, if found. Null otherwise.</returns>
+ protected abstract IDictionary<string, string> ReadFromResponseInternal(DirectWebResponse response);
+
+ /// <summary>
+ /// Prepares an HTTP request that carries a given message.
+ /// </summary>
+ /// <param name="request">The message to send.</param>
+ /// <returns>The <see cref="HttpWebRequest"/> prepared to send the request.</returns>
+ /// <remarks>
+ /// This method must be overridden by a derived class, unless the <see cref="RequestInternal"/> method
+ /// is overridden and does not require this method.
+ /// </remarks>
+ protected virtual HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Queues a message for sending in the response stream where the fields
+ /// are sent in the response stream in querystring style.
+ /// </summary>
+ /// <param name="response">The message to send as a response.</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ /// <remarks>
+ /// This method implements spec V1.0 section 5.3.
+ /// </remarks>
+ protected abstract UserAgentResponse SendDirectMessageResponse(IProtocolMessage response);
+
+ /// <summary>
+ /// Prepares a message for transmit by applying signatures, nonces, etc.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <remarks>
+ /// This method should NOT be called by derived types
+ /// except when sending ONE WAY request messages.
+ /// </remarks>
+ protected void PrepareMessageForSending(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ Logger.DebugFormat("Preparing to send {0} ({1}) message.", message.GetType().Name, message.Version);
+ this.OnSending(message);
+
+ MessageProtections appliedProtection = MessageProtections.None;
+ foreach (IChannelBindingElement bindingElement in this.outgoingBindingElements) {
+ if (bindingElement.PrepareMessageForSending(message)) {
+ Logger.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
+
+ // Ensure that only one protection binding element applies to this message
+ // for each protection type.
+ ErrorUtilities.VerifyProtocol((appliedProtection & bindingElement.Protection) == 0, MessagingStrings.TooManyBindingsOfferingSameProtection, bindingElement.Protection);
+ appliedProtection |= bindingElement.Protection;
+ } else {
+ Logger.DebugFormat("Binding element {0} did not apply to message.", bindingElement.GetType().FullName);
+ }
+ }
+
+ // Ensure that the message's protection requirements have been satisfied.
+ if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
+ throw new UnprotectedMessageException(message, appliedProtection);
+ }
+
+ EnsureValidMessageParts(message);
+ message.EnsureValidMessage();
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider as the query string in a GET request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP Get request with the message parts serialized to the query string.
+ /// This method satisfies OAuth 1.0 section 5.2, item #3.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
+ if (requestMessage == null) {
+ throw new ArgumentNullException("requestMessage");
+ }
+
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ UriBuilder builder = new UriBuilder(requestMessage.Recipient);
+ MessagingUtilities.AppendQueryArgs(builder, fields);
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider as the payload of a POST request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP POST request with the message parts serialized to the POST entity
+ /// with the application/x-www-form-urlencoded content type
+ /// This method satisfies OAuth 1.0 section 5.2, item #2 and OpenID 2.0 section 4.1.2.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
+ if (requestMessage == null) {
+ throw new ArgumentNullException("requestMessage");
+ }
+
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
+ httpRequest.Method = "POST";
+ httpRequest.ContentType = "application/x-www-form-urlencoded";
+ string requestBody = MessagingUtilities.CreateQueryString(fields);
+ httpRequest.ContentLength = requestBody.Length;
+ using (TextWriter writer = this.WebRequestHandler.GetRequestStream(httpRequest)) {
+ writer.Write(requestBody);
+ }
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Verifies the integrity and applicability of an incoming message.
+ /// </summary>
+ /// <param name="message">The message just received.</param>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the message is somehow invalid.
+ /// This can be due to tampering, replay attack or expiration, among other things.
+ /// </exception>
+ protected virtual void VerifyMessageAfterReceiving(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+
+ Logger.DebugFormat("Preparing to receive {0} ({1}) message.", message.GetType().Name, message.Version);
+
+ MessageProtections appliedProtection = MessageProtections.None;
+ foreach (IChannelBindingElement bindingElement in this.incomingBindingElements) {
+ if (bindingElement.PrepareMessageForReceiving(message)) {
+ Logger.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
+
+ // Ensure that only one protection binding element applies to this message
+ // for each protection type.
+ ErrorUtilities.VerifyInternal((appliedProtection & bindingElement.Protection) == 0, MessagingStrings.TooManyBindingsOfferingSameProtection, bindingElement.Protection);
+ appliedProtection |= bindingElement.Protection;
+ } else {
+ Logger.DebugFormat("Binding element {0} did not apply to message.", bindingElement.GetType().FullName);
+ }
+ }
+
+ // Ensure that the message's protection requirements have been satisfied.
+ if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
+ throw new UnprotectedMessageException(message, appliedProtection);
+ }
+
+ // We do NOT verify that all required message parts are present here... the
+ // message deserializer did for us. It would be too late to do it here since
+ // they might look initialized by the time we have an IProtocolMessage instance.
+ message.EnsureValidMessage();
+ }
+
+ /// <summary>
+ /// Customizes the binding element order for outgoing and incoming messages.
+ /// </summary>
+ /// <param name="outgoingOrder">The outgoing order.</param>
+ /// <param name="incomingOrder">The incoming order.</param>
+ /// <remarks>
+ /// No binding elements can be added or removed from the channel using this method.
+ /// Only a customized order is allowed.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Thrown if a binding element is new or missing in one of the ordered lists.</exception>
+ protected void CustomizeBindingElementOrder(IEnumerable<IChannelBindingElement> outgoingOrder, IEnumerable<IChannelBindingElement> incomingOrder) {
+ ErrorUtilities.VerifyArgumentNotNull(outgoingOrder, "outgoingOrder");
+ ErrorUtilities.VerifyArgumentNotNull(incomingOrder, "incomingOrder");
+
+ ErrorUtilities.VerifyArgument(this.IsBindingElementOrderValid(outgoingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
+ ErrorUtilities.VerifyArgument(this.IsBindingElementOrderValid(incomingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
+
+ this.outgoingBindingElements.Clear();
+ this.outgoingBindingElements.AddRange(outgoingOrder);
+ this.incomingBindingElements.Clear();
+ this.incomingBindingElements.AddRange(incomingOrder);
+ }
+
+ /// <summary>
+ /// Verifies that all required message parts are initialized to values
+ /// prior to sending the message to a remote party.
+ /// </summary>
+ /// <param name="message">The message to verify.</param>
+ /// <exception cref="ProtocolException">
+ /// Thrown when any required message part does not have a value.
+ /// </exception>
+ private static void EnsureValidMessageParts(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+
+ MessageDictionary dictionary = new MessageDictionary(message);
+ MessageDescription description = MessageDescription.Get(message.GetType(), message.Version);
+ description.EnsureMessagePartsPassBasicValidation(dictionary);
+ }
+
+ /// <summary>
+ /// Ensures a consistent and secure set of binding elements and
+ /// sorts them as necessary for a valid sequence of operations.
+ /// </summary>
+ /// <param name="elements">The binding elements provided to the channel.</param>
+ /// <returns>The properly ordered list of elements.</returns>
+ /// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
+ private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
+ if (elements == null) {
+ return new IChannelBindingElement[0];
+ }
+ if (elements.Contains(null)) {
+ throw new ArgumentException(MessagingStrings.SequenceContainsNullElement, "elements");
+ }
+
+ // Filter the elements between the mere transforming ones and the protection ones.
+ var transformationElements = new List<IChannelBindingElement>(
+ elements.Where(element => element.Protection == MessageProtections.None));
+ var protectionElements = new List<IChannelBindingElement>(
+ elements.Where(element => element.Protection != MessageProtections.None));
+
+ bool wasLastProtectionPresent = true;
+ foreach (MessageProtections protectionKind in Enum.GetValues(typeof(MessageProtections))) {
+ if (protectionKind == MessageProtections.None) {
+ continue;
+ }
+
+ int countProtectionsOfThisKind = protectionElements.Count(element => (element.Protection & protectionKind) == protectionKind);
+
+ // Each protection binding element is backed by the presence of its dependent protection(s).
+ if (countProtectionsOfThisKind > 0 && !wasLastProtectionPresent) {
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.RequiredProtectionMissing,
+ protectionKind));
+ }
+
+ wasLastProtectionPresent = countProtectionsOfThisKind > 0;
+ }
+
+ // Put the binding elements in order so they are correctly applied to outgoing messages.
+ // Start with the transforming (non-protecting) binding elements first and preserve their original order.
+ var orderedList = new List<IChannelBindingElement>(transformationElements);
+
+ // Now sort the protection binding elements among themselves and add them to the list.
+ orderedList.AddRange(protectionElements.OrderBy(element => element.Protection, BindingElementOutgoingMessageApplicationOrder));
+ return orderedList;
+ }
+
+ /// <summary>
+ /// Puts binding elements in their correct outgoing message processing order.
+ /// </summary>
+ /// <param name="protection1">The first protection type to compare.</param>
+ /// <param name="protection2">The second protection type to compare.</param>
+ /// <returns>
+ /// -1 if <paramref name="element1"/> should be applied to an outgoing message before <paramref name="element2"/>.
+ /// 1 if <paramref name="element2"/> should be applied to an outgoing message before <paramref name="element1"/>.
+ /// 0 if it doesn't matter.
+ /// </returns>
+ private static int BindingElementOutgoingMessageApplicationOrder(MessageProtections protection1, MessageProtections protection2) {
+ Debug.Assert(protection1 != MessageProtections.None || protection2 != MessageProtections.None, "This comparison function should only be used to compare protection binding elements. Otherwise we change the order of user-defined message transformations.");
+
+ // Now put the protection ones in the right order.
+ return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
+ }
+
+ /// <summary>
+ /// Determines whether a given ordered list of binding elements includes every
+ /// binding element in this channel exactly once.
+ /// </summary>
+ /// <param name="order">The list of binding elements to test.</param>
+ /// <returns>
+ /// <c>true</c> if the given list is a valid description of a binding element ordering; otherwise, <c>false</c>.
+ /// </returns>
+ private bool IsBindingElementOrderValid(IEnumerable<IChannelBindingElement> order) {
+ ErrorUtilities.VerifyArgumentNotNull(order, "order");
+
+ // Check that the same number of binding elements are defined.
+ if (order.Count() != this.OutgoingBindingElements.Count) {
+ return false;
+ }
+
+ // Check that every binding element appears exactly once.
+ if (order.Any(el => !this.OutgoingBindingElements.Contains(el))) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IExtensionMessage.cs b/src/DotNetOpenAuth/Messaging/IExtensionMessage.cs
index e9c11e0..afbb8fe 100644
--- a/src/DotNetOpenAuth/Messaging/IExtensionMessage.cs
+++ b/src/DotNetOpenAuth/Messaging/IExtensionMessage.cs
@@ -1,13 +1,13 @@
-//-----------------------------------------------------------------------
-// <copyright file="IExtensionMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- /// <summary>
- /// An interface that extension messages must implement.
- /// </summary>
- public interface IExtensionMessage : IMessage {
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IExtensionMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ /// <summary>
+ /// An interface that extension messages must implement.
+ /// </summary>
+ public interface IExtensionMessage : IMessage {
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IMessage.cs b/src/DotNetOpenAuth/Messaging/IMessage.cs
index cd42fce..acc1224 100644
--- a/src/DotNetOpenAuth/Messaging/IMessage.cs
+++ b/src/DotNetOpenAuth/Messaging/IMessage.cs
@@ -1,44 +1,44 @@
-//-----------------------------------------------------------------------
-// <copyright file="IMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- /// <summary>
- /// The interface that classes must implement to be serialized/deserialized
- /// as protocol or extension messages.
- /// </summary>
- public interface IMessage {
- /// <summary>
- /// Gets the version of the protocol or extension this message is prepared to implement.
- /// </summary>
- Version Version { get; }
-
- /// <summary>
- /// Gets the extra, non-standard Protocol parameters included in the message.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IDictionary<string, string> ExtraData { get; }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- void EnsureValidMessage();
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as protocol or extension messages.
+ /// </summary>
+ public interface IMessage {
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ Version Version { get; }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IDictionary<string, string> ExtraData { get; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void EnsureValidMessage();
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs b/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs
index 0bc2ac1..cf43360 100644
--- a/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs
+++ b/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs
@@ -1,27 +1,27 @@
-//-----------------------------------------------------------------------
-// <copyright file="IProtocolMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- /// <summary>
- /// The interface that classes must implement to be serialized/deserialized
- /// as protocol messages.
- /// </summary>
- public interface IProtocolMessage : IMessage {
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- MessageProtections RequiredProtection { get; }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- MessageTransport Transport { get; }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as protocol messages.
+ /// </summary>
+ public interface IProtocolMessage : IMessage {
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ MessageProtections RequiredProtection { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport Transport { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs b/src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs
index 7627dd6..316d701 100644
--- a/src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs
+++ b/src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs
@@ -1,25 +1,25 @@
-//-----------------------------------------------------------------------
-// <copyright file="IProtocolMessageWithExtensions.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
-
- /// <summary>
- /// A protocol message that supports adding extensions to the payload for transmission.
- /// </summary>
- public interface IProtocolMessageWithExtensions : IProtocolMessage {
- /// <summary>
- /// Gets the list of extensions that are included with this message.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IList<IExtensionMessage> Extensions { get; }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessageWithExtensions.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A protocol message that supports adding extensions to the payload for transmission.
+ /// </summary>
+ public interface IProtocolMessageWithExtensions : IProtocolMessage {
+ /// <summary>
+ /// Gets the list of extensions that are included with this message.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IList<IExtensionMessage> Extensions { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
index 4e9a6f8..f9c423d 100644
--- a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
@@ -1,100 +1,100 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessageSerializer.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.Reflection;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OAuth.ChannelElements;
-
- /// <summary>
- /// Serializes/deserializes OAuth messages for/from transit.
- /// </summary>
- internal class MessageSerializer {
- /// <summary>
- /// The specific <see cref="IProtocolMessage"/>-derived type
- /// that will be serialized and deserialized using this class.
- /// </summary>
- private readonly Type messageType;
-
- /// <summary>
- /// Initializes a new instance of the MessageSerializer class.
- /// </summary>
- /// <param name="messageType">The specific <see cref="IProtocolMessage"/>-derived type
- /// that will be serialized and deserialized using this class.</param>
- private MessageSerializer(Type messageType) {
- Debug.Assert(messageType != null, "messageType == null");
-
- if (!typeof(IProtocolMessage).IsAssignableFrom(messageType)) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(IProtocolMessage).FullName,
- messageType.FullName),
- "messageType");
- }
-
- this.messageType = messageType;
- }
-
- /// <summary>
- /// Creates or reuses a message serializer for a given message type.
- /// </summary>
- /// <param name="messageType">The type of message that will be serialized/deserialized.</param>
- /// <returns>A message serializer for the given message type.</returns>
- internal static MessageSerializer Get(Type messageType) {
- if (messageType == null) {
- throw new ArgumentNullException("messageType");
- }
-
- return new MessageSerializer(messageType);
- }
-
- /// <summary>
- /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message.
- /// </summary>
- /// <param name="message">The message to be serialized.</param>
- /// <returns>The dictionary of values to send for the message.</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")]
- internal IDictionary<string, string> Serialize(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- var result = new Reflection.MessageDictionary(message);
-
- return result;
- }
-
- /// <summary>
- /// Reads name=value pairs into an OAuth message.
- /// </summary>
- /// <param name="fields">The name=value pairs that were read in from the transport.</param>
- /// <param name="message">The message to deserialize into.</param>
- /// <exception cref="ProtocolException">Thrown when protocol rules are broken by the incoming message.</exception>
- internal void Deserialize(IDictionary<string, string> fields, IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
-
- // Before we deserialize the message, make sure all the required parts are present.
- MessageDescription.Get(this.messageType, message.Version).EnsureMessagePartsPassBasicValidation(fields);
-
- try {
- foreach (var pair in fields) {
- IDictionary<string, string> dictionary = new MessageDictionary(message);
- dictionary[pair.Key] = pair.Value;
- }
- } catch (ArgumentException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorDeserializingMessage, this.messageType.Name);
- }
- message.EnsureValidMessage();
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessageSerializer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Globalization;
+ using System.Reflection;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+
+ /// <summary>
+ /// Serializes/deserializes OAuth messages for/from transit.
+ /// </summary>
+ internal class MessageSerializer {
+ /// <summary>
+ /// The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.
+ /// </summary>
+ private readonly Type messageType;
+
+ /// <summary>
+ /// Initializes a new instance of the MessageSerializer class.
+ /// </summary>
+ /// <param name="messageType">The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.</param>
+ private MessageSerializer(Type messageType) {
+ Debug.Assert(messageType != null, "messageType == null");
+
+ if (!typeof(IProtocolMessage).IsAssignableFrom(messageType)) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedType,
+ typeof(IProtocolMessage).FullName,
+ messageType.FullName),
+ "messageType");
+ }
+
+ this.messageType = messageType;
+ }
+
+ /// <summary>
+ /// Creates or reuses a message serializer for a given message type.
+ /// </summary>
+ /// <param name="messageType">The type of message that will be serialized/deserialized.</param>
+ /// <returns>A message serializer for the given message type.</returns>
+ internal static MessageSerializer Get(Type messageType) {
+ if (messageType == null) {
+ throw new ArgumentNullException("messageType");
+ }
+
+ return new MessageSerializer(messageType);
+ }
+
+ /// <summary>
+ /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message.
+ /// </summary>
+ /// <param name="message">The message to be serialized.</param>
+ /// <returns>The dictionary of values to send for the message.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")]
+ internal IDictionary<string, string> Serialize(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var result = new Reflection.MessageDictionary(message);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Reads name=value pairs into an OAuth message.
+ /// </summary>
+ /// <param name="fields">The name=value pairs that were read in from the transport.</param>
+ /// <param name="message">The message to deserialize into.</param>
+ /// <exception cref="ProtocolException">Thrown when protocol rules are broken by the incoming message.</exception>
+ internal void Deserialize(IDictionary<string, string> fields, IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ // Before we deserialize the message, make sure all the required parts are present.
+ MessageDescription.Get(this.messageType, message.Version).EnsureMessagePartsPassBasicValidation(fields);
+
+ try {
+ foreach (var pair in fields) {
+ IDictionary<string, string> dictionary = new MessageDictionary(message);
+ dictionary[pair.Key] = pair.Value;
+ }
+ } catch (ArgumentException ex) {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorDeserializingMessage, this.messageType.Name);
+ }
+ message.EnsureValidMessage();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/ProtocolException.cs b/src/DotNetOpenAuth/Messaging/ProtocolException.cs
index 22daa24..7551e03 100644
--- a/src/DotNetOpenAuth/Messaging/ProtocolException.cs
+++ b/src/DotNetOpenAuth/Messaging/ProtocolException.cs
@@ -1,272 +1,272 @@
-//-----------------------------------------------------------------------
-// <copyright file="ProtocolException.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Security.Permissions;
-
- /// <summary>
- /// An exception to represent errors in the local or remote implementation of the protocol.
- /// </summary>
- [Serializable]
- public class ProtocolException : Exception, IDirectedProtocolMessage {
- /// <summary>
- /// The request message being processed when this exception was generated, if any.
- /// </summary>
- private IProtocolMessage inResponseTo;
-
- /// <summary>
- /// The indirect message receiver this exception should be delivered to, if any.
- /// </summary>
- private Uri recipient;
-
- /// <summary>
- /// A cache for extra name/value pairs tacked on as data when this exception is sent as a message.
- /// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class.
- /// </summary>
- public ProtocolException() { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class.
- /// </summary>
- /// <param name="message">A message describing the specific error the occurred or was detected.</param>
- public ProtocolException(string message) : base(message) { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class.
- /// </summary>
- /// <param name="message">A message describing the specific error the occurred or was detected.</param>
- /// <param name="inner">The inner exception to include.</param>
- public ProtocolException(string message, Exception inner) : base(message, inner) { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class
- /// such that it can be sent as a protocol message response to a remote caller.
- /// </summary>
- /// <param name="message">The human-readable exception message.</param>
- /// <param name="faultedMessage">The message that was the cause of the exception. May not be null.</param>
- internal ProtocolException(string message, IProtocolMessage faultedMessage)
- : base(message) {
- if (faultedMessage == null) {
- throw new ArgumentNullException("faultedMessage");
- }
-
- this.FaultedMessage = faultedMessage;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class
- /// such that it can be sent as a protocol message response to a remote caller.
- /// </summary>
- /// <param name="message">The human-readable exception message.</param>
- /// <param name="inResponseTo">
- /// If <paramref name="message"/> is a response to an incoming message, this is the incoming message.
- /// This is useful for error scenarios in deciding just how to send the response message.
- /// May be null.
- /// </param>
- /// <param name="remoteIndirectReceiver">
- /// In the case of exceptions that will be sent as indirect messages to the original calling
- /// remote party, this is the URI of that remote site's receiver.
- /// May be null only if the <paramref name="inResponseTo"/> message is a direct request.
- /// </param>
- internal ProtocolException(string message, IProtocolMessage inResponseTo, Uri remoteIndirectReceiver)
- : this(message) {
- if (inResponseTo == null) {
- throw new ArgumentNullException("inResponseTo");
- }
- this.inResponseTo = inResponseTo;
- this.FaultedMessage = inResponseTo;
-
- if (remoteIndirectReceiver == null && inResponseTo.Transport != MessageTransport.Direct) {
- // throw an exception, with ourselves as the inner exception (as fully initialized as we can be).
- throw new ArgumentNullException("remoteIndirectReceiver");
- }
- this.recipient = remoteIndirectReceiver;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class.
- /// </summary>
- /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
- /// that holds the serialized object data about the exception being thrown.</param>
- /// <param name="context">The System.Runtime.Serialization.StreamingContext
- /// that contains contextual information about the source or destination.</param>
- protected ProtocolException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context)
- : base(info, context) {
- throw new NotImplementedException();
- }
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- Version IMessage.Version {
- get { return this.Version; }
- }
-
- /// <summary>
- /// Gets the level of protection this exception requires when transmitted as a message.
- /// </summary>
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return RequiredProtection; }
- }
-
- /// <summary>
- /// Gets whether this is a direct or indirect message.
- /// </summary>
- MessageTransport IProtocolMessage.Transport {
- get { return this.Transport; }
- }
-
- #endregion
-
- #region IDirectedProtocolMessage Members
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- /// <remarks>
- /// This exception may be delivered as an indirect message or a direct response. This property
- /// only applies to the former. The latter will never query this property.
- /// </remarks>
- HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
- get { return HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest; }
- }
-
- /// <summary>
- /// Gets the URL of the intended receiver of this message.
- /// </summary>
- /// <remarks>
- /// This property should only be called when the error is being sent as an indirect response.
- /// </remarks>
- Uri IDirectedProtocolMessage.Recipient {
- get { return this.Recipient; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- IDictionary<string, string> IMessage.ExtraData {
- get { return this.ExtraData; }
- }
-
- /// <summary>
- /// Gets the message that caused the exception.
- /// </summary>
- internal IProtocolMessage FaultedMessage {
- get;
- private set;
- }
-
- /// <summary>
- /// Gets the level of protection this exception requires when transmitted as a message.
- /// </summary>
- protected static MessageProtections RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- protected HttpDeliveryMethods HttpMethods {
- get { return ((IDirectedProtocolMessage)this).HttpMethods; }
- }
-
- /// <summary>
- /// Gets the URL of the intended receiver of this message.
- /// </summary>
- /// <remarks>
- /// This property should only be called when the error is being sent as an indirect response.
- /// </remarks>
- protected Uri Recipient {
- get {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- return this.recipient;
- }
- }
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- protected Version Version {
- get {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- return this.inResponseTo.Version;
- }
- }
-
- /// <summary>
- /// Gets whether this is a direct or indirect message.
- /// </summary>
- protected MessageTransport Transport {
- get {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- return this.inResponseTo.Transport;
- }
- }
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- protected IDictionary<string, string> ExtraData {
- get { return this.extraData; }
- }
-
- /// <summary>
- /// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with information about the exception.
- /// </summary>
- /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
- /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
- /// <exception cref="T:System.ArgumentNullException">
- /// The <paramref name="info"/> parameter is a null reference (Nothing in Visual Basic).
- /// </exception>
- /// <PermissionSet>
- /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/>
- /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter"/>
- /// </PermissionSet>
- [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
- public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) {
- base.GetObjectData(info, context);
- throw new NotImplementedException();
- }
-
- #region IProtocolMessage Methods
-
- /// <summary>
- /// See <see cref="IMessage.EnsureValidMessage"/>.
- /// </summary>
- void IMessage.EnsureValidMessage() {
- this.EnsureValidMessage();
- }
-
- /// <summary>
- /// See <see cref="IMessage.EnsureValidMessage"/>.
- /// </summary>
- protected virtual void EnsureValidMessage() {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ProtocolException.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Security.Permissions;
+
+ /// <summary>
+ /// An exception to represent errors in the local or remote implementation of the protocol.
+ /// </summary>
+ [Serializable]
+ public class ProtocolException : Exception, IDirectedProtocolMessage {
+ /// <summary>
+ /// The request message being processed when this exception was generated, if any.
+ /// </summary>
+ private IProtocolMessage inResponseTo;
+
+ /// <summary>
+ /// The indirect message receiver this exception should be delivered to, if any.
+ /// </summary>
+ private Uri recipient;
+
+ /// <summary>
+ /// A cache for extra name/value pairs tacked on as data when this exception is sent as a message.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class.
+ /// </summary>
+ public ProtocolException() { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class.
+ /// </summary>
+ /// <param name="message">A message describing the specific error the occurred or was detected.</param>
+ public ProtocolException(string message) : base(message) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class.
+ /// </summary>
+ /// <param name="message">A message describing the specific error the occurred or was detected.</param>
+ /// <param name="inner">The inner exception to include.</param>
+ public ProtocolException(string message, Exception inner) : base(message, inner) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class
+ /// such that it can be sent as a protocol message response to a remote caller.
+ /// </summary>
+ /// <param name="message">The human-readable exception message.</param>
+ /// <param name="faultedMessage">The message that was the cause of the exception. May not be null.</param>
+ internal ProtocolException(string message, IProtocolMessage faultedMessage)
+ : base(message) {
+ if (faultedMessage == null) {
+ throw new ArgumentNullException("faultedMessage");
+ }
+
+ this.FaultedMessage = faultedMessage;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class
+ /// such that it can be sent as a protocol message response to a remote caller.
+ /// </summary>
+ /// <param name="message">The human-readable exception message.</param>
+ /// <param name="inResponseTo">
+ /// If <paramref name="message"/> is a response to an incoming message, this is the incoming message.
+ /// This is useful for error scenarios in deciding just how to send the response message.
+ /// May be null.
+ /// </param>
+ /// <param name="remoteIndirectReceiver">
+ /// In the case of exceptions that will be sent as indirect messages to the original calling
+ /// remote party, this is the URI of that remote site's receiver.
+ /// May be null only if the <paramref name="inResponseTo"/> message is a direct request.
+ /// </param>
+ internal ProtocolException(string message, IProtocolMessage inResponseTo, Uri remoteIndirectReceiver)
+ : this(message) {
+ if (inResponseTo == null) {
+ throw new ArgumentNullException("inResponseTo");
+ }
+ this.inResponseTo = inResponseTo;
+ this.FaultedMessage = inResponseTo;
+
+ if (remoteIndirectReceiver == null && inResponseTo.Transport != MessageTransport.Direct) {
+ // throw an exception, with ourselves as the inner exception (as fully initialized as we can be).
+ throw new ArgumentNullException("remoteIndirectReceiver");
+ }
+ this.recipient = remoteIndirectReceiver;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class.
+ /// </summary>
+ /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
+ /// that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The System.Runtime.Serialization.StreamingContext
+ /// that contains contextual information about the source or destination.</param>
+ protected ProtocolException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) {
+ throw new NotImplementedException();
+ }
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ Version IMessage.Version {
+ get { return this.Version; }
+ }
+
+ /// <summary>
+ /// Gets the level of protection this exception requires when transmitted as a message.
+ /// </summary>
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return RequiredProtection; }
+ }
+
+ /// <summary>
+ /// Gets whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport IProtocolMessage.Transport {
+ get { return this.Transport; }
+ }
+
+ #endregion
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ /// <remarks>
+ /// This exception may be delivered as an indirect message or a direct response. This property
+ /// only applies to the former. The latter will never query this property.
+ /// </remarks>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get { return HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest; }
+ }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ /// <remarks>
+ /// This property should only be called when the error is being sent as an indirect response.
+ /// </remarks>
+ Uri IDirectedProtocolMessage.Recipient {
+ get { return this.Recipient; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.ExtraData; }
+ }
+
+ /// <summary>
+ /// Gets the message that caused the exception.
+ /// </summary>
+ internal IProtocolMessage FaultedMessage {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Gets the level of protection this exception requires when transmitted as a message.
+ /// </summary>
+ protected static MessageProtections RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ protected HttpDeliveryMethods HttpMethods {
+ get { return ((IDirectedProtocolMessage)this).HttpMethods; }
+ }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ /// <remarks>
+ /// This property should only be called when the error is being sent as an indirect response.
+ /// </remarks>
+ protected Uri Recipient {
+ get {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ return this.recipient;
+ }
+ }
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ protected Version Version {
+ get {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ return this.inResponseTo.Version;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether this is a direct or indirect message.
+ /// </summary>
+ protected MessageTransport Transport {
+ get {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ return this.inResponseTo.Transport;
+ }
+ }
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ protected IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with information about the exception.
+ /// </summary>
+ /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="info"/> parameter is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ /// <PermissionSet>
+ /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/>
+ /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter"/>
+ /// </PermissionSet>
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
+ public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) {
+ base.GetObjectData(info, context);
+ throw new NotImplementedException();
+ }
+
+ #region IProtocolMessage Methods
+
+ /// <summary>
+ /// See <see cref="IMessage.EnsureValidMessage"/>.
+ /// </summary>
+ void IMessage.EnsureValidMessage() {
+ this.EnsureValidMessage();
+ }
+
+ /// <summary>
+ /// See <see cref="IMessage.EnsureValidMessage"/>.
+ /// </summary>
+ protected virtual void EnsureValidMessage() {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index e75d56a..8537b76 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -1,321 +1,321 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessageDictionary.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Reflection {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
-
- /// <summary>
- /// Wraps an <see cref="IMessage"/> instance in a dictionary that
- /// provides access to both well-defined message properties and "extra"
- /// name/value pairs that have no properties associated with them.
- /// </summary>
- internal class MessageDictionary : IDictionary<string, string> {
- /// <summary>
- /// The <see cref="IMessage"/> instance manipulated by this dictionary.
- /// </summary>
- private IMessage message;
-
- /// <summary>
- /// The <see cref="MessageDescription"/> instance that describes the message type.
- /// </summary>
- private MessageDescription description;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageDictionary"/> class.
- /// </summary>
- /// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
- internal MessageDictionary(IMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- this.message = message;
- this.description = MessageDescription.Get(message.GetType(), message.Version);
- }
-
- #region ICollection<KeyValuePair<string,string>> Properties
-
- /// <summary>
- /// Gets the number of explicitly set values in the message.
- /// </summary>
- public int Count {
- get { return this.Keys.Count; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message is read only.
- /// </summary>
- bool ICollection<KeyValuePair<string, string>>.IsReadOnly {
- get { return false; }
- }
-
- #endregion
-
- #region IDictionary<string,string> Properties
-
- /// <summary>
- /// Gets all the keys that have values associated with them.
- /// </summary>
- public ICollection<string> Keys {
- get {
- List<string> keys = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
- keys.AddRange(this.DeclaredKeys);
- keys.AddRange(this.AdditionalKeys);
- return keys.AsReadOnly();
- }
- }
-
- /// <summary>
- /// Gets the set of official OAuth keys that have non-null values associated with them.
- /// </summary>
- public ICollection<string> DeclaredKeys {
- get {
- List<string> keys = new List<string>(this.description.Mapping.Count);
- foreach (var pair in this.description.Mapping) {
- // Don't include keys with null values, but default values for structs is ok
- if (pair.Value.GetValue(this.message) != null) {
- keys.Add(pair.Key);
- }
- }
-
- return keys.AsReadOnly();
- }
- }
-
- /// <summary>
- /// Gets the keys that are in the message but not declared as official OAuth properties.
- /// </summary>
- public ICollection<string> AdditionalKeys {
- get { return this.message.ExtraData.Keys; }
- }
-
- /// <summary>
- /// Gets all the values.
- /// </summary>
- public ICollection<string> Values {
- get {
- List<string> values = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
- foreach (MessagePart part in this.description.Mapping.Values) {
- if (part.GetValue(this.message) != null) {
- values.Add(part.GetValue(this.message));
- }
- }
-
- foreach (string value in this.message.ExtraData.Values) {
- Debug.Assert(value != null, "Null values should never be allowed in the extra data dictionary.");
- values.Add(value);
- }
-
- return values.AsReadOnly();
- }
- }
-
- /// <summary>
- /// Gets or sets a value for some named value.
- /// </summary>
- /// <param name="key">The serialized form of a name for the value to read or write.</param>
- /// <returns>The named value.</returns>
- /// <remarks>
- /// If the key matches a declared property or field on the message type,
- /// that type member is set. Otherwise the key/value is stored in a
- /// dictionary for extra (weakly typed) strings.
- /// </remarks>
- /// <exception cref="ArgumentException">Thrown when setting a value that is not allowed for a given <paramref name="key"/>.</exception>
- public string this[string key] {
- get {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- // Never throw KeyNotFoundException for declared properties.
- return part.GetValue(this.message);
- } else {
- return this.message.ExtraData[key];
- }
- }
-
- set {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- part.SetValue(this.message, value);
- } else {
- if (value == null) {
- this.message.ExtraData.Remove(key);
- } else {
- this.message.ExtraData[key] = value;
- }
- }
- }
- }
-
- #endregion
-
- #region IDictionary<string,string> Methods
-
- /// <summary>
- /// Adds a named value to the message.
- /// </summary>
- /// <param name="key">The serialized form of the name whose value is being set.</param>
- /// <param name="value">The serialized form of the value.</param>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="key"/> already has a set value in this message.
- /// </exception>
- /// <exception cref="ArgumentNullException">
- /// Thrown if <paramref name="value"/> is null.
- /// </exception>
- public void Add(string key, string value) {
- if (value == null) {
- throw new ArgumentNullException("value");
- }
-
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- if (part.IsNondefaultValueSet(this.message)) {
- throw new ArgumentException(MessagingStrings.KeyAlreadyExists);
- }
- part.SetValue(this.message, value);
- } else {
- this.message.ExtraData.Add(key, value);
- }
- }
-
- /// <summary>
- /// Checks whether some named parameter has a value set in the message.
- /// </summary>
- /// <param name="key">The serialized form of the message part's name.</param>
- /// <returns>True if the parameter by the given name has a set value. False otherwise.</returns>
- public bool ContainsKey(string key) {
- return this.message.ExtraData.ContainsKey(key) ||
- (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
- }
-
- /// <summary>
- /// Removes a name and value from the message given its name.
- /// </summary>
- /// <param name="key">The serialized form of the name to remove.</param>
- /// <returns>True if a message part by the given name was found and removed. False otherwise.</returns>
- public bool Remove(string key) {
- if (this.message.ExtraData.Remove(key)) {
- return true;
- } else {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- if (part.GetValue(this.message) != null) {
- part.SetValue(this.message, null);
- return true;
- }
- }
- return false;
- }
- }
-
- /// <summary>
- /// Gets some named value if the key has a value.
- /// </summary>
- /// <param name="key">The name (in serialized form) of the value being sought.</param>
- /// <param name="value">The variable where the value will be set.</param>
- /// <returns>True if the key was found and <paramref name="value"/> was set. False otherwise.</returns>
- public bool TryGetValue(string key, out string value) {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- value = part.GetValue(this.message);
- return true;
- }
- return this.message.ExtraData.TryGetValue(key, out value);
- }
-
- #endregion
-
- #region ICollection<KeyValuePair<string,string>> Methods
-
- /// <summary>
- /// Sets a named value in the message.
- /// </summary>
- /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
- public void Add(KeyValuePair<string, string> item) {
- this.Add(item.Key, item.Value);
- }
-
- /// <summary>
- /// Removes all values in the message.
- /// </summary>
- public void Clear() {
- foreach (string key in this.Keys) {
- this.Remove(key);
- }
- }
-
- /// <summary>
- /// Checks whether a named value has been set on the message.
- /// </summary>
- /// <param name="item">The name/value pair.</param>
- /// <returns>True if the key exists and has the given value. False otherwise.</returns>
- public bool Contains(KeyValuePair<string, string> item) {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(item.Key, out part)) {
- return string.Equals(part.GetValue(this.message), item.Value, StringComparison.Ordinal);
- } else {
- return this.message.ExtraData.Contains(item);
- }
- }
-
- /// <summary>
- /// Copies all the serializable data from the message to a key/value array.
- /// </summary>
- /// <param name="array">The array to copy to.</param>
- /// <param name="arrayIndex">The index in the <paramref name="array"/> to begin copying to.</param>
- void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) {
- foreach (var pair in (IDictionary<string, string>)this) {
- array[arrayIndex++] = pair;
- }
- }
-
- /// <summary>
- /// Removes a named value from the message if it exists.
- /// </summary>
- /// <param name="item">The serialized form of the name and value to remove.</param>
- /// <returns>True if the name/value was found and removed. False otherwise.</returns>
- public bool Remove(KeyValuePair<string, string> item) {
- // We use contains because that checks that the value is equal as well.
- if (((ICollection<KeyValuePair<string, string>>)this).Contains(item)) {
- ((IDictionary<string, string>)this).Remove(item.Key);
- return true;
- }
- return false;
- }
-
- #endregion
-
- #region IEnumerable<KeyValuePair<string,string>> Members
-
- /// <summary>
- /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
- /// for all the key/value pairs that are set in the message.
- /// </summary>
- /// <returns>The enumerator that can generate the name/value pairs.</returns>
- public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
- foreach (string key in this.Keys) {
- yield return new KeyValuePair<string, string>(key, this[key]);
- }
- }
-
- #endregion
-
- #region IEnumerable Members
-
- /// <summary>
- /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
- /// for all the key/value pairs that are set in the message.
- /// </summary>
- /// <returns>The enumerator that can generate the name/value pairs.</returns>
- IEnumerator System.Collections.IEnumerable.GetEnumerator() {
- return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator();
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessageDictionary.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Reflection {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+
+ /// <summary>
+ /// Wraps an <see cref="IMessage"/> instance in a dictionary that
+ /// provides access to both well-defined message properties and "extra"
+ /// name/value pairs that have no properties associated with them.
+ /// </summary>
+ internal class MessageDictionary : IDictionary<string, string> {
+ /// <summary>
+ /// The <see cref="IMessage"/> instance manipulated by this dictionary.
+ /// </summary>
+ private IMessage message;
+
+ /// <summary>
+ /// The <see cref="MessageDescription"/> instance that describes the message type.
+ /// </summary>
+ private MessageDescription description;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageDictionary"/> class.
+ /// </summary>
+ /// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
+ internal MessageDictionary(IMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ this.message = message;
+ this.description = MessageDescription.Get(message.GetType(), message.Version);
+ }
+
+ #region ICollection<KeyValuePair<string,string>> Properties
+
+ /// <summary>
+ /// Gets the number of explicitly set values in the message.
+ /// </summary>
+ public int Count {
+ get { return this.Keys.Count; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message is read only.
+ /// </summary>
+ bool ICollection<KeyValuePair<string, string>>.IsReadOnly {
+ get { return false; }
+ }
+
+ #endregion
+
+ #region IDictionary<string,string> Properties
+
+ /// <summary>
+ /// Gets all the keys that have values associated with them.
+ /// </summary>
+ public ICollection<string> Keys {
+ get {
+ List<string> keys = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
+ keys.AddRange(this.DeclaredKeys);
+ keys.AddRange(this.AdditionalKeys);
+ return keys.AsReadOnly();
+ }
+ }
+
+ /// <summary>
+ /// Gets the set of official OAuth keys that have non-null values associated with them.
+ /// </summary>
+ public ICollection<string> DeclaredKeys {
+ get {
+ List<string> keys = new List<string>(this.description.Mapping.Count);
+ foreach (var pair in this.description.Mapping) {
+ // Don't include keys with null values, but default values for structs is ok
+ if (pair.Value.GetValue(this.message) != null) {
+ keys.Add(pair.Key);
+ }
+ }
+
+ return keys.AsReadOnly();
+ }
+ }
+
+ /// <summary>
+ /// Gets the keys that are in the message but not declared as official OAuth properties.
+ /// </summary>
+ public ICollection<string> AdditionalKeys {
+ get { return this.message.ExtraData.Keys; }
+ }
+
+ /// <summary>
+ /// Gets all the values.
+ /// </summary>
+ public ICollection<string> Values {
+ get {
+ List<string> values = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
+ foreach (MessagePart part in this.description.Mapping.Values) {
+ if (part.GetValue(this.message) != null) {
+ values.Add(part.GetValue(this.message));
+ }
+ }
+
+ foreach (string value in this.message.ExtraData.Values) {
+ Debug.Assert(value != null, "Null values should never be allowed in the extra data dictionary.");
+ values.Add(value);
+ }
+
+ return values.AsReadOnly();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value for some named value.
+ /// </summary>
+ /// <param name="key">The serialized form of a name for the value to read or write.</param>
+ /// <returns>The named value.</returns>
+ /// <remarks>
+ /// If the key matches a declared property or field on the message type,
+ /// that type member is set. Otherwise the key/value is stored in a
+ /// dictionary for extra (weakly typed) strings.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Thrown when setting a value that is not allowed for a given <paramref name="key"/>.</exception>
+ public string this[string key] {
+ get {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ // Never throw KeyNotFoundException for declared properties.
+ return part.GetValue(this.message);
+ } else {
+ return this.message.ExtraData[key];
+ }
+ }
+
+ set {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ part.SetValue(this.message, value);
+ } else {
+ if (value == null) {
+ this.message.ExtraData.Remove(key);
+ } else {
+ this.message.ExtraData[key] = value;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region IDictionary<string,string> Methods
+
+ /// <summary>
+ /// Adds a named value to the message.
+ /// </summary>
+ /// <param name="key">The serialized form of the name whose value is being set.</param>
+ /// <param name="value">The serialized form of the value.</param>
+ /// <exception cref="ArgumentException">
+ /// Thrown if <paramref name="key"/> already has a set value in this message.
+ /// </exception>
+ /// <exception cref="ArgumentNullException">
+ /// Thrown if <paramref name="value"/> is null.
+ /// </exception>
+ public void Add(string key, string value) {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ if (part.IsNondefaultValueSet(this.message)) {
+ throw new ArgumentException(MessagingStrings.KeyAlreadyExists);
+ }
+ part.SetValue(this.message, value);
+ } else {
+ this.message.ExtraData.Add(key, value);
+ }
+ }
+
+ /// <summary>
+ /// Checks whether some named parameter has a value set in the message.
+ /// </summary>
+ /// <param name="key">The serialized form of the message part's name.</param>
+ /// <returns>True if the parameter by the given name has a set value. False otherwise.</returns>
+ public bool ContainsKey(string key) {
+ return this.message.ExtraData.ContainsKey(key) ||
+ (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
+ }
+
+ /// <summary>
+ /// Removes a name and value from the message given its name.
+ /// </summary>
+ /// <param name="key">The serialized form of the name to remove.</param>
+ /// <returns>True if a message part by the given name was found and removed. False otherwise.</returns>
+ public bool Remove(string key) {
+ if (this.message.ExtraData.Remove(key)) {
+ return true;
+ } else {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ if (part.GetValue(this.message) != null) {
+ part.SetValue(this.message, null);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets some named value if the key has a value.
+ /// </summary>
+ /// <param name="key">The name (in serialized form) of the value being sought.</param>
+ /// <param name="value">The variable where the value will be set.</param>
+ /// <returns>True if the key was found and <paramref name="value"/> was set. False otherwise.</returns>
+ public bool TryGetValue(string key, out string value) {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ value = part.GetValue(this.message);
+ return true;
+ }
+ return this.message.ExtraData.TryGetValue(key, out value);
+ }
+
+ #endregion
+
+ #region ICollection<KeyValuePair<string,string>> Methods
+
+ /// <summary>
+ /// Sets a named value in the message.
+ /// </summary>
+ /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
+ public void Add(KeyValuePair<string, string> item) {
+ this.Add(item.Key, item.Value);
+ }
+
+ /// <summary>
+ /// Removes all values in the message.
+ /// </summary>
+ public void Clear() {
+ foreach (string key in this.Keys) {
+ this.Remove(key);
+ }
+ }
+
+ /// <summary>
+ /// Checks whether a named value has been set on the message.
+ /// </summary>
+ /// <param name="item">The name/value pair.</param>
+ /// <returns>True if the key exists and has the given value. False otherwise.</returns>
+ public bool Contains(KeyValuePair<string, string> item) {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(item.Key, out part)) {
+ return string.Equals(part.GetValue(this.message), item.Value, StringComparison.Ordinal);
+ } else {
+ return this.message.ExtraData.Contains(item);
+ }
+ }
+
+ /// <summary>
+ /// Copies all the serializable data from the message to a key/value array.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="arrayIndex">The index in the <paramref name="array"/> to begin copying to.</param>
+ void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) {
+ foreach (var pair in (IDictionary<string, string>)this) {
+ array[arrayIndex++] = pair;
+ }
+ }
+
+ /// <summary>
+ /// Removes a named value from the message if it exists.
+ /// </summary>
+ /// <param name="item">The serialized form of the name and value to remove.</param>
+ /// <returns>True if the name/value was found and removed. False otherwise.</returns>
+ public bool Remove(KeyValuePair<string, string> item) {
+ // We use contains because that checks that the value is equal as well.
+ if (((ICollection<KeyValuePair<string, string>>)this).Contains(item)) {
+ ((IDictionary<string, string>)this).Remove(item.Key);
+ return true;
+ }
+ return false;
+ }
+
+ #endregion
+
+ #region IEnumerable<KeyValuePair<string,string>> Members
+
+ /// <summary>
+ /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
+ /// for all the key/value pairs that are set in the message.
+ /// </summary>
+ /// <returns>The enumerator that can generate the name/value pairs.</returns>
+ public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
+ foreach (string key in this.Keys) {
+ yield return new KeyValuePair<string, string>(key, this[key]);
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ /// <summary>
+ /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
+ /// for all the key/value pairs that are set in the message.
+ /// </summary>
+ /// <returns>The enumerator that can generate the name/value pairs.</returns>
+ IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 4c47ebf..6cd2c02 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -1,328 +1,328 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessagePart.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Reflection {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.Net.Security;
- using System.Reflection;
- using System.Xml;
- using DotNetOpenAuth.OpenId;
-
- /// <summary>
- /// Describes an individual member of a message and assists in its serialization.
- /// </summary>
- internal class MessagePart {
- /// <summary>
- /// A map of converters that help serialize custom objects to string values and back again.
- /// </summary>
- private static readonly Dictionary<Type, ValueMapping> converters = new Dictionary<Type, ValueMapping>();
-
- /// <summary>
- /// A map of instantiated custom encoders used to encode/decode message parts.
- /// </summary>
- private static readonly Dictionary<Type, IMessagePartEncoder> encoders = new Dictionary<Type, IMessagePartEncoder>();
-
- /// <summary>
- /// The string-object conversion routines to use for this individual message part.
- /// </summary>
- private ValueMapping converter;
-
- /// <summary>
- /// The property that this message part is associated with, if aplicable.
- /// </summary>
- private PropertyInfo property;
-
- /// <summary>
- /// The field that this message part is associated with, if aplicable.
- /// </summary>
- private FieldInfo field;
-
- /// <summary>
- /// The type of the message part. (Not the type of the message itself).
- /// </summary>
- private Type memberDeclaredType;
-
- /// <summary>
- /// The default (uninitialized) value of the member inherent in its type.
- /// </summary>
- private object defaultMemberValue;
-
- /// <summary>
- /// Initializes static members of the <see cref="MessagePart"/> class.
- /// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
- static MessagePart() {
- Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
- Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
- Map<byte[]>(bytes => Convert.ToBase64String(bytes), str => Convert.FromBase64String(str));
- Map<Realm>(realm => realm.ToString(), str => new Realm(str));
- Map<Identifier>(id => id.ToString(), str => Identifier.Parse(str));
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessagePart"/> class.
- /// </summary>
- /// <param name="member">
- /// A property or field of an <see cref="IMessage"/> implementing type
- /// that has a <see cref="MessagePartAttribute"/> attached to it.
- /// </param>
- /// <param name="attribute">
- /// The attribute discovered on <paramref name="member"/> that describes the
- /// serialization requirements of the message part.
- /// </param>
- internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
- if (member == null) {
- throw new ArgumentNullException("member");
- }
-
- this.field = member as FieldInfo;
- this.property = member as PropertyInfo;
- if (this.field == null && this.property == null) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
- member.GetType().Name),
- "member");
- }
-
- if (attribute == null) {
- throw new ArgumentNullException("attribute");
- }
-
- this.Name = attribute.Name ?? member.Name;
- this.RequiredProtection = attribute.RequiredProtection;
- this.IsRequired = attribute.IsRequired;
- this.AllowEmpty = attribute.AllowEmpty;
- this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
- this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
-
- if (attribute.Encoder == null) {
- if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
- this.converter = new ValueMapping(
- obj => obj != null ? obj.ToString() : null,
- str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
- }
- } else {
- var encoder = GetEncoder(attribute.Encoder);
- this.converter = new ValueMapping(
- obj => encoder.Encode(obj),
- str => encoder.Decode(str));
- }
-
- if (this.field != null && (this.field.Attributes & FieldAttributes.InitOnly) != 0) {
- this.IsConstantValue = true;
- } else if (this.property != null && !this.property.CanWrite) {
- this.IsConstantValue = true;
- }
-
- // Validate a sane combination of settings
- this.ValidateSettings();
- }
-
- /// <summary>
- /// Gets or sets the name to use when serializing or deserializing this parameter in a message.
- /// </summary>
- internal string Name { get; set; }
-
- /// <summary>
- /// Gets or sets whether this message part must be signed.
- /// </summary>
- internal ProtectionLevel RequiredProtection { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this message part is required for the
- /// containing message to be valid.
- /// </summary>
- internal bool IsRequired { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the string value is allowed to be empty in the serialized message.
- /// </summary>
- internal bool AllowEmpty { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the field or property must remain its default value.
- /// </summary>
- internal bool IsConstantValue { get; set; }
-
- /// <summary>
- /// Sets the member of a given message to some given value.
- /// Used in deserialization.
- /// </summary>
- /// <param name="message">The message instance containing the member whose value should be set.</param>
- /// <param name="value">The string representation of the value to set.</param>
- internal void SetValue(IMessage message, string value) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- try {
- if (this.IsConstantValue) {
- string constantValue = this.GetValue(message);
- if (!string.Equals(constantValue, value)) {
- throw new ArgumentException(string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedMessagePartValueForConstant,
- message.GetType().Name,
- this.Name,
- constantValue,
- value));
- }
- } else {
- if (this.property != null) {
- this.property.SetValue(message, this.ToValue(value), null);
- } else {
- this.field.SetValue(message, this.ToValue(value));
- }
- }
- } catch (FormatException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartReadFailure, message.GetType(), this.Name, value);
- }
- }
-
- /// <summary>
- /// Gets the value of a member of a given message.
- /// Used in serialization.
- /// </summary>
- /// <param name="message">The message instance to read the value from.</param>
- /// <returns>The string representation of the member's value.</returns>
- internal string GetValue(IMessage message) {
- try {
- object value = this.GetValueAsObject(message);
- return this.ToString(value);
- } catch (FormatException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name);
- }
- }
-
- /// <summary>
- /// Gets whether the value has been set to something other than its CLR type default value.
- /// </summary>
- /// <param name="message">The message instance to check the value on.</param>
- /// <returns>True if the value is not the CLR default value.</returns>
- internal bool IsNondefaultValueSet(IMessage message) {
- if (this.memberDeclaredType.IsValueType) {
- return !this.GetValueAsObject(message).Equals(this.defaultMemberValue);
- } else {
- return this.defaultMemberValue != this.GetValueAsObject(message);
- }
- }
-
- /// <summary>
- /// Figures out the CLR default value for a given type.
- /// </summary>
- /// <param name="type">The type whose default value is being sought.</param>
- /// <returns>Either null, or some default value like 0 or 0.0.</returns>
- private static object DeriveDefaultValue(Type type) {
- if (type.IsValueType) {
- return Activator.CreateInstance(type);
- } else {
- return null;
- }
- }
-
- /// <summary>
- /// Adds a pair of type conversion functions to the static converstion map.
- /// </summary>
- /// <typeparam name="T">The custom type to convert to and from strings.</typeparam>
- /// <param name="toString">The function to convert the custom type to a string.</param>
- /// <param name="toValue">The function to convert a string to the custom type.</param>
- private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) {
- Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null;
- Func<string, object> safeToT = str => str != null ? toValue(str) : default(T);
- converters.Add(typeof(T), new ValueMapping(safeToString, safeToT));
- }
-
- /// <summary>
- /// Checks whether a type is a nullable value type (i.e. int?)
- /// </summary>
- /// <param name="type">The type in question.</param>
- /// <returns>True if this is a nullable value type.</returns>
- private static bool IsNonNullableValueType(Type type) {
- if (!type.IsValueType) {
- return false;
- }
-
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
- return false;
- }
-
- return true;
- }
-
- /// <summary>
- /// Retrieves a previously instantiated encoder of a given type, or creates a new one and stores it for later retrieval as well.
- /// </summary>
- /// <param name="messagePartEncoder">The message part encoder type.</param>
- /// <returns>An instance of the desired encoder.</returns>
- private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) {
- IMessagePartEncoder encoder;
- if (!encoders.TryGetValue(messagePartEncoder, out encoder)) {
- encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder);
- }
-
- return encoder;
- }
-
- /// <summary>
- /// Converts a string representation of the member's value to the appropriate type.
- /// </summary>
- /// <param name="value">The string representation of the member's value.</param>
- /// <returns>
- /// An instance of the appropriate type for setting the member.
- /// </returns>
- private object ToValue(string value) {
- return value == null ? null : this.converter.StringToValue(value);
- }
-
- /// <summary>
- /// Converts the member's value to its string representation.
- /// </summary>
- /// <param name="value">The value of the member.</param>
- /// <returns>
- /// The string representation of the member's value.
- /// </returns>
- private string ToString(object value) {
- return value == null ? null : this.converter.ValueToString(value);
- }
-
- /// <summary>
- /// Gets the value of the message part, without converting it to/from a string.
- /// </summary>
- /// <param name="message">The message instance to read from.</param>
- /// <returns>The value of the member.</returns>
- private object GetValueAsObject(IMessage message) {
- if (this.property != null) {
- return this.property.GetValue(message, null);
- } else {
- return this.field.GetValue(message);
- }
- }
-
- /// <summary>
- /// Validates that the message part and its attribute have agreeable settings.
- /// </summary>
- /// <exception cref="ArgumentException">
- /// Thrown when a non-nullable value type is set as optional.
- /// </exception>
- private void ValidateSettings() {
- if (!this.IsRequired && IsNonNullableValueType(this.memberDeclaredType)) {
- MemberInfo member = (MemberInfo)this.field ?? this.property;
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- "Invalid combination: {0} on message type {1} is a non-nullable value type but is marked as optional.",
- member.Name,
- member.DeclaringType));
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessagePart.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Reflection {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Net.Security;
+ using System.Reflection;
+ using System.Xml;
+ using DotNetOpenAuth.OpenId;
+
+ /// <summary>
+ /// Describes an individual member of a message and assists in its serialization.
+ /// </summary>
+ internal class MessagePart {
+ /// <summary>
+ /// A map of converters that help serialize custom objects to string values and back again.
+ /// </summary>
+ private static readonly Dictionary<Type, ValueMapping> converters = new Dictionary<Type, ValueMapping>();
+
+ /// <summary>
+ /// A map of instantiated custom encoders used to encode/decode message parts.
+ /// </summary>
+ private static readonly Dictionary<Type, IMessagePartEncoder> encoders = new Dictionary<Type, IMessagePartEncoder>();
+
+ /// <summary>
+ /// The string-object conversion routines to use for this individual message part.
+ /// </summary>
+ private ValueMapping converter;
+
+ /// <summary>
+ /// The property that this message part is associated with, if aplicable.
+ /// </summary>
+ private PropertyInfo property;
+
+ /// <summary>
+ /// The field that this message part is associated with, if aplicable.
+ /// </summary>
+ private FieldInfo field;
+
+ /// <summary>
+ /// The type of the message part. (Not the type of the message itself).
+ /// </summary>
+ private Type memberDeclaredType;
+
+ /// <summary>
+ /// The default (uninitialized) value of the member inherent in its type.
+ /// </summary>
+ private object defaultMemberValue;
+
+ /// <summary>
+ /// Initializes static members of the <see cref="MessagePart"/> class.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
+ static MessagePart() {
+ Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
+ Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
+ Map<byte[]>(bytes => Convert.ToBase64String(bytes), str => Convert.FromBase64String(str));
+ Map<Realm>(realm => realm.ToString(), str => new Realm(str));
+ Map<Identifier>(id => id.ToString(), str => Identifier.Parse(str));
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessagePart"/> class.
+ /// </summary>
+ /// <param name="member">
+ /// A property or field of an <see cref="IMessage"/> implementing type
+ /// that has a <see cref="MessagePartAttribute"/> attached to it.
+ /// </param>
+ /// <param name="attribute">
+ /// The attribute discovered on <paramref name="member"/> that describes the
+ /// serialization requirements of the message part.
+ /// </param>
+ internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
+ if (member == null) {
+ throw new ArgumentNullException("member");
+ }
+
+ this.field = member as FieldInfo;
+ this.property = member as PropertyInfo;
+ if (this.field == null && this.property == null) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedType,
+ typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
+ member.GetType().Name),
+ "member");
+ }
+
+ if (attribute == null) {
+ throw new ArgumentNullException("attribute");
+ }
+
+ this.Name = attribute.Name ?? member.Name;
+ this.RequiredProtection = attribute.RequiredProtection;
+ this.IsRequired = attribute.IsRequired;
+ this.AllowEmpty = attribute.AllowEmpty;
+ this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
+ this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
+
+ if (attribute.Encoder == null) {
+ if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
+ this.converter = new ValueMapping(
+ obj => obj != null ? obj.ToString() : null,
+ str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
+ }
+ } else {
+ var encoder = GetEncoder(attribute.Encoder);
+ this.converter = new ValueMapping(
+ obj => encoder.Encode(obj),
+ str => encoder.Decode(str));
+ }
+
+ if (this.field != null && (this.field.Attributes & FieldAttributes.InitOnly) != 0) {
+ this.IsConstantValue = true;
+ } else if (this.property != null && !this.property.CanWrite) {
+ this.IsConstantValue = true;
+ }
+
+ // Validate a sane combination of settings
+ this.ValidateSettings();
+ }
+
+ /// <summary>
+ /// Gets or sets the name to use when serializing or deserializing this parameter in a message.
+ /// </summary>
+ internal string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether this message part must be signed.
+ /// </summary>
+ internal ProtectionLevel RequiredProtection { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this message part is required for the
+ /// containing message to be valid.
+ /// </summary>
+ internal bool IsRequired { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the string value is allowed to be empty in the serialized message.
+ /// </summary>
+ internal bool AllowEmpty { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the field or property must remain its default value.
+ /// </summary>
+ internal bool IsConstantValue { get; set; }
+
+ /// <summary>
+ /// Sets the member of a given message to some given value.
+ /// Used in deserialization.
+ /// </summary>
+ /// <param name="message">The message instance containing the member whose value should be set.</param>
+ /// <param name="value">The string representation of the value to set.</param>
+ internal void SetValue(IMessage message, string value) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ try {
+ if (this.IsConstantValue) {
+ string constantValue = this.GetValue(message);
+ if (!string.Equals(constantValue, value)) {
+ throw new ArgumentException(string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedMessagePartValueForConstant,
+ message.GetType().Name,
+ this.Name,
+ constantValue,
+ value));
+ }
+ } else {
+ if (this.property != null) {
+ this.property.SetValue(message, this.ToValue(value), null);
+ } else {
+ this.field.SetValue(message, this.ToValue(value));
+ }
+ }
+ } catch (FormatException ex) {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartReadFailure, message.GetType(), this.Name, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets the value of a member of a given message.
+ /// Used in serialization.
+ /// </summary>
+ /// <param name="message">The message instance to read the value from.</param>
+ /// <returns>The string representation of the member's value.</returns>
+ internal string GetValue(IMessage message) {
+ try {
+ object value = this.GetValueAsObject(message);
+ return this.ToString(value);
+ } catch (FormatException ex) {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name);
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the value has been set to something other than its CLR type default value.
+ /// </summary>
+ /// <param name="message">The message instance to check the value on.</param>
+ /// <returns>True if the value is not the CLR default value.</returns>
+ internal bool IsNondefaultValueSet(IMessage message) {
+ if (this.memberDeclaredType.IsValueType) {
+ return !this.GetValueAsObject(message).Equals(this.defaultMemberValue);
+ } else {
+ return this.defaultMemberValue != this.GetValueAsObject(message);
+ }
+ }
+
+ /// <summary>
+ /// Figures out the CLR default value for a given type.
+ /// </summary>
+ /// <param name="type">The type whose default value is being sought.</param>
+ /// <returns>Either null, or some default value like 0 or 0.0.</returns>
+ private static object DeriveDefaultValue(Type type) {
+ if (type.IsValueType) {
+ return Activator.CreateInstance(type);
+ } else {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Adds a pair of type conversion functions to the static converstion map.
+ /// </summary>
+ /// <typeparam name="T">The custom type to convert to and from strings.</typeparam>
+ /// <param name="toString">The function to convert the custom type to a string.</param>
+ /// <param name="toValue">The function to convert a string to the custom type.</param>
+ private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) {
+ Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null;
+ Func<string, object> safeToT = str => str != null ? toValue(str) : default(T);
+ converters.Add(typeof(T), new ValueMapping(safeToString, safeToT));
+ }
+
+ /// <summary>
+ /// Checks whether a type is a nullable value type (i.e. int?)
+ /// </summary>
+ /// <param name="type">The type in question.</param>
+ /// <returns>True if this is a nullable value type.</returns>
+ private static bool IsNonNullableValueType(Type type) {
+ if (!type.IsValueType) {
+ return false;
+ }
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Retrieves a previously instantiated encoder of a given type, or creates a new one and stores it for later retrieval as well.
+ /// </summary>
+ /// <param name="messagePartEncoder">The message part encoder type.</param>
+ /// <returns>An instance of the desired encoder.</returns>
+ private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) {
+ IMessagePartEncoder encoder;
+ if (!encoders.TryGetValue(messagePartEncoder, out encoder)) {
+ encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder);
+ }
+
+ return encoder;
+ }
+
+ /// <summary>
+ /// Converts a string representation of the member's value to the appropriate type.
+ /// </summary>
+ /// <param name="value">The string representation of the member's value.</param>
+ /// <returns>
+ /// An instance of the appropriate type for setting the member.
+ /// </returns>
+ private object ToValue(string value) {
+ return value == null ? null : this.converter.StringToValue(value);
+ }
+
+ /// <summary>
+ /// Converts the member's value to its string representation.
+ /// </summary>
+ /// <param name="value">The value of the member.</param>
+ /// <returns>
+ /// The string representation of the member's value.
+ /// </returns>
+ private string ToString(object value) {
+ return value == null ? null : this.converter.ValueToString(value);
+ }
+
+ /// <summary>
+ /// Gets the value of the message part, without converting it to/from a string.
+ /// </summary>
+ /// <param name="message">The message instance to read from.</param>
+ /// <returns>The value of the member.</returns>
+ private object GetValueAsObject(IMessage message) {
+ if (this.property != null) {
+ return this.property.GetValue(message, null);
+ } else {
+ return this.field.GetValue(message);
+ }
+ }
+
+ /// <summary>
+ /// Validates that the message part and its attribute have agreeable settings.
+ /// </summary>
+ /// <exception cref="ArgumentException">
+ /// Thrown when a non-nullable value type is set as optional.
+ /// </exception>
+ private void ValidateSettings() {
+ if (!this.IsRequired && IsNonNullableValueType(this.memberDeclaredType)) {
+ MemberInfo member = (MemberInfo)this.field ?? this.property;
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ "Invalid combination: {0} on message type {1} is a non-nullable value type but is marked as optional.",
+ member.Name,
+ member.DeclaringType));
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
index 35f8768..c995066 100644
--- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
@@ -1,166 +1,166 @@
-//-----------------------------------------------------------------------
-// <copyright file="ConsumerBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Net;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OAuth.ChannelElements;
- using DotNetOpenAuth.OAuth.Messages;
-
- /// <summary>
- /// Base class for <see cref="WebConsumer"/> and <see cref="DesktopConsumer"/> types.
- /// </summary>
- public class ConsumerBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="ConsumerBase"/> class.
- /// </summary>
- /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
- /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
- protected ConsumerBase(ServiceProviderDescription serviceDescription, ITokenManager tokenManager) {
- if (serviceDescription == null) {
- throw new ArgumentNullException("serviceDescription");
- }
- if (tokenManager == null) {
- throw new ArgumentNullException("tokenManager");
- }
-
- ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
- INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
- this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, new OAuthConsumerMessageFactory());
- this.ServiceProvider = serviceDescription;
- }
-
- /// <summary>
- /// Gets or sets the Consumer Key used to communicate with the Service Provider.
- /// </summary>
- public string ConsumerKey { get; set; }
-
- /// <summary>
- /// Gets the Service Provider that will be accessed.
- /// </summary>
- public ServiceProviderDescription ServiceProvider { get; private set; }
-
- /// <summary>
- /// Gets the persistence store for tokens and secrets.
- /// </summary>
- public ITokenManager TokenManager {
- get { return this.OAuthChannel.TokenManager; }
- }
-
- /// <summary>
- /// Gets the channel to use for sending/receiving messages.
- /// </summary>
- public Channel Channel {
- get { return this.OAuthChannel; }
- }
-
- /// <summary>
- /// Gets or sets the channel to use for sending/receiving messages.
- /// </summary>
- internal OAuthChannel OAuthChannel { get; set; }
-
- /// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- public WebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
- IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
- HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
- return wr;
- }
-
- /// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- /// <exception cref="WebException">Thrown if the request fails for any reason after it is sent to the Service Provider.</exception>
- public DirectWebResponse PrepareAuthorizedRequestAndSend(MessageReceivingEndpoint endpoint, string accessToken) {
- IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
- HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
- return this.Channel.WebRequestHandler.GetResponse(wr);
- }
-
- /// <summary>
- /// Prepares an OAuth message that begins an authorization request that will
- /// redirect the user to the Service Provider to provide that authorization.
- /// </summary>
- /// <param name="callback">
- /// An optional Consumer URL that the Service Provider should redirect the
- /// User Agent to upon successful authorization.
- /// </param>
- /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
- /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
- /// <param name="requestToken">The request token that must be exchanged for an access token after the user has provided authorization.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")]
- protected internal UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) {
- // Obtain an unauthorized request token.
- var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint) {
- ConsumerKey = this.ConsumerKey,
- };
- token.AddExtraParameters(requestParameters);
- var requestTokenResponse = this.Channel.Request<UnauthorizedTokenResponse>(token);
- this.TokenManager.StoreNewRequestToken(token, requestTokenResponse);
-
- // Request user authorization.
- ITokenContainingMessage assignedRequestToken = requestTokenResponse;
- var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token) {
- Callback = callback,
- };
- requestAuthorization.AddExtraParameters(redirectParameters);
- requestToken = requestAuthorization.RequestToken;
- return requestAuthorization;
- }
-
- /// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
- if (endpoint == null) {
- throw new ArgumentNullException("endpoint");
- }
- if (String.IsNullOrEmpty(accessToken)) {
- throw new ArgumentNullException("accessToken");
- }
-
- AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) {
- AccessToken = accessToken,
- ConsumerKey = this.ConsumerKey,
- };
-
- return message;
- }
-
- /// <summary>
- /// Exchanges a given request token for access token.
- /// </summary>
- /// <param name="requestToken">The request token that the user has authorized.</param>
- /// <returns>The access token assigned by the Service Provider.</returns>
- protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) {
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) {
- RequestToken = requestToken,
- ConsumerKey = this.ConsumerKey,
- };
- var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess);
- this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, requestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
- return grantAccess;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ConsumerBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Net;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// Base class for <see cref="WebConsumer"/> and <see cref="DesktopConsumer"/> types.
+ /// </summary>
+ public class ConsumerBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConsumerBase"/> class.
+ /// </summary>
+ /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
+ /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
+ protected ConsumerBase(ServiceProviderDescription serviceDescription, ITokenManager tokenManager) {
+ if (serviceDescription == null) {
+ throw new ArgumentNullException("serviceDescription");
+ }
+ if (tokenManager == null) {
+ throw new ArgumentNullException("tokenManager");
+ }
+
+ ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
+ INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
+ this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, new OAuthConsumerMessageFactory());
+ this.ServiceProvider = serviceDescription;
+ }
+
+ /// <summary>
+ /// Gets or sets the Consumer Key used to communicate with the Service Provider.
+ /// </summary>
+ public string ConsumerKey { get; set; }
+
+ /// <summary>
+ /// Gets the Service Provider that will be accessed.
+ /// </summary>
+ public ServiceProviderDescription ServiceProvider { get; private set; }
+
+ /// <summary>
+ /// Gets the persistence store for tokens and secrets.
+ /// </summary>
+ public ITokenManager TokenManager {
+ get { return this.OAuthChannel.TokenManager; }
+ }
+
+ /// <summary>
+ /// Gets the channel to use for sending/receiving messages.
+ /// </summary>
+ public Channel Channel {
+ get { return this.OAuthChannel; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel to use for sending/receiving messages.
+ /// </summary>
+ internal OAuthChannel OAuthChannel { get; set; }
+
+ /// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ public WebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
+ IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
+ HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
+ return wr;
+ }
+
+ /// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ /// <exception cref="WebException">Thrown if the request fails for any reason after it is sent to the Service Provider.</exception>
+ public DirectWebResponse PrepareAuthorizedRequestAndSend(MessageReceivingEndpoint endpoint, string accessToken) {
+ IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
+ HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
+ return this.Channel.WebRequestHandler.GetResponse(wr);
+ }
+
+ /// <summary>
+ /// Prepares an OAuth message that begins an authorization request that will
+ /// redirect the user to the Service Provider to provide that authorization.
+ /// </summary>
+ /// <param name="callback">
+ /// An optional Consumer URL that the Service Provider should redirect the
+ /// User Agent to upon successful authorization.
+ /// </param>
+ /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
+ /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
+ /// <param name="requestToken">The request token that must be exchanged for an access token after the user has provided authorization.</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")]
+ protected internal UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) {
+ // Obtain an unauthorized request token.
+ var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint) {
+ ConsumerKey = this.ConsumerKey,
+ };
+ token.AddExtraParameters(requestParameters);
+ var requestTokenResponse = this.Channel.Request<UnauthorizedTokenResponse>(token);
+ this.TokenManager.StoreNewRequestToken(token, requestTokenResponse);
+
+ // Request user authorization.
+ ITokenContainingMessage assignedRequestToken = requestTokenResponse;
+ var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token) {
+ Callback = callback,
+ };
+ requestAuthorization.AddExtraParameters(redirectParameters);
+ requestToken = requestAuthorization.RequestToken;
+ return requestAuthorization;
+ }
+
+ /// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
+ if (endpoint == null) {
+ throw new ArgumentNullException("endpoint");
+ }
+ if (String.IsNullOrEmpty(accessToken)) {
+ throw new ArgumentNullException("accessToken");
+ }
+
+ AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) {
+ AccessToken = accessToken,
+ ConsumerKey = this.ConsumerKey,
+ };
+
+ return message;
+ }
+
+ /// <summary>
+ /// Exchanges a given request token for access token.
+ /// </summary>
+ /// <param name="requestToken">The request token that the user has authorized.</param>
+ /// <returns>The access token assigned by the Service Provider.</returns>
+ protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) {
+ var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) {
+ RequestToken = requestToken,
+ ConsumerKey = this.ConsumerKey,
+ };
+ var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess);
+ this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, requestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
+ return grantAccess;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
index ac40efd..f2977e2 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
@@ -1,272 +1,272 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessageBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.Messages {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OAuth.ChannelElements;
-
- /// <summary>
- /// A base class for all OAuth messages.
- /// </summary>
- public abstract class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage {
- /// <summary>
- /// A store for extra name/value data pairs that are attached to this message.
- /// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- /// <summary>
- /// Gets a value indicating whether signing this message is required.
- /// </summary>
- private MessageProtections protectionRequired;
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- private MessageTransport transport;
-
- /// <summary>
- /// The URI to the remote endpoint to send this message to.
- /// </summary>
- private MessageReceivingEndpoint recipient;
-
- /// <summary>
- /// Backing store for the <see cref="OriginatingRequest"/> properties.
- /// </summary>
- private IDirectedProtocolMessage originatingRequest;
-
- /// <summary>
- /// Backing store for the <see cref="Incoming"/> properties.
- /// </summary>
- private bool incoming;
-
-#if DEBUG
- /// <summary>
- /// Initializes static members of the <see cref="MessageBase"/> class.
- /// </summary>
- static MessageBase() {
- LowSecurityMode = true;
- }
-#endif
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageBase"/> class for direct response messages.
- /// </summary>
- /// <param name="protectionRequired">The level of protection the message requires.</param>
- /// <param name="originatingRequest">The request that asked for this direct response.</param>
- protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest) {
- ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
-
- this.protectionRequired = protectionRequired;
- this.transport = MessageTransport.Direct;
- this.originatingRequest = originatingRequest;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageBase"/> class for direct requests or indirect messages.
- /// </summary>
- /// <param name="protectionRequired">The level of protection the message requires.</param>
- /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
- /// <param name="recipient">The URI that a directed message will be delivered to.</param>
- protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient) {
- if (recipient == null) {
- throw new ArgumentNullException("recipient");
- }
-
- this.protectionRequired = protectionRequired;
- this.transport = transport;
- this.recipient = recipient;
- }
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- Version IMessage.Version {
- get { return this.Version; }
- }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return this.RequiredProtection; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- MessageTransport IProtocolMessage.Transport {
- get { return this.Transport; }
- }
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- IDictionary<string, string> IMessage.ExtraData {
- get { return this.ExtraData; }
- }
-
- #endregion
-
- #region IDirectedProtocolMessage Members
-
- /// <summary>
- /// Gets the URI to the Service Provider endpoint to send this message to.
- /// </summary>
- Uri IDirectedProtocolMessage.Recipient {
- get { return this.recipient != null ? this.recipient.Location : null; }
- }
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
- get { return this.HttpMethods; }
- }
-
- #endregion
-
- #region IDirectResponseProtocolMessage Members
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets or sets a value indicating whether security sensitive strings are
- /// emitted from the ToString() method.
- /// </summary>
- internal static bool LowSecurityMode { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- protected internal bool Incoming {
- get { return this.incoming; }
- }
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- protected virtual Version Version {
- get { return new Version(1, 0); }
- }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- protected MessageProtections RequiredProtection {
- get { return this.protectionRequired; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- protected MessageTransport Transport {
- get { return this.transport; }
- }
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- protected IDictionary<string, string> ExtraData {
- get { return this.extraData; }
- }
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- protected HttpDeliveryMethods HttpMethods {
- get { return this.recipient != null ? this.recipient.AllowedMethods : HttpDeliveryMethods.None; }
- }
-
- /// <summary>
- /// Gets or sets the URI to the Service Provider endpoint to send this message to.
- /// </summary>
- protected Uri Recipient {
- get {
- return this.recipient != null ? this.recipient.Location : null;
- }
-
- set {
- if (this.recipient != null) {
- this.recipient = new MessageReceivingEndpoint(value, this.recipient.AllowedMethods);
- } else if (value != null) {
- throw new InvalidOperationException();
- }
- }
- }
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- protected IDirectedProtocolMessage OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- #region IProtocolMessage Methods
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- void IMessage.EnsureValidMessage() {
- this.EnsureValidMessage();
- }
-
- #endregion
-
- /// <summary>
- /// Returns a human-friendly string describing the message and all serializable properties.
- /// </summary>
- /// <returns>The string representation of this object.</returns>
- public override string ToString() {
- StringBuilder builder = new StringBuilder();
- builder.AppendFormat(CultureInfo.InvariantCulture, "{0} message", GetType().Name);
- if (this.recipient != null) {
- builder.AppendFormat(CultureInfo.InvariantCulture, " as {0} to {1}", this.recipient.AllowedMethods, this.recipient.Location);
- }
- builder.AppendLine();
- MessageDictionary dictionary = new MessageDictionary(this);
- foreach (var pair in dictionary) {
- string value = pair.Value;
- if (pair.Key == "oauth_signature" && !LowSecurityMode) {
- value = "xxxxxxxxxxxxx (not shown)";
- }
- builder.Append('\t');
- builder.Append(pair.Key);
- builder.Append(": ");
- builder.AppendLine(value);
- }
-
- return builder.ToString();
- }
-
- /// <summary>
- /// Sets a flag indicating that this message is received (as opposed to sent).
- /// </summary>
- internal void SetAsIncoming() {
- this.incoming = true;
- }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- protected virtual void EnsureValidMessage() { }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessageBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+
+ /// <summary>
+ /// A base class for all OAuth messages.
+ /// </summary>
+ public abstract class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage {
+ /// <summary>
+ /// A store for extra name/value data pairs that are attached to this message.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Gets a value indicating whether signing this message is required.
+ /// </summary>
+ private MessageProtections protectionRequired;
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ private MessageTransport transport;
+
+ /// <summary>
+ /// The URI to the remote endpoint to send this message to.
+ /// </summary>
+ private MessageReceivingEndpoint recipient;
+
+ /// <summary>
+ /// Backing store for the <see cref="OriginatingRequest"/> properties.
+ /// </summary>
+ private IDirectedProtocolMessage originatingRequest;
+
+ /// <summary>
+ /// Backing store for the <see cref="Incoming"/> properties.
+ /// </summary>
+ private bool incoming;
+
+#if DEBUG
+ /// <summary>
+ /// Initializes static members of the <see cref="MessageBase"/> class.
+ /// </summary>
+ static MessageBase() {
+ LowSecurityMode = true;
+ }
+#endif
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBase"/> class for direct response messages.
+ /// </summary>
+ /// <param name="protectionRequired">The level of protection the message requires.</param>
+ /// <param name="originatingRequest">The request that asked for this direct response.</param>
+ protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest) {
+ ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
+
+ this.protectionRequired = protectionRequired;
+ this.transport = MessageTransport.Direct;
+ this.originatingRequest = originatingRequest;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBase"/> class for direct requests or indirect messages.
+ /// </summary>
+ /// <param name="protectionRequired">The level of protection the message requires.</param>
+ /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
+ /// <param name="recipient">The URI that a directed message will be delivered to.</param>
+ protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient) {
+ if (recipient == null) {
+ throw new ArgumentNullException("recipient");
+ }
+
+ this.protectionRequired = protectionRequired;
+ this.transport = transport;
+ this.recipient = recipient;
+ }
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ Version IMessage.Version {
+ get { return this.Version; }
+ }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return this.RequiredProtection; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport IProtocolMessage.Transport {
+ get { return this.Transport; }
+ }
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.ExtraData; }
+ }
+
+ #endregion
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the URI to the Service Provider endpoint to send this message to.
+ /// </summary>
+ Uri IDirectedProtocolMessage.Recipient {
+ get { return this.recipient != null ? this.recipient.Location : null; }
+ }
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get { return this.HttpMethods; }
+ }
+
+ #endregion
+
+ #region IDirectResponseProtocolMessage Members
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets a value indicating whether security sensitive strings are
+ /// emitted from the ToString() method.
+ /// </summary>
+ internal static bool LowSecurityMode { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ protected internal bool Incoming {
+ get { return this.incoming; }
+ }
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ protected virtual Version Version {
+ get { return new Version(1, 0); }
+ }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ protected MessageProtections RequiredProtection {
+ get { return this.protectionRequired; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ protected MessageTransport Transport {
+ get { return this.transport; }
+ }
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ protected IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ protected HttpDeliveryMethods HttpMethods {
+ get { return this.recipient != null ? this.recipient.AllowedMethods : HttpDeliveryMethods.None; }
+ }
+
+ /// <summary>
+ /// Gets or sets the URI to the Service Provider endpoint to send this message to.
+ /// </summary>
+ protected Uri Recipient {
+ get {
+ return this.recipient != null ? this.recipient.Location : null;
+ }
+
+ set {
+ if (this.recipient != null) {
+ this.recipient = new MessageReceivingEndpoint(value, this.recipient.AllowedMethods);
+ } else if (value != null) {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ protected IDirectedProtocolMessage OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #region IProtocolMessage Methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ void IMessage.EnsureValidMessage() {
+ this.EnsureValidMessage();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Returns a human-friendly string describing the message and all serializable properties.
+ /// </summary>
+ /// <returns>The string representation of this object.</returns>
+ public override string ToString() {
+ StringBuilder builder = new StringBuilder();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "{0} message", GetType().Name);
+ if (this.recipient != null) {
+ builder.AppendFormat(CultureInfo.InvariantCulture, " as {0} to {1}", this.recipient.AllowedMethods, this.recipient.Location);
+ }
+ builder.AppendLine();
+ MessageDictionary dictionary = new MessageDictionary(this);
+ foreach (var pair in dictionary) {
+ string value = pair.Value;
+ if (pair.Key == "oauth_signature" && !LowSecurityMode) {
+ value = "xxxxxxxxxxxxx (not shown)";
+ }
+ builder.Append('\t');
+ builder.Append(pair.Key);
+ builder.Append(": ");
+ builder.AppendLine(value);
+ }
+
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Sets a flag indicating that this message is received (as opposed to sent).
+ /// </summary>
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ protected virtual void EnsureValidMessage() { }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
index ee5d46f..cbe8b8a 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
@@ -1,169 +1,169 @@
-//-----------------------------------------------------------------------
-// <copyright file="SignedMessageBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.Messages {
- using System;
- using System.Diagnostics.CodeAnalysis;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OAuth.ChannelElements;
-
- /// <summary>
- /// A base class for all signed OAuth messages.
- /// </summary>
- public class SignedMessageBase : MessageBase, ITamperResistantOAuthMessage, IExpiringProtocolMessage, IReplayProtectedProtocolMessage {
- /// <summary>
- /// The reference date and time for calculating time stamps.
- /// </summary>
- private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
-
- /// <summary>
- /// The number of seconds since 1/1/1970, consistent with the OAuth timestamp requirement.
- /// </summary>
- [MessagePart("oauth_timestamp", IsRequired = true)]
- private long timestamp;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SignedMessageBase"/> class.
- /// </summary>
- /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
- /// <param name="recipient">The URI that a directed message will be delivered to.</param>
- internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient)
- : base(MessageProtections.All, transport, recipient) {
- ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this;
- HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods;
- self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
- }
-
- #region ITamperResistantOAuthMessage Members
-
- /// <summary>
- /// Gets or sets the signature method used to sign the request.
- /// </summary>
- string ITamperResistantOAuthMessage.SignatureMethod {
- get { return this.SignatureMethod; }
- set { this.SignatureMethod = value; }
- }
-
- /// <summary>
- /// Gets or sets the Token Secret used to sign the message.
- /// </summary>
- string ITamperResistantOAuthMessage.TokenSecret {
- get { return this.TokenSecret; }
- set { this.TokenSecret = value; }
- }
-
- /// <summary>
- /// Gets or sets the Consumer key.
- /// </summary>
- [MessagePart("oauth_consumer_key", IsRequired = true)]
- public string ConsumerKey { get; set; }
-
- /// <summary>
- /// Gets or sets the Consumer Secret used to sign the message.
- /// </summary>
- string ITamperResistantOAuthMessage.ConsumerSecret {
- get { return this.ConsumerSecret; }
- set { this.ConsumerSecret = value; }
- }
-
- /// <summary>
- /// Gets or sets the HTTP method that will be used to transmit the message.
- /// </summary>
- string ITamperResistantOAuthMessage.HttpMethod {
- get { return this.HttpMethod; }
- set { this.HttpMethod = value; }
- }
-
- /// <summary>
- /// Gets or sets the URI to the Service Provider endpoint to send this message to.
- /// </summary>
- Uri ITamperResistantOAuthMessage.Recipient {
- get { return this.Recipient; }
- set { this.Recipient = value; }
- }
-
- #endregion
-
- #region ITamperResistantProtocolMessage Members
-
- /// <summary>
- /// Gets or sets the message signature.
- /// </summary>
- string ITamperResistantProtocolMessage.Signature {
- get { return this.Signature; }
- set { this.Signature = value; }
- }
-
- #endregion
-
- #region IExpiringProtocolMessage Members
-
- /// <summary>
- /// Gets or sets the OAuth timestamp of the message.
- /// </summary>
- DateTime IExpiringProtocolMessage.UtcCreationDate {
- get { return epoch + TimeSpan.FromSeconds(this.timestamp); }
- set { this.timestamp = (long)(value - epoch).TotalSeconds; }
- }
-
- #endregion
-
- #region IReplayProtectedProtocolMessage Members
-
- /// <summary>
- /// Gets or sets the message nonce used for replay detection.
- /// </summary>
- [MessagePart("oauth_nonce", IsRequired = true)]
- string IReplayProtectedProtocolMessage.Nonce { get; set; }
-
- #endregion
-
- /// <summary>
- /// Gets or sets the signature method used to sign the request.
- /// </summary>
- [MessagePart("oauth_signature_method", IsRequired = true)]
- protected string SignatureMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the Token Secret used to sign the message.
- /// </summary>
- protected string TokenSecret { get; set; }
-
- /// <summary>
- /// Gets or sets the Consumer Secret used to sign the message.
- /// </summary>
- protected string ConsumerSecret { get; set; }
-
- /// <summary>
- /// Gets or sets the HTTP method that will be used to transmit the message.
- /// </summary>
- protected string HttpMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the message signature.
- /// </summary>
- [MessagePart("oauth_signature", IsRequired = true)]
- protected string Signature { get; set; }
-
- /// <summary>
- /// Gets or sets the version of the protocol this message was created with.
- /// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Accessed via reflection.")]
- [MessagePart("oauth_version", IsRequired = false)]
- private string OAuthVersion {
- get {
- return Version.ToString();
- }
-
- set {
- if (value != this.OAuthVersion) {
- throw new ArgumentOutOfRangeException("value");
- }
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="SignedMessageBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+
+ /// <summary>
+ /// A base class for all signed OAuth messages.
+ /// </summary>
+ public class SignedMessageBase : MessageBase, ITamperResistantOAuthMessage, IExpiringProtocolMessage, IReplayProtectedProtocolMessage {
+ /// <summary>
+ /// The reference date and time for calculating time stamps.
+ /// </summary>
+ private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ /// <summary>
+ /// The number of seconds since 1/1/1970, consistent with the OAuth timestamp requirement.
+ /// </summary>
+ [MessagePart("oauth_timestamp", IsRequired = true)]
+ private long timestamp;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SignedMessageBase"/> class.
+ /// </summary>
+ /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
+ /// <param name="recipient">The URI that a directed message will be delivered to.</param>
+ internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient)
+ : base(MessageProtections.All, transport, recipient) {
+ ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this;
+ HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods;
+ self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
+ }
+
+ #region ITamperResistantOAuthMessage Members
+
+ /// <summary>
+ /// Gets or sets the signature method used to sign the request.
+ /// </summary>
+ string ITamperResistantOAuthMessage.SignatureMethod {
+ get { return this.SignatureMethod; }
+ set { this.SignatureMethod = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the Token Secret used to sign the message.
+ /// </summary>
+ string ITamperResistantOAuthMessage.TokenSecret {
+ get { return this.TokenSecret; }
+ set { this.TokenSecret = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the Consumer key.
+ /// </summary>
+ [MessagePart("oauth_consumer_key", IsRequired = true)]
+ public string ConsumerKey { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Consumer Secret used to sign the message.
+ /// </summary>
+ string ITamperResistantOAuthMessage.ConsumerSecret {
+ get { return this.ConsumerSecret; }
+ set { this.ConsumerSecret = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the HTTP method that will be used to transmit the message.
+ /// </summary>
+ string ITamperResistantOAuthMessage.HttpMethod {
+ get { return this.HttpMethod; }
+ set { this.HttpMethod = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the URI to the Service Provider endpoint to send this message to.
+ /// </summary>
+ Uri ITamperResistantOAuthMessage.Recipient {
+ get { return this.Recipient; }
+ set { this.Recipient = value; }
+ }
+
+ #endregion
+
+ #region ITamperResistantProtocolMessage Members
+
+ /// <summary>
+ /// Gets or sets the message signature.
+ /// </summary>
+ string ITamperResistantProtocolMessage.Signature {
+ get { return this.Signature; }
+ set { this.Signature = value; }
+ }
+
+ #endregion
+
+ #region IExpiringProtocolMessage Members
+
+ /// <summary>
+ /// Gets or sets the OAuth timestamp of the message.
+ /// </summary>
+ DateTime IExpiringProtocolMessage.UtcCreationDate {
+ get { return epoch + TimeSpan.FromSeconds(this.timestamp); }
+ set { this.timestamp = (long)(value - epoch).TotalSeconds; }
+ }
+
+ #endregion
+
+ #region IReplayProtectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets or sets the message nonce used for replay detection.
+ /// </summary>
+ [MessagePart("oauth_nonce", IsRequired = true)]
+ string IReplayProtectedProtocolMessage.Nonce { get; set; }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the signature method used to sign the request.
+ /// </summary>
+ [MessagePart("oauth_signature_method", IsRequired = true)]
+ protected string SignatureMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Token Secret used to sign the message.
+ /// </summary>
+ protected string TokenSecret { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Consumer Secret used to sign the message.
+ /// </summary>
+ protected string ConsumerSecret { get; set; }
+
+ /// <summary>
+ /// Gets or sets the HTTP method that will be used to transmit the message.
+ /// </summary>
+ protected string HttpMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the message signature.
+ /// </summary>
+ [MessagePart("oauth_signature", IsRequired = true)]
+ protected string Signature { get; set; }
+
+ /// <summary>
+ /// Gets or sets the version of the protocol this message was created with.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Accessed via reflection.")]
+ [MessagePart("oauth_version", IsRequired = false)]
+ private string OAuthVersion {
+ get {
+ return Version.ToString();
+ }
+
+ set {
+ if (value != this.OAuthVersion) {
+ throw new ArgumentOutOfRangeException("value");
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
index 8b2cfc3..848b427 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
@@ -1,127 +1,127 @@
-//-----------------------------------------------------------------------
-// <copyright file="BackwardCompatibilityBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- using System;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// Provides a mechanism for Relying Parties to work with OpenID 1.0 Providers
- /// without losing claimed_id and op_endpoint data, which OpenID 2.0 Providers
- /// are required to send back with positive assertions.
- /// </summary>
- internal class BackwardCompatibilityBindingElement : IChannelBindingElement {
- /// <summary>
- /// The name of the callback parameter that stores the Provider Endpoint URL
- /// to tack onto the return_to URI.
- /// </summary>
- private static readonly string ProviderEndpointParameterName = "dnoi.op_endpoint";
-
- /// <summary>
- /// The name of the callback parameter that stores the Claimed Identifier
- /// to tack onto the return_to URI.
- /// </summary>
- private static readonly string ClaimedIdentifierParameterName = "dnoi.claimed_id";
-
- #region IChannelBindingElement Members
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// This property is set by the channel when it is first constructed.
- /// </remarks>
- public Channel Channel { get; set; }
-
- /// <summary>
- /// Gets the protection offered (if any) by this binding element.
- /// </summary>
- /// <value><see cref="MessageProtections.None"/></value>
- public MessageProtections Protection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Prepares a message for sending based on the rules of this channel binding element.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False otherwise.
- /// </returns>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- SignedResponseRequest request = message as SignedResponseRequest;
- if (request != null && request.Version.Major < 2) {
- request.AddReturnToArguments(ProviderEndpointParameterName, request.Recipient.AbsoluteUri);
-
- CheckIdRequest authRequest = request as CheckIdRequest;
- if (authRequest != null) {
- request.AddReturnToArguments(ClaimedIdentifierParameterName, authRequest.ClaimedIdentifier);
- }
-
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="message">The incoming message to process.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False if the operation did not apply to this message.
- /// </returns>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- IndirectSignedResponse response = message as IndirectSignedResponse;
- if (response != null && response.Version.Major < 2) {
- var dictionary = new MessageDictionary(response);
- var protocol = Protocol.Lookup(response.Version);
-
- // Although GetReturnToArgument may return null if the parameters are not signed,
- // the ReturnToSignatureBindingElement should have thrown an exception already
- // if this is a 1.0 OP signed response without a valid signature since 1.0 OPs
- // are not supposed to be able to send unsolicited assertions.
- // Any safe solicited assertion would include our signature, allowing us to find
- // these values.
- if (response.ProviderEndpoint == null) {
- string op_endpoint = response.GetReturnToArgument(ProviderEndpointParameterName);
- response.ProviderEndpoint = new Uri(op_endpoint);
- }
-
- PositiveAssertionResponse authResponse = response as PositiveAssertionResponse;
- if (authResponse != null) {
- if (authResponse.ClaimedIdentifier == null) {
- authResponse.ClaimedIdentifier = response.GetReturnToArgument(ClaimedIdentifierParameterName);
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="BackwardCompatibilityBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Provides a mechanism for Relying Parties to work with OpenID 1.0 Providers
+ /// without losing claimed_id and op_endpoint data, which OpenID 2.0 Providers
+ /// are required to send back with positive assertions.
+ /// </summary>
+ internal class BackwardCompatibilityBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The name of the callback parameter that stores the Provider Endpoint URL
+ /// to tack onto the return_to URI.
+ /// </summary>
+ private static readonly string ProviderEndpointParameterName = "dnoi.op_endpoint";
+
+ /// <summary>
+ /// The name of the callback parameter that stores the Claimed Identifier
+ /// to tack onto the return_to URI.
+ /// </summary>
+ private static readonly string ClaimedIdentifierParameterName = "dnoi.claimed_id";
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel { get; set; }
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public MessageProtections Protection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ SignedResponseRequest request = message as SignedResponseRequest;
+ if (request != null && request.Version.Major < 2) {
+ request.AddReturnToArguments(ProviderEndpointParameterName, request.Recipient.AbsoluteUri);
+
+ CheckIdRequest authRequest = request as CheckIdRequest;
+ if (authRequest != null) {
+ request.AddReturnToArguments(ClaimedIdentifierParameterName, authRequest.ClaimedIdentifier);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IndirectSignedResponse response = message as IndirectSignedResponse;
+ if (response != null && response.Version.Major < 2) {
+ var dictionary = new MessageDictionary(response);
+ var protocol = Protocol.Lookup(response.Version);
+
+ // Although GetReturnToArgument may return null if the parameters are not signed,
+ // the ReturnToSignatureBindingElement should have thrown an exception already
+ // if this is a 1.0 OP signed response without a valid signature since 1.0 OPs
+ // are not supposed to be able to send unsolicited assertions.
+ // Any safe solicited assertion would include our signature, allowing us to find
+ // these values.
+ if (response.ProviderEndpoint == null) {
+ string op_endpoint = response.GetReturnToArgument(ProviderEndpointParameterName);
+ response.ProviderEndpoint = new Uri(op_endpoint);
+ }
+
+ PositiveAssertionResponse authResponse = response as PositiveAssertionResponse;
+ if (authResponse != null) {
+ if (authResponse.ClaimedIdentifier == null) {
+ authResponse.ClaimedIdentifier = response.GetReturnToArgument(ClaimedIdentifierParameterName);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
index eb5dc41..1082021 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
@@ -1,153 +1,153 @@
-//-----------------------------------------------------------------------
-// <copyright file="ExtensionsBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId.Extensions;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// The binding element that serializes/deserializes OpenID extensions to/from
- /// their carrying OpenID messages.
- /// </summary>
- internal class ExtensionsBindingElement : IChannelBindingElement {
- /// <summary>
- /// Initializes a new instance of the <see cref="ExtensionsBindingElement"/> class.
- /// </summary>
- /// <param name="extensionFactory">The extension factory.</param>
- internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory) {
- ErrorUtilities.VerifyArgumentNotNull(extensionFactory, "extensionFactory");
- this.ExtensionFactory = extensionFactory;
- }
-
- #region IChannelBindingElement Members
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// This property is set by the channel when it is first constructed.
- /// </remarks>
- public Channel Channel { get; set; }
-
- /// <summary>
- /// Gets the extension factory.
- /// </summary>
- public IOpenIdExtensionFactory ExtensionFactory { get; private set; }
-
- /// <summary>
- /// Gets the protection offered (if any) by this binding element.
- /// </summary>
- /// <value><see cref="MessageProtections.None"/></value>
- public MessageProtections Protection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Prepares a message for sending based on the rules of this channel binding element.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False otherwise.
- /// </returns>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
-
- var extendableMessage = message as IProtocolMessageWithExtensions;
- if (extendableMessage != null) {
- Protocol protocol = Protocol.Lookup(message.Version);
- MessageDictionary baseMessageDictionary = new MessageDictionary(message);
-
- // We have a helper class that will do all the heavy-lifting of organizing
- // all the extensions, their aliases, and their parameters.
- var extensionManager = ExtensionArgumentsManager.CreateOutgoingExtensions(protocol);
- foreach (IExtensionMessage protocolExtension in extendableMessage.Extensions) {
- var extension = protocolExtension as IOpenIdMessageExtension;
- if (extension != null) {
- var extensionDictionary = new MessageDictionary(extension);
- extensionManager.AddExtensionArguments(extension.TypeUri, extensionDictionary);
- } else {
- Logger.WarnFormat("Unexpected extension type {0} did not implement {1}.", protocolExtension.GetType(), typeof(IOpenIdMessageExtension).Name);
- }
- }
-
- // We use a cheap trick (for now at least) to determine whether the 'openid.' prefix
- // belongs on the parameters by just looking at what other parameters do.
- // Technically, direct message responses from Provider to Relying Party are the only
- // messages that leave off the 'openid.' prefix.
- bool includeOpenIdPrefix = baseMessageDictionary.Keys.Any(key => key.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal));
-
- // Add the extension parameters to the base message for transmission.
- extendableMessage.AddExtraParameters(extensionManager.GetArgumentsToSend(includeOpenIdPrefix));
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="message">The incoming message to process.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False if the operation did not apply to this message.
- /// </returns>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- var extendableMessage = message as IProtocolMessageWithExtensions;
- if (extendableMessage != null) {
- Protocol protocol = Protocol.Lookup(message.Version);
- MessageDictionary baseMessageDictionary = new MessageDictionary(message);
-
- // We have a helper class that will do all the heavy-lifting of organizing
- // all the extensions, their aliases, and their parameters.
- var extensionManager = ExtensionArgumentsManager.CreateIncomingExtensions(baseMessageDictionary);
- foreach (string typeUri in extensionManager.GetExtensionTypeUris()) {
- var extensionData = extensionManager.GetExtensionArguments(typeUri);
-
- // Initialize this particular extension.
- IOpenIdMessageExtension extension = this.ExtensionFactory.Create(typeUri, extensionData, extendableMessage);
- if (extension != null) {
- MessageDictionary extensionDictionary = new MessageDictionary(extension);
- foreach (var pair in extensionData) {
- extensionDictionary[pair.Key] = pair.Value;
- }
-
- extendableMessage.Extensions.Add(extension);
- } else {
- Logger.WarnFormat("Extension with type URI '{0}' ignored because it is not a recognized extension.", typeUri);
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionsBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// The binding element that serializes/deserializes OpenID extensions to/from
+ /// their carrying OpenID messages.
+ /// </summary>
+ internal class ExtensionsBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExtensionsBindingElement"/> class.
+ /// </summary>
+ /// <param name="extensionFactory">The extension factory.</param>
+ internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory) {
+ ErrorUtilities.VerifyArgumentNotNull(extensionFactory, "extensionFactory");
+ this.ExtensionFactory = extensionFactory;
+ }
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel { get; set; }
+
+ /// <summary>
+ /// Gets the extension factory.
+ /// </summary>
+ public IOpenIdExtensionFactory ExtensionFactory { get; private set; }
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public MessageProtections Protection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ var extendableMessage = message as IProtocolMessageWithExtensions;
+ if (extendableMessage != null) {
+ Protocol protocol = Protocol.Lookup(message.Version);
+ MessageDictionary baseMessageDictionary = new MessageDictionary(message);
+
+ // We have a helper class that will do all the heavy-lifting of organizing
+ // all the extensions, their aliases, and their parameters.
+ var extensionManager = ExtensionArgumentsManager.CreateOutgoingExtensions(protocol);
+ foreach (IExtensionMessage protocolExtension in extendableMessage.Extensions) {
+ var extension = protocolExtension as IOpenIdMessageExtension;
+ if (extension != null) {
+ var extensionDictionary = new MessageDictionary(extension);
+ extensionManager.AddExtensionArguments(extension.TypeUri, extensionDictionary);
+ } else {
+ Logger.WarnFormat("Unexpected extension type {0} did not implement {1}.", protocolExtension.GetType(), typeof(IOpenIdMessageExtension).Name);
+ }
+ }
+
+ // We use a cheap trick (for now at least) to determine whether the 'openid.' prefix
+ // belongs on the parameters by just looking at what other parameters do.
+ // Technically, direct message responses from Provider to Relying Party are the only
+ // messages that leave off the 'openid.' prefix.
+ bool includeOpenIdPrefix = baseMessageDictionary.Keys.Any(key => key.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal));
+
+ // Add the extension parameters to the base message for transmission.
+ extendableMessage.AddExtraParameters(extensionManager.GetArgumentsToSend(includeOpenIdPrefix));
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ var extendableMessage = message as IProtocolMessageWithExtensions;
+ if (extendableMessage != null) {
+ Protocol protocol = Protocol.Lookup(message.Version);
+ MessageDictionary baseMessageDictionary = new MessageDictionary(message);
+
+ // We have a helper class that will do all the heavy-lifting of organizing
+ // all the extensions, their aliases, and their parameters.
+ var extensionManager = ExtensionArgumentsManager.CreateIncomingExtensions(baseMessageDictionary);
+ foreach (string typeUri in extensionManager.GetExtensionTypeUris()) {
+ var extensionData = extensionManager.GetExtensionArguments(typeUri);
+
+ // Initialize this particular extension.
+ IOpenIdMessageExtension extension = this.ExtensionFactory.Create(typeUri, extensionData, extendableMessage);
+ if (extension != null) {
+ MessageDictionary extensionDictionary = new MessageDictionary(extension);
+ foreach (var pair in extensionData) {
+ extensionDictionary[pair.Key] = pair.Value;
+ }
+
+ extendableMessage.Extensions.Add(extension);
+ } else {
+ Logger.WarnFormat("Extension with type URI '{0}' ignored because it is not a recognized extension.", typeUri);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs
index 9fa3d06..4bae08f 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs
@@ -1,32 +1,32 @@
-//-----------------------------------------------------------------------
-// <copyright file="IOpenIdExtensionFactory.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- using System.Collections.Generic;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// OpenID extension factory class for creating extensions based on received Type URIs.
- /// </summary>
- internal interface IOpenIdExtensionFactory {
- /// <summary>
- /// Creates a new instance of some extension based on the received extension parameters.
- /// </summary>
- /// <param name="typeUri">The type URI of the extension.</param>
- /// <param name="data">The parameters associated specifically with this extension.</param>
- /// <param name="baseMessage">The OpenID message carrying this extension.</param>
- /// <returns>
- /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes
- /// the extension described in the input parameters; <c>null</c> otherwise.
- /// </returns>
- /// <remarks>
- /// This factory method need only initialize properties in the instantiated extension object
- /// that are not bound using <see cref="MessagePartAttribute"/>.
- /// </remarks>
- IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage);
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IOpenIdExtensionFactory.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System.Collections.Generic;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// OpenID extension factory class for creating extensions based on received Type URIs.
+ /// </summary>
+ internal interface IOpenIdExtensionFactory {
+ /// <summary>
+ /// Creates a new instance of some extension based on the received extension parameters.
+ /// </summary>
+ /// <param name="typeUri">The type URI of the extension.</param>
+ /// <param name="data">The parameters associated specifically with this extension.</param>
+ /// <param name="baseMessage">The OpenID message carrying this extension.</param>
+ /// <returns>
+ /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes
+ /// the extension described in the input parameters; <c>null</c> otherwise.
+ /// </returns>
+ /// <remarks>
+ /// This factory method need only initialize properties in the instantiated extension object
+ /// that are not bound using <see cref="MessagePartAttribute"/>.
+ /// </remarks>
+ IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs
index 1702212..953eeb7 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs
@@ -1,18 +1,18 @@
-//-----------------------------------------------------------------------
-// <copyright file="IPrivateSecretStore.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- /// <summary>
- /// Provides access to and persists a private secret that is used for signing.
- /// </summary>
- public interface IPrivateSecretStore {
- /// <summary>
- /// Gets or sets a secret key that can be used for signing.
- /// </summary>
- /// <value>A 64-byte binary value, which may contain null bytes.</value>
- byte[] PrivateSecret { get; set; }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IPrivateSecretStore.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ /// <summary>
+ /// Provides access to and persists a private secret that is used for signing.
+ /// </summary>
+ public interface IPrivateSecretStore {
+ /// <summary>
+ /// Gets or sets a secret key that can be used for signing.
+ /// </summary>
+ /// <value>A 64-byte binary value, which may contain null bytes.</value>
+ byte[] PrivateSecret { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
index d42c84b..6b4a1ef 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
@@ -1,239 +1,239 @@
-//-----------------------------------------------------------------------
-// <copyright file="ReturnToNonceBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// This binding element adds a nonce to a Relying Party's outgoing
- /// authentication request when working against an OpenID 1.0 Provider
- /// in order to protect against replay attacks.
- /// </summary>
- /// <remarks>
- /// <para>In the messaging stack, this binding element looks like an ordinary
- /// transform-type of binding element rather than a protection element,
- /// due to its required order in the channel stack and that it exists
- /// only on the RP side and only on 1.0 messages.</para>
- /// </remarks>
- internal class ReturnToNonceBindingElement : IChannelBindingElement {
- /// <summary>
- /// The parameter of the callback parameter we tack onto the return_to URL
- /// to store the replay-detection nonce.
- /// </summary>
- private static readonly string NonceParameter = "dnoi.request_nonce";
-
- /// <summary>
- /// The length of the generated nonce's random part.
- /// </summary>
- private static readonly int NonceByteLength = 128 / 8; // 128-bit nonce
-
- /// <summary>
- /// The nonce store that will allow us to recall which nonces we've seen before.
- /// </summary>
- private INonceStore nonceStore;
-
- /// <summary>
- /// The standard expiration binding element used in the channel.
- /// </summary>
- private StandardExpirationBindingElement expirationElement;
-
- /// <summary>
- /// Backing field for the <see cref="Channel"/> property.
- /// </summary>
- private Channel channel;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ReturnToNonceBindingElement"/> class.
- /// </summary>
- /// <param name="nonceStore">The nonce store to use.</param>
- internal ReturnToNonceBindingElement(INonceStore nonceStore) {
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
-
- this.nonceStore = nonceStore;
- }
-
- #region IChannelBindingElement Properties
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- /// <remarks>
- /// This property is set by the channel when it is first constructed.
- /// </remarks>
- public Channel Channel {
- get {
- return this.channel;
- }
-
- set {
- if (this.channel == value) {
- return;
- }
-
- this.channel = value;
- this.expirationElement = this.channel.BindingElements.OfType<StandardExpirationBindingElement>().Single();
- }
- }
-
- /// <summary>
- /// Gets the protection offered (if any) by this binding element.
- /// </summary>
- public MessageProtections Protection {
- get { return MessageProtections.ReplayProtection; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets the maximum message age from the standard expiration binding element.
- /// </summary>
- private TimeSpan MaximumMessageAge {
- get { return this.expirationElement.MaximumMessageAge; }
- }
-
- #region IChannelBindingElement Methods
-
- /// <summary>
- /// Prepares a message for sending based on the rules of this channel binding element.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False otherwise.
- /// </returns>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- // We only add a nonce to 1.x auth requests.
- SignedResponseRequest request = message as SignedResponseRequest;
- if (request != null && request.Version.Major < 2) {
- request.AddReturnToArguments(NonceParameter, CustomNonce.NewNonce().Serialize());
-
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="message">The incoming message to process.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False if the operation did not apply to this message.
- /// </returns>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- IndirectSignedResponse response = message as IndirectSignedResponse;
- if (response != null && response.Version.Major < 2) {
- string nonceValue = response.GetReturnToArgument(NonceParameter);
- ErrorUtilities.VerifyProtocol(nonceValue != null, OpenIdStrings.UnsolicitedAssertionsNotAllowedFrom1xOPs);
-
- CustomNonce nonce = CustomNonce.Deserialize(nonceValue);
- DateTime expirationDate = nonce.CreationDateUtc + this.MaximumMessageAge;
- if (expirationDate < DateTime.UtcNow) {
- throw new ExpiredMessageException(expirationDate, message);
- }
-
- if (!this.nonceStore.StoreNonce(nonce.RandomPartAsString, nonce.CreationDateUtc)) {
- throw new ReplayedMessageException(message);
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
-
- /// <summary>
- /// A special DotNetOpenId-only nonce used by the RP when talking to 1.0 OPs in order
- /// to protect against replay attacks.
- /// </summary>
- private class CustomNonce {
- /// <summary>
- /// The random bits generated for the nonce.
- /// </summary>
- private byte[] randomPart;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CustomNonce"/> class.
- /// </summary>
- /// <param name="creationDate">The creation date of the nonce.</param>
- /// <param name="randomPart">The random bits that help make the nonce unique.</param>
- private CustomNonce(DateTime creationDate, byte[] randomPart) {
- this.CreationDateUtc = creationDate;
- this.randomPart = randomPart;
- }
-
- /// <summary>
- /// Gets the creation date.
- /// </summary>
- internal DateTime CreationDateUtc { get; private set; }
-
- /// <summary>
- /// Gets the random part of the nonce as a base64 encoded string.
- /// </summary>
- internal string RandomPartAsString {
- get { return Convert.ToBase64String(this.randomPart); }
- }
-
- /// <summary>
- /// Creates a new nonce.
- /// </summary>
- /// <returns>The newly instantiated instance.</returns>
- internal static CustomNonce NewNonce() {
- return new CustomNonce(DateTime.UtcNow, MessagingUtilities.GetCryptoRandomData(NonceByteLength));
- }
-
- /// <summary>
- /// Deserializes a nonce from the return_to parameter.
- /// </summary>
- /// <param name="value">The base64-encoded value of the nonce.</param>
- /// <returns>The instantiated and initialized nonce.</returns>
- internal static CustomNonce Deserialize(string value) {
- ErrorUtilities.VerifyNonZeroLength(value, "value");
-
- byte[] nonce = Convert.FromBase64String(value);
- DateTime creationDateUtc = new DateTime(BitConverter.ToInt64(nonce, 0), DateTimeKind.Utc);
- byte[] randomPart = new byte[NonceByteLength];
- Array.Copy(nonce, sizeof(long), randomPart, 0, NonceByteLength);
- return new CustomNonce(creationDateUtc, randomPart);
- }
-
- /// <summary>
- /// Serializes the entire nonce for adding to the return_to URL.
- /// </summary>
- /// <returns>The base64-encoded string representing the nonce.</returns>
- internal string Serialize() {
- byte[] timestamp = BitConverter.GetBytes(this.CreationDateUtc.Ticks);
- byte[] nonce = new byte[timestamp.Length + this.randomPart.Length];
- timestamp.CopyTo(nonce, 0);
- this.randomPart.CopyTo(nonce, timestamp.Length);
- string base64Nonce = Convert.ToBase64String(nonce);
- return base64Nonce;
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ReturnToNonceBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// This binding element adds a nonce to a Relying Party's outgoing
+ /// authentication request when working against an OpenID 1.0 Provider
+ /// in order to protect against replay attacks.
+ /// </summary>
+ /// <remarks>
+ /// <para>In the messaging stack, this binding element looks like an ordinary
+ /// transform-type of binding element rather than a protection element,
+ /// due to its required order in the channel stack and that it exists
+ /// only on the RP side and only on 1.0 messages.</para>
+ /// </remarks>
+ internal class ReturnToNonceBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The parameter of the callback parameter we tack onto the return_to URL
+ /// to store the replay-detection nonce.
+ /// </summary>
+ private static readonly string NonceParameter = "dnoi.request_nonce";
+
+ /// <summary>
+ /// The length of the generated nonce's random part.
+ /// </summary>
+ private static readonly int NonceByteLength = 128 / 8; // 128-bit nonce
+
+ /// <summary>
+ /// The nonce store that will allow us to recall which nonces we've seen before.
+ /// </summary>
+ private INonceStore nonceStore;
+
+ /// <summary>
+ /// The standard expiration binding element used in the channel.
+ /// </summary>
+ private StandardExpirationBindingElement expirationElement;
+
+ /// <summary>
+ /// Backing field for the <see cref="Channel"/> property.
+ /// </summary>
+ private Channel channel;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ReturnToNonceBindingElement"/> class.
+ /// </summary>
+ /// <param name="nonceStore">The nonce store to use.</param>
+ internal ReturnToNonceBindingElement(INonceStore nonceStore) {
+ ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
+
+ this.nonceStore = nonceStore;
+ }
+
+ #region IChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel {
+ get {
+ return this.channel;
+ }
+
+ set {
+ if (this.channel == value) {
+ return;
+ }
+
+ this.channel = value;
+ this.expirationElement = this.channel.BindingElements.OfType<StandardExpirationBindingElement>().Single();
+ }
+ }
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ public MessageProtections Protection {
+ get { return MessageProtections.ReplayProtection; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the maximum message age from the standard expiration binding element.
+ /// </summary>
+ private TimeSpan MaximumMessageAge {
+ get { return this.expirationElement.MaximumMessageAge; }
+ }
+
+ #region IChannelBindingElement Methods
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ // We only add a nonce to 1.x auth requests.
+ SignedResponseRequest request = message as SignedResponseRequest;
+ if (request != null && request.Version.Major < 2) {
+ request.AddReturnToArguments(NonceParameter, CustomNonce.NewNonce().Serialize());
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IndirectSignedResponse response = message as IndirectSignedResponse;
+ if (response != null && response.Version.Major < 2) {
+ string nonceValue = response.GetReturnToArgument(NonceParameter);
+ ErrorUtilities.VerifyProtocol(nonceValue != null, OpenIdStrings.UnsolicitedAssertionsNotAllowedFrom1xOPs);
+
+ CustomNonce nonce = CustomNonce.Deserialize(nonceValue);
+ DateTime expirationDate = nonce.CreationDateUtc + this.MaximumMessageAge;
+ if (expirationDate < DateTime.UtcNow) {
+ throw new ExpiredMessageException(expirationDate, message);
+ }
+
+ if (!this.nonceStore.StoreNonce(nonce.RandomPartAsString, nonce.CreationDateUtc)) {
+ throw new ReplayedMessageException(message);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// A special DotNetOpenId-only nonce used by the RP when talking to 1.0 OPs in order
+ /// to protect against replay attacks.
+ /// </summary>
+ private class CustomNonce {
+ /// <summary>
+ /// The random bits generated for the nonce.
+ /// </summary>
+ private byte[] randomPart;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CustomNonce"/> class.
+ /// </summary>
+ /// <param name="creationDate">The creation date of the nonce.</param>
+ /// <param name="randomPart">The random bits that help make the nonce unique.</param>
+ private CustomNonce(DateTime creationDate, byte[] randomPart) {
+ this.CreationDateUtc = creationDate;
+ this.randomPart = randomPart;
+ }
+
+ /// <summary>
+ /// Gets the creation date.
+ /// </summary>
+ internal DateTime CreationDateUtc { get; private set; }
+
+ /// <summary>
+ /// Gets the random part of the nonce as a base64 encoded string.
+ /// </summary>
+ internal string RandomPartAsString {
+ get { return Convert.ToBase64String(this.randomPart); }
+ }
+
+ /// <summary>
+ /// Creates a new nonce.
+ /// </summary>
+ /// <returns>The newly instantiated instance.</returns>
+ internal static CustomNonce NewNonce() {
+ return new CustomNonce(DateTime.UtcNow, MessagingUtilities.GetCryptoRandomData(NonceByteLength));
+ }
+
+ /// <summary>
+ /// Deserializes a nonce from the return_to parameter.
+ /// </summary>
+ /// <param name="value">The base64-encoded value of the nonce.</param>
+ /// <returns>The instantiated and initialized nonce.</returns>
+ internal static CustomNonce Deserialize(string value) {
+ ErrorUtilities.VerifyNonZeroLength(value, "value");
+
+ byte[] nonce = Convert.FromBase64String(value);
+ DateTime creationDateUtc = new DateTime(BitConverter.ToInt64(nonce, 0), DateTimeKind.Utc);
+ byte[] randomPart = new byte[NonceByteLength];
+ Array.Copy(nonce, sizeof(long), randomPart, 0, NonceByteLength);
+ return new CustomNonce(creationDateUtc, randomPart);
+ }
+
+ /// <summary>
+ /// Serializes the entire nonce for adding to the return_to URL.
+ /// </summary>
+ /// <returns>The base64-encoded string representing the nonce.</returns>
+ internal string Serialize() {
+ byte[] timestamp = BitConverter.GetBytes(this.CreationDateUtc.Ticks);
+ byte[] nonce = new byte[timestamp.Length + this.randomPart.Length];
+ timestamp.CopyTo(nonce, 0);
+ this.randomPart.CopyTo(nonce, timestamp.Length);
+ string base64Nonce = Convert.ToBase64String(nonce);
+ return base64Nonce;
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
index 62875f9..34f0c3e 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
@@ -1,184 +1,184 @@
-//-----------------------------------------------------------------------
-// <copyright file="ReturnToSignatureBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.Security.Cryptography;
- using System.Web;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// This binding element signs a Relying Party's openid.return_to parameter
- /// so that upon return, it can verify that it hasn't been tampered with.
- /// </summary>
- /// <remarks>
- /// <para>Since Providers can send unsolicited assertions, not all openid.return_to
- /// values will be signed. But those that are signed will be validated, and
- /// any invalid or missing signatures will cause this library to not trust
- /// the parameters in the return_to URL.</para>
- /// <para>In the messaging stack, this binding element looks like an ordinary
- /// transform-type of binding element rather than a protection element,
- /// due to its required order in the channel stack and that it doesn't sign
- /// anything except a particular message part.</para>
- /// </remarks>
- internal class ReturnToSignatureBindingElement : IChannelBindingElement {
- /// <summary>
- /// The optimal length for a private secret used for signing using the HMACSHA256 class.
- /// </summary>
- /// <remarks>
- /// The 64-byte length is optimized for highest security when used with HMACSHA256.
- /// See HMACSHA256.HMACSHA256(byte[]) documentation for more information.
- /// </remarks>
- internal static readonly int OptimalPrivateSecretLength = 64;
-
- /// <summary>
- /// The name of the callback parameter we'll tack onto the return_to value
- /// to store our signature on the return_to parameter.
- /// </summary>
- private static readonly string ReturnToSignatureParameterName = "dnoi.return_to_sig";
-
- /// <summary>
- /// The hashing algorithm used to generate the private signature on the return_to parameter.
- /// </summary>
- private HashAlgorithm signingHasher;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ReturnToSignatureBindingElement"/> class.
- /// </summary>
- /// <param name="secretStore">The secret store from which to retrieve the secret used for signing.</param>
- internal ReturnToSignatureBindingElement(IPrivateSecretStore secretStore) {
- ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
- ErrorUtilities.VerifyInternal(secretStore.PrivateSecret != null, "Private secret should have been set already.");
-
- if (secretStore.PrivateSecret.Length < OptimalPrivateSecretLength) {
- Logger.WarnFormat("For best security, the optimal length of a private signing secret is {0} bytes, but the secret we have is only {1} bytes.", OptimalPrivateSecretLength, secretStore.PrivateSecret.Length);
- }
-
- this.signingHasher = new HMACSHA256(secretStore.PrivateSecret);
- }
-
- #region IChannelBindingElement Members
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// This property is set by the channel when it is first constructed.
- /// </remarks>
- public Channel Channel { get; set; }
-
- /// <summary>
- /// Gets the protection offered (if any) by this binding element.
- /// </summary>
- /// <value><see cref="MessageProtections.None"/></value>
- public MessageProtections Protection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Prepares a message for sending based on the rules of this channel binding element.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False otherwise.
- /// </returns>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- SignedResponseRequest request = message as SignedResponseRequest;
- if (request != null) {
- string signature = this.GetReturnToSignature(request.ReturnTo);
- request.AddReturnToArguments(ReturnToSignatureParameterName, signature);
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="message">The incoming message to process.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False if the operation did not apply to this message.
- /// </returns>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- IndirectSignedResponse response = message as IndirectSignedResponse;
-
- if (response != null) {
- // We can't use response.GetReturnToArgument(string) because that relies
- // on us already having validated this signature.
- NameValueCollection returnToParameters = HttpUtility.ParseQueryString(response.ReturnTo.Query);
-
- // Set the safety flag showing whether the return_to url had a valid signature.
- string expected = this.GetReturnToSignature(response.ReturnTo);
- string actual = returnToParameters[ReturnToSignatureParameterName];
- actual = OpenIdUtilities.FixDoublyUriDecodedBase64String(actual);
- response.ReturnToParametersSignatureValidated = actual == expected;
- if (!response.ReturnToParametersSignatureValidated) {
- Logger.WarnFormat("The return_to signature failed verification.");
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
-
- /// <summary>
- /// Gets the return to signature.
- /// </summary>
- /// <param name="returnTo">The return to.</param>
- /// <returns>The generated signature.</returns>
- /// <remarks>
- /// Only the parameters in the return_to URI are signed, rather than the base URI
- /// itself, in order that OPs that might change the return_to's implicit port :80 part
- /// or other minor changes do not invalidate the signature.
- /// </remarks>
- private string GetReturnToSignature(Uri returnTo) {
- ErrorUtilities.VerifyArgumentNotNull(returnTo, "returnTo");
-
- // Assemble the dictionary to sign, taking care to remove the signature itself
- // in order to accurately reproduce the original signature (which of course didn't include
- // the signature).
- // Also we need to sort the dictionary's keys so that we sign in the same order as we did
- // the last time.
- var returnToParameters = HttpUtility.ParseQueryString(returnTo.Query).ToDictionary();
- returnToParameters.Remove(ReturnToSignatureParameterName);
- var sortedReturnToParameters = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- foreach (var pair in returnToParameters) {
- sortedReturnToParameters.Add(pair.Key, pair.Value);
- }
-
- Logger.DebugFormat("ReturnTo signed data: {0}", sortedReturnToParameters.ToStringDeferred());
-
- // Sign the parameters.
- byte[] bytesToSign = KeyValueFormEncoding.GetBytes(sortedReturnToParameters);
- byte[] signature = this.signingHasher.ComputeHash(bytesToSign);
- string signatureBase64 = Convert.ToBase64String(signature);
- return signatureBase64;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ReturnToSignatureBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Security.Cryptography;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// This binding element signs a Relying Party's openid.return_to parameter
+ /// so that upon return, it can verify that it hasn't been tampered with.
+ /// </summary>
+ /// <remarks>
+ /// <para>Since Providers can send unsolicited assertions, not all openid.return_to
+ /// values will be signed. But those that are signed will be validated, and
+ /// any invalid or missing signatures will cause this library to not trust
+ /// the parameters in the return_to URL.</para>
+ /// <para>In the messaging stack, this binding element looks like an ordinary
+ /// transform-type of binding element rather than a protection element,
+ /// due to its required order in the channel stack and that it doesn't sign
+ /// anything except a particular message part.</para>
+ /// </remarks>
+ internal class ReturnToSignatureBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The optimal length for a private secret used for signing using the HMACSHA256 class.
+ /// </summary>
+ /// <remarks>
+ /// The 64-byte length is optimized for highest security when used with HMACSHA256.
+ /// See HMACSHA256.HMACSHA256(byte[]) documentation for more information.
+ /// </remarks>
+ internal static readonly int OptimalPrivateSecretLength = 64;
+
+ /// <summary>
+ /// The name of the callback parameter we'll tack onto the return_to value
+ /// to store our signature on the return_to parameter.
+ /// </summary>
+ private static readonly string ReturnToSignatureParameterName = "dnoi.return_to_sig";
+
+ /// <summary>
+ /// The hashing algorithm used to generate the private signature on the return_to parameter.
+ /// </summary>
+ private HashAlgorithm signingHasher;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ReturnToSignatureBindingElement"/> class.
+ /// </summary>
+ /// <param name="secretStore">The secret store from which to retrieve the secret used for signing.</param>
+ internal ReturnToSignatureBindingElement(IPrivateSecretStore secretStore) {
+ ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
+ ErrorUtilities.VerifyInternal(secretStore.PrivateSecret != null, "Private secret should have been set already.");
+
+ if (secretStore.PrivateSecret.Length < OptimalPrivateSecretLength) {
+ Logger.WarnFormat("For best security, the optimal length of a private signing secret is {0} bytes, but the secret we have is only {1} bytes.", OptimalPrivateSecretLength, secretStore.PrivateSecret.Length);
+ }
+
+ this.signingHasher = new HMACSHA256(secretStore.PrivateSecret);
+ }
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel { get; set; }
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public MessageProtections Protection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ SignedResponseRequest request = message as SignedResponseRequest;
+ if (request != null) {
+ string signature = this.GetReturnToSignature(request.ReturnTo);
+ request.AddReturnToArguments(ReturnToSignatureParameterName, signature);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IndirectSignedResponse response = message as IndirectSignedResponse;
+
+ if (response != null) {
+ // We can't use response.GetReturnToArgument(string) because that relies
+ // on us already having validated this signature.
+ NameValueCollection returnToParameters = HttpUtility.ParseQueryString(response.ReturnTo.Query);
+
+ // Set the safety flag showing whether the return_to url had a valid signature.
+ string expected = this.GetReturnToSignature(response.ReturnTo);
+ string actual = returnToParameters[ReturnToSignatureParameterName];
+ actual = OpenIdUtilities.FixDoublyUriDecodedBase64String(actual);
+ response.ReturnToParametersSignatureValidated = actual == expected;
+ if (!response.ReturnToParametersSignatureValidated) {
+ Logger.WarnFormat("The return_to signature failed verification.");
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the return to signature.
+ /// </summary>
+ /// <param name="returnTo">The return to.</param>
+ /// <returns>The generated signature.</returns>
+ /// <remarks>
+ /// Only the parameters in the return_to URI are signed, rather than the base URI
+ /// itself, in order that OPs that might change the return_to's implicit port :80 part
+ /// or other minor changes do not invalidate the signature.
+ /// </remarks>
+ private string GetReturnToSignature(Uri returnTo) {
+ ErrorUtilities.VerifyArgumentNotNull(returnTo, "returnTo");
+
+ // Assemble the dictionary to sign, taking care to remove the signature itself
+ // in order to accurately reproduce the original signature (which of course didn't include
+ // the signature).
+ // Also we need to sort the dictionary's keys so that we sign in the same order as we did
+ // the last time.
+ var returnToParameters = HttpUtility.ParseQueryString(returnTo.Query).ToDictionary();
+ returnToParameters.Remove(ReturnToSignatureParameterName);
+ var sortedReturnToParameters = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ foreach (var pair in returnToParameters) {
+ sortedReturnToParameters.Add(pair.Key, pair.Value);
+ }
+
+ Logger.DebugFormat("ReturnTo signed data: {0}", sortedReturnToParameters.ToStringDeferred());
+
+ // Sign the parameters.
+ byte[] bytesToSign = KeyValueFormEncoding.GetBytes(sortedReturnToParameters);
+ byte[] signature = this.signingHasher.ComputeHash(bytesToSign);
+ string signatureBase64 = Convert.ToBase64String(signature);
+ return signatureBase64;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
index b691a0b..6109052 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
@@ -1,274 +1,274 @@
-//-----------------------------------------------------------------------
-// <copyright file="SigningBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Net.Security;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// Signs and verifies authentication assertions.
- /// </summary>
- internal class SigningBindingElement : IChannelBindingElement {
- /// <summary>
- /// The association store used by Relying Parties to look up the secrets needed for signing.
- /// </summary>
- private readonly IAssociationStore<Uri> rpAssociations;
-
- /// <summary>
- /// The association store used by Providers to look up the secrets needed for signing.
- /// </summary>
- private readonly IAssociationStore<AssociationRelyingPartyType> opAssociations;
-
- /// <summary>
- /// Initializes a new instance of the SigningBindingElement class for use by a Relying Party.
- /// </summary>
- /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
- internal SigningBindingElement(IAssociationStore<Uri> associations) {
- ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
-
- this.rpAssociations = associations;
- }
-
- /// <summary>
- /// Initializes a new instance of the SigningBindingElement class for use by a Provider.
- /// </summary>
- /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
- internal SigningBindingElement(IAssociationStore<AssociationRelyingPartyType> associations) {
- ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
-
- this.opAssociations = associations;
- }
-
- #region IChannelBindingElement Properties
-
- /// <summary>
- /// Gets the protection offered (if any) by this binding element.
- /// </summary>
- /// <value><see cref="MessageProtections.TamperProtection"/></value>
- public MessageProtections Protection {
- get { return MessageProtections.TamperProtection; }
- }
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- public Channel Channel { get; set; }
-
- /// <summary>
- /// Prepares a message for sending based on the rules of this channel binding element.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False otherwise.
- /// </returns>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- var signedMessage = message as ITamperResistantOpenIdMessage;
- if (signedMessage != null) {
- Logger.DebugFormat("Signing {0} message.", message.GetType().Name);
- Association association = this.GetAssociation(signedMessage);
- signedMessage.AssociationHandle = association.Handle;
- signedMessage.SignedParameterOrder = GetSignedParameterOrder(signedMessage);
- signedMessage.Signature = this.GetSignature(signedMessage, association);
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="message">The incoming message to process.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False if the operation did not apply to this message.
- /// </returns>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- var signedMessage = message as ITamperResistantOpenIdMessage;
- if (signedMessage != null) {
- Logger.DebugFormat("Verifying incoming {0} message signature of: {1}", message.GetType().Name, signedMessage.Signature);
-
- EnsureParametersRequiringSignatureAreSigned(signedMessage);
-
- Association association = this.GetSpecificAssociation(signedMessage);
- if (association != null) {
- string signature = this.GetSignature(signedMessage, association);
- if (!string.Equals(signedMessage.Signature, signature, StringComparison.Ordinal)) {
- Logger.Error("Signature verification failed.");
- throw new InvalidSignatureException(message);
- }
- } else {
- ErrorUtilities.VerifyInternal(this.Channel != null, "Cannot verify private association signature because we don't have a channel.");
-
- // We did not recognize the association the provider used to sign the message.
- // Ask the provider to check the signature then.
- var checkSignatureRequest = new CheckAuthenticationRequest((IndirectSignedResponse)signedMessage);
- var checkSignatureResponse = this.Channel.Request<CheckAuthenticationResponse>(checkSignatureRequest);
- if (!checkSignatureResponse.IsValid) {
- Logger.Error("Provider reports signature verification failed.");
- throw new InvalidSignatureException(message);
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
-
- /// <summary>
- /// Ensures that all message parameters that must be signed are in fact included
- /// in the signature.
- /// </summary>
- /// <param name="signedMessage">The signed message.</param>
- private static void EnsureParametersRequiringSignatureAreSigned(ITamperResistantOpenIdMessage signedMessage) {
- // Verify that the signed parameter order includes the mandated fields.
- // We do this in such a way that derived classes that add mandated fields automatically
- // get included in the list of checked parameters.
- Protocol protocol = Protocol.Lookup(signedMessage.Version);
- var partsRequiringProtection = from part in MessageDescription.Get(signedMessage.GetType(), signedMessage.Version).Mapping.Values
- where part.RequiredProtection != ProtectionLevel.None
- select part.Name;
- ErrorUtilities.VerifyInternal(partsRequiringProtection.All(name => name.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal)), "Signing only works when the parameters start with the 'openid.' prefix.");
- string[] signedParts = signedMessage.SignedParameterOrder.Split(',');
- var unsignedParts = from partName in partsRequiringProtection
- where !signedParts.Contains(partName.Substring(protocol.openid.Prefix.Length))
- select partName;
- ErrorUtilities.VerifyProtocol(!unsignedParts.Any(), OpenIdStrings.SignatureDoesNotIncludeMandatoryParts, string.Join(", ", unsignedParts.ToArray()));
- }
-
- /// <summary>
- /// Gets the value to use for the openid.signed parameter.
- /// </summary>
- /// <param name="signedMessage">The signable message.</param>
- /// <returns>
- /// A comma-delimited list of parameter names, omitting the 'openid.' prefix, that determines
- /// the inclusion and order of message parts that will be signed.
- /// </returns>
- private static string GetSignedParameterOrder(ITamperResistantOpenIdMessage signedMessage) {
- ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
-
- MessageDescription description = MessageDescription.Get(signedMessage.GetType(), signedMessage.Version);
- var signedParts = from part in description.Mapping.Values
- where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0
- && part.GetValue(signedMessage) != null
- select part.Name;
- string prefix = Protocol.V20.openid.Prefix;
- Debug.Assert(signedParts.All(name => name.StartsWith(prefix, StringComparison.Ordinal)), "All signed message parts must start with 'openid.'.");
- int skipLength = prefix.Length;
- string signedFields = string.Join(",", signedParts.Select(name => name.Substring(skipLength)).ToArray());
- return signedFields;
- }
-
- /// <summary>
- /// Calculates the signature for a given message.
- /// </summary>
- /// <param name="signedMessage">The message to sign or verify.</param>
- /// <param name="association">The association to use to sign the message.</param>
- /// <returns>The calculated signature of the method.</returns>
- private string GetSignature(ITamperResistantOpenIdMessage signedMessage, Association association) {
- ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
- ErrorUtilities.VerifyNonZeroLength(signedMessage.SignedParameterOrder, "signedMessage.SignedParameterOrder");
- ErrorUtilities.VerifyArgumentNotNull(association, "association");
-
- // Prepare the parts to sign, taking care to replace an openid.mode value
- // of check_authentication with its original id_res so the signature matches.
- Protocol protocol = Protocol.Lookup(signedMessage.Version);
- MessageDictionary dictionary = new MessageDictionary(signedMessage);
- var parametersToSign = from name in signedMessage.SignedParameterOrder.Split(',')
- let prefixedName = Protocol.V20.openid.Prefix + name
- select new KeyValuePair<string, string>(prefixedName, dictionary[prefixedName]);
-
- byte[] dataToSign = KeyValueFormEncoding.GetBytes(parametersToSign);
- return Convert.ToBase64String(association.Sign(dataToSign));
- }
-
- /// <summary>
- /// Gets the association to use to sign or verify a message.
- /// </summary>
- /// <param name="signedMessage">The message to sign or verify.</param>
- /// <returns>The association to use to sign or verify the message.</returns>
- private Association GetAssociation(ITamperResistantOpenIdMessage signedMessage) {
- if (this.rpAssociations != null) {
- // We're on a Relying Party verifying a signature.
- IDirectedProtocolMessage directedMessage = (IDirectedProtocolMessage)signedMessage;
- return this.rpAssociations.GetAssociation(directedMessage.Recipient, signedMessage.AssociationHandle);
- } else {
- // We're on a Provider to either sign (smart/dumb) or verify a dumb signature.
- return this.GetSpecificAssociation(signedMessage) ?? this.GetDumbAssociationForSigning();
- }
- }
-
- /// <summary>
- /// Gets a specific association referenced in a given message's association handle.
- /// </summary>
- /// <param name="signedMessage">The signed message whose association handle should be used to lookup the association to return.</param>
- /// <returns>The referenced association; or <c>null</c> if such an association cannot be found.</returns>
- /// <remarks>
- /// If the association handle set in the message does not match any valid association,
- /// the association handle property is cleared, and the
- /// <see cref="ITamperResistantOpenIdMessage.InvalidateHandle"/> property is set to the
- /// handle that could not be found.
- /// </remarks>
- private Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) {
- Association association = null;
-
- if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) {
- if (this.opAssociations != null) {
- // Since we have an association handle, we're either signing with a smart association,
- // or verifying a dumb one.
- bool signing = string.IsNullOrEmpty(signedMessage.Signature);
- ErrorUtilities.VerifyInternal(signing == (signedMessage is PositiveAssertionResponse), "Ooops... somehow we think we're signing a message that isn't a positive assertion!");
- AssociationRelyingPartyType type = signing ? AssociationRelyingPartyType.Smart : AssociationRelyingPartyType.Dumb;
- association = this.opAssociations.GetAssociation(type, signedMessage.AssociationHandle);
- if (association == null) {
- // There was no valid association with the requested handle.
- // Let's tell the RP to forget about that association.
- signedMessage.InvalidateHandle = signedMessage.AssociationHandle;
- signedMessage.AssociationHandle = null;
- }
- } else {
- Uri providerEndpoint = ((PositiveAssertionResponse)signedMessage).ProviderEndpoint;
- association = this.rpAssociations.GetAssociation(providerEndpoint, signedMessage.AssociationHandle);
- }
- }
-
- return association;
- }
-
- /// <summary>
- /// Gets a private Provider association used for signing messages in "dumb" mode.
- /// </summary>
- /// <returns>An existing or newly created association.</returns>
- private Association GetDumbAssociationForSigning() {
- // If no assoc_handle was given or it was invalid, the only thing
- // left to do is sign a message using a 'dumb' mode association.
- Protocol protocol = Protocol.Default;
- Association association = this.opAssociations.GetAssociation(AssociationRelyingPartyType.Dumb);
- if (association == null) {
- association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.HMAC_SHA256, AssociationRelyingPartyType.Dumb);
- this.opAssociations.StoreAssociation(AssociationRelyingPartyType.Dumb, association);
- }
-
- return association;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="SigningBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Net.Security;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Signs and verifies authentication assertions.
+ /// </summary>
+ internal class SigningBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The association store used by Relying Parties to look up the secrets needed for signing.
+ /// </summary>
+ private readonly IAssociationStore<Uri> rpAssociations;
+
+ /// <summary>
+ /// The association store used by Providers to look up the secrets needed for signing.
+ /// </summary>
+ private readonly IAssociationStore<AssociationRelyingPartyType> opAssociations;
+
+ /// <summary>
+ /// Initializes a new instance of the SigningBindingElement class for use by a Relying Party.
+ /// </summary>
+ /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
+ internal SigningBindingElement(IAssociationStore<Uri> associations) {
+ ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
+
+ this.rpAssociations = associations;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the SigningBindingElement class for use by a Provider.
+ /// </summary>
+ /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
+ internal SigningBindingElement(IAssociationStore<AssociationRelyingPartyType> associations) {
+ ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
+
+ this.opAssociations = associations;
+ }
+
+ #region IChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.TamperProtection"/></value>
+ public MessageProtections Protection {
+ get { return MessageProtections.TamperProtection; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ public Channel Channel { get; set; }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ var signedMessage = message as ITamperResistantOpenIdMessage;
+ if (signedMessage != null) {
+ Logger.DebugFormat("Signing {0} message.", message.GetType().Name);
+ Association association = this.GetAssociation(signedMessage);
+ signedMessage.AssociationHandle = association.Handle;
+ signedMessage.SignedParameterOrder = GetSignedParameterOrder(signedMessage);
+ signedMessage.Signature = this.GetSignature(signedMessage, association);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ var signedMessage = message as ITamperResistantOpenIdMessage;
+ if (signedMessage != null) {
+ Logger.DebugFormat("Verifying incoming {0} message signature of: {1}", message.GetType().Name, signedMessage.Signature);
+
+ EnsureParametersRequiringSignatureAreSigned(signedMessage);
+
+ Association association = this.GetSpecificAssociation(signedMessage);
+ if (association != null) {
+ string signature = this.GetSignature(signedMessage, association);
+ if (!string.Equals(signedMessage.Signature, signature, StringComparison.Ordinal)) {
+ Logger.Error("Signature verification failed.");
+ throw new InvalidSignatureException(message);
+ }
+ } else {
+ ErrorUtilities.VerifyInternal(this.Channel != null, "Cannot verify private association signature because we don't have a channel.");
+
+ // We did not recognize the association the provider used to sign the message.
+ // Ask the provider to check the signature then.
+ var checkSignatureRequest = new CheckAuthenticationRequest((IndirectSignedResponse)signedMessage);
+ var checkSignatureResponse = this.Channel.Request<CheckAuthenticationResponse>(checkSignatureRequest);
+ if (!checkSignatureResponse.IsValid) {
+ Logger.Error("Provider reports signature verification failed.");
+ throw new InvalidSignatureException(message);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Ensures that all message parameters that must be signed are in fact included
+ /// in the signature.
+ /// </summary>
+ /// <param name="signedMessage">The signed message.</param>
+ private static void EnsureParametersRequiringSignatureAreSigned(ITamperResistantOpenIdMessage signedMessage) {
+ // Verify that the signed parameter order includes the mandated fields.
+ // We do this in such a way that derived classes that add mandated fields automatically
+ // get included in the list of checked parameters.
+ Protocol protocol = Protocol.Lookup(signedMessage.Version);
+ var partsRequiringProtection = from part in MessageDescription.Get(signedMessage.GetType(), signedMessage.Version).Mapping.Values
+ where part.RequiredProtection != ProtectionLevel.None
+ select part.Name;
+ ErrorUtilities.VerifyInternal(partsRequiringProtection.All(name => name.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal)), "Signing only works when the parameters start with the 'openid.' prefix.");
+ string[] signedParts = signedMessage.SignedParameterOrder.Split(',');
+ var unsignedParts = from partName in partsRequiringProtection
+ where !signedParts.Contains(partName.Substring(protocol.openid.Prefix.Length))
+ select partName;
+ ErrorUtilities.VerifyProtocol(!unsignedParts.Any(), OpenIdStrings.SignatureDoesNotIncludeMandatoryParts, string.Join(", ", unsignedParts.ToArray()));
+ }
+
+ /// <summary>
+ /// Gets the value to use for the openid.signed parameter.
+ /// </summary>
+ /// <param name="signedMessage">The signable message.</param>
+ /// <returns>
+ /// A comma-delimited list of parameter names, omitting the 'openid.' prefix, that determines
+ /// the inclusion and order of message parts that will be signed.
+ /// </returns>
+ private static string GetSignedParameterOrder(ITamperResistantOpenIdMessage signedMessage) {
+ ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
+
+ MessageDescription description = MessageDescription.Get(signedMessage.GetType(), signedMessage.Version);
+ var signedParts = from part in description.Mapping.Values
+ where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0
+ && part.GetValue(signedMessage) != null
+ select part.Name;
+ string prefix = Protocol.V20.openid.Prefix;
+ Debug.Assert(signedParts.All(name => name.StartsWith(prefix, StringComparison.Ordinal)), "All signed message parts must start with 'openid.'.");
+ int skipLength = prefix.Length;
+ string signedFields = string.Join(",", signedParts.Select(name => name.Substring(skipLength)).ToArray());
+ return signedFields;
+ }
+
+ /// <summary>
+ /// Calculates the signature for a given message.
+ /// </summary>
+ /// <param name="signedMessage">The message to sign or verify.</param>
+ /// <param name="association">The association to use to sign the message.</param>
+ /// <returns>The calculated signature of the method.</returns>
+ private string GetSignature(ITamperResistantOpenIdMessage signedMessage, Association association) {
+ ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
+ ErrorUtilities.VerifyNonZeroLength(signedMessage.SignedParameterOrder, "signedMessage.SignedParameterOrder");
+ ErrorUtilities.VerifyArgumentNotNull(association, "association");
+
+ // Prepare the parts to sign, taking care to replace an openid.mode value
+ // of check_authentication with its original id_res so the signature matches.
+ Protocol protocol = Protocol.Lookup(signedMessage.Version);
+ MessageDictionary dictionary = new MessageDictionary(signedMessage);
+ var parametersToSign = from name in signedMessage.SignedParameterOrder.Split(',')
+ let prefixedName = Protocol.V20.openid.Prefix + name
+ select new KeyValuePair<string, string>(prefixedName, dictionary[prefixedName]);
+
+ byte[] dataToSign = KeyValueFormEncoding.GetBytes(parametersToSign);
+ return Convert.ToBase64String(association.Sign(dataToSign));
+ }
+
+ /// <summary>
+ /// Gets the association to use to sign or verify a message.
+ /// </summary>
+ /// <param name="signedMessage">The message to sign or verify.</param>
+ /// <returns>The association to use to sign or verify the message.</returns>
+ private Association GetAssociation(ITamperResistantOpenIdMessage signedMessage) {
+ if (this.rpAssociations != null) {
+ // We're on a Relying Party verifying a signature.
+ IDirectedProtocolMessage directedMessage = (IDirectedProtocolMessage)signedMessage;
+ return this.rpAssociations.GetAssociation(directedMessage.Recipient, signedMessage.AssociationHandle);
+ } else {
+ // We're on a Provider to either sign (smart/dumb) or verify a dumb signature.
+ return this.GetSpecificAssociation(signedMessage) ?? this.GetDumbAssociationForSigning();
+ }
+ }
+
+ /// <summary>
+ /// Gets a specific association referenced in a given message's association handle.
+ /// </summary>
+ /// <param name="signedMessage">The signed message whose association handle should be used to lookup the association to return.</param>
+ /// <returns>The referenced association; or <c>null</c> if such an association cannot be found.</returns>
+ /// <remarks>
+ /// If the association handle set in the message does not match any valid association,
+ /// the association handle property is cleared, and the
+ /// <see cref="ITamperResistantOpenIdMessage.InvalidateHandle"/> property is set to the
+ /// handle that could not be found.
+ /// </remarks>
+ private Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) {
+ Association association = null;
+
+ if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) {
+ if (this.opAssociations != null) {
+ // Since we have an association handle, we're either signing with a smart association,
+ // or verifying a dumb one.
+ bool signing = string.IsNullOrEmpty(signedMessage.Signature);
+ ErrorUtilities.VerifyInternal(signing == (signedMessage is PositiveAssertionResponse), "Ooops... somehow we think we're signing a message that isn't a positive assertion!");
+ AssociationRelyingPartyType type = signing ? AssociationRelyingPartyType.Smart : AssociationRelyingPartyType.Dumb;
+ association = this.opAssociations.GetAssociation(type, signedMessage.AssociationHandle);
+ if (association == null) {
+ // There was no valid association with the requested handle.
+ // Let's tell the RP to forget about that association.
+ signedMessage.InvalidateHandle = signedMessage.AssociationHandle;
+ signedMessage.AssociationHandle = null;
+ }
+ } else {
+ Uri providerEndpoint = ((PositiveAssertionResponse)signedMessage).ProviderEndpoint;
+ association = this.rpAssociations.GetAssociation(providerEndpoint, signedMessage.AssociationHandle);
+ }
+ }
+
+ return association;
+ }
+
+ /// <summary>
+ /// Gets a private Provider association used for signing messages in "dumb" mode.
+ /// </summary>
+ /// <returns>An existing or newly created association.</returns>
+ private Association GetDumbAssociationForSigning() {
+ // If no assoc_handle was given or it was invalid, the only thing
+ // left to do is sign a message using a 'dumb' mode association.
+ Protocol protocol = Protocol.Default;
+ Association association = this.opAssociations.GetAssociation(AssociationRelyingPartyType.Dumb);
+ if (association == null) {
+ association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.HMAC_SHA256, AssociationRelyingPartyType.Dumb);
+ this.opAssociations.StoreAssociation(AssociationRelyingPartyType.Dumb, association);
+ }
+
+ return association;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs b/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
index ee26c3b..e16f9a4 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
@@ -1,183 +1,183 @@
-//-----------------------------------------------------------------------
-// <copyright file="AliasManager.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.Text;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// Manages a fast, two-way mapping between type URIs and their aliases.
- /// </summary>
- internal class AliasManager {
- /// <summary>
- /// The format of auto-generated aliases.
- /// </summary>
- private readonly string aliasFormat = "alias{0}";
-
- /// <summary>
- /// Tracks extension Type URIs and aliases assigned to them.
- /// </summary>
- private Dictionary<string, string> typeUriToAliasMap = new Dictionary<string, string>();
-
- /// <summary>
- /// Tracks extension aliases and Type URIs assigned to them.
- /// </summary>
- private Dictionary<string, string> aliasToTypeUriMap = new Dictionary<string, string>();
-
- /// <summary>
- /// Gets the aliases that have been set.
- /// </summary>
- public IEnumerable<string> Aliases {
- get { return this.aliasToTypeUriMap.Keys; }
- }
-
- /// <summary>
- /// Gets an alias assigned for a given Type URI. A new alias is assigned if necessary.
- /// </summary>
- /// <param name="typeUri">The type URI.</param>
- /// <returns>The alias assigned to this type URI. Never null.</returns>
- public string GetAlias(string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
- string alias;
- return this.typeUriToAliasMap.TryGetValue(typeUri, out alias) ? alias : this.AssignNewAlias(typeUri);
- }
-
- /// <summary>
- /// Sets an alias and the value that will be returned by <see cref="ResolveAlias"/>.
- /// </summary>
- /// <param name="alias">The alias.</param>
- /// <param name="typeUri">The type URI.</param>
- public void SetAlias(string alias, string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(alias, "alias");
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
- this.aliasToTypeUriMap.Add(alias, typeUri);
- this.typeUriToAliasMap.Add(typeUri, alias);
- }
-
- /// <summary>
- /// Takes a sequence of type URIs and assigns aliases for all of them.
- /// </summary>
- /// <param name="typeUris">The type URIs to create aliases for.</param>
- /// <param name="preferredTypeUriToAliases">An optional dictionary of URI/alias pairs that suggest preferred aliases to use if available for certain type URIs.</param>
- public void AssignAliases(IEnumerable<string> typeUris, IDictionary<string, string> preferredTypeUriToAliases) {
- // First go through the actually used type URIs and see which ones have matching preferred aliases.
- if (preferredTypeUriToAliases != null) {
- foreach (string typeUri in typeUris) {
- if (this.typeUriToAliasMap.ContainsKey(typeUri)) {
- // this Type URI is already mapped to an alias.
- continue;
- }
-
- string preferredAlias;
- if (preferredTypeUriToAliases.TryGetValue(typeUri, out preferredAlias) && !this.IsAliasUsed(preferredAlias)) {
- this.SetAlias(preferredAlias, typeUri);
- }
- }
- }
-
- // Now go through the whole list again and assign whatever is left now that the preferred ones
- // have gotten their picks where available.
- foreach (string typeUri in typeUris) {
- if (this.typeUriToAliasMap.ContainsKey(typeUri)) {
- // this Type URI is already mapped to an alias.
- continue;
- }
-
- this.AssignNewAlias(typeUri);
- }
- }
-
- /// <summary>
- /// Sets up aliases for any Type URIs in a dictionary that do not yet have aliases defined,
- /// and where the given preferred alias is still available.
- /// </summary>
- /// <param name="preferredTypeUriToAliases">A dictionary of type URI keys and alias values.</param>
- public void SetPreferredAliasesWhereNotSet(IDictionary<string, string> preferredTypeUriToAliases) {
- ErrorUtilities.VerifyArgumentNotNull(preferredTypeUriToAliases, "preferredTypeUriToAliases");
-
- foreach (var pair in preferredTypeUriToAliases) {
- if (this.typeUriToAliasMap.ContainsKey(pair.Key)) {
- // type URI is already mapped
- continue;
- }
-
- if (this.aliasToTypeUriMap.ContainsKey(pair.Value)) {
- // alias is already mapped
- continue;
- }
-
- // The type URI and alias are as yet unset, so go ahead and assign them.
- this.SetAlias(pair.Value, pair.Key);
- }
- }
-
- /// <summary>
- /// Gets the Type Uri encoded by a given alias.
- /// </summary>
- /// <param name="alias">The alias.</param>
- /// <returns>The Type URI.</returns>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if the given alias does not have a matching TypeURI.</exception>
- public string ResolveAlias(string alias) {
- string typeUri = this.TryResolveAlias(alias);
- if (typeUri == null) {
- throw new ArgumentOutOfRangeException("alias");
- }
- return typeUri;
- }
-
- /// <summary>
- /// Gets the Type Uri encoded by a given alias.
- /// </summary>
- /// <param name="alias">The alias.</param>
- /// <returns>The Type URI for the given alias, or null if none for that alias exist.</returns>
- public string TryResolveAlias(string alias) {
- ErrorUtilities.VerifyNonZeroLength(alias, "alias");
- string typeUri = null;
- this.aliasToTypeUriMap.TryGetValue(alias, out typeUri);
- return typeUri;
- }
-
- /// <summary>
- /// Returns a value indicating whether an alias has already been assigned to a type URI.
- /// </summary>
- /// <param name="alias">The alias in question.</param>
- /// <returns>True if the alias has already been assigned. False otherwise.</returns>
- public bool IsAliasUsed(string alias) {
- ErrorUtilities.VerifyNonZeroLength(alias, "alias");
- return this.aliasToTypeUriMap.ContainsKey(alias);
- }
-
- /// <summary>
- /// Determines whether a given TypeURI has an associated alias assigned to it.
- /// </summary>
- /// <param name="typeUri">The type URI.</param>
- /// <returns>
- /// <c>true</c> if the given type URI already has an alias assigned; <c>false</c> otherwise.
- /// </returns>
- public bool IsAliasAssignedTo(string typeUri) {
- ErrorUtilities.VerifyArgumentNotNull(typeUri, "typeUri");
- return this.typeUriToAliasMap.ContainsKey(typeUri);
- }
-
- /// <summary>
- /// Assigns a new alias to a given Type URI.
- /// </summary>
- /// <param name="typeUri">The type URI to assign a new alias to.</param>
- /// <returns>The newly generated alias.</returns>
- private string AssignNewAlias(string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
- ErrorUtilities.VerifyInternal(!this.typeUriToAliasMap.ContainsKey(typeUri), "Oops! This type URI already has an alias!");
- string alias = string.Format(CultureInfo.InvariantCulture, this.aliasFormat, this.typeUriToAliasMap.Count + 1);
- this.typeUriToAliasMap.Add(typeUri, alias);
- this.aliasToTypeUriMap.Add(alias, typeUri);
- return alias;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="AliasManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Globalization;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Manages a fast, two-way mapping between type URIs and their aliases.
+ /// </summary>
+ internal class AliasManager {
+ /// <summary>
+ /// The format of auto-generated aliases.
+ /// </summary>
+ private readonly string aliasFormat = "alias{0}";
+
+ /// <summary>
+ /// Tracks extension Type URIs and aliases assigned to them.
+ /// </summary>
+ private Dictionary<string, string> typeUriToAliasMap = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Tracks extension aliases and Type URIs assigned to them.
+ /// </summary>
+ private Dictionary<string, string> aliasToTypeUriMap = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Gets the aliases that have been set.
+ /// </summary>
+ public IEnumerable<string> Aliases {
+ get { return this.aliasToTypeUriMap.Keys; }
+ }
+
+ /// <summary>
+ /// Gets an alias assigned for a given Type URI. A new alias is assigned if necessary.
+ /// </summary>
+ /// <param name="typeUri">The type URI.</param>
+ /// <returns>The alias assigned to this type URI. Never null.</returns>
+ public string GetAlias(string typeUri) {
+ ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ string alias;
+ return this.typeUriToAliasMap.TryGetValue(typeUri, out alias) ? alias : this.AssignNewAlias(typeUri);
+ }
+
+ /// <summary>
+ /// Sets an alias and the value that will be returned by <see cref="ResolveAlias"/>.
+ /// </summary>
+ /// <param name="alias">The alias.</param>
+ /// <param name="typeUri">The type URI.</param>
+ public void SetAlias(string alias, string typeUri) {
+ ErrorUtilities.VerifyNonZeroLength(alias, "alias");
+ ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ this.aliasToTypeUriMap.Add(alias, typeUri);
+ this.typeUriToAliasMap.Add(typeUri, alias);
+ }
+
+ /// <summary>
+ /// Takes a sequence of type URIs and assigns aliases for all of them.
+ /// </summary>
+ /// <param name="typeUris">The type URIs to create aliases for.</param>
+ /// <param name="preferredTypeUriToAliases">An optional dictionary of URI/alias pairs that suggest preferred aliases to use if available for certain type URIs.</param>
+ public void AssignAliases(IEnumerable<string> typeUris, IDictionary<string, string> preferredTypeUriToAliases) {
+ // First go through the actually used type URIs and see which ones have matching preferred aliases.
+ if (preferredTypeUriToAliases != null) {
+ foreach (string typeUri in typeUris) {
+ if (this.typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ string preferredAlias;
+ if (preferredTypeUriToAliases.TryGetValue(typeUri, out preferredAlias) && !this.IsAliasUsed(preferredAlias)) {
+ this.SetAlias(preferredAlias, typeUri);
+ }
+ }
+ }
+
+ // Now go through the whole list again and assign whatever is left now that the preferred ones
+ // have gotten their picks where available.
+ foreach (string typeUri in typeUris) {
+ if (this.typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ this.AssignNewAlias(typeUri);
+ }
+ }
+
+ /// <summary>
+ /// Sets up aliases for any Type URIs in a dictionary that do not yet have aliases defined,
+ /// and where the given preferred alias is still available.
+ /// </summary>
+ /// <param name="preferredTypeUriToAliases">A dictionary of type URI keys and alias values.</param>
+ public void SetPreferredAliasesWhereNotSet(IDictionary<string, string> preferredTypeUriToAliases) {
+ ErrorUtilities.VerifyArgumentNotNull(preferredTypeUriToAliases, "preferredTypeUriToAliases");
+
+ foreach (var pair in preferredTypeUriToAliases) {
+ if (this.typeUriToAliasMap.ContainsKey(pair.Key)) {
+ // type URI is already mapped
+ continue;
+ }
+
+ if (this.aliasToTypeUriMap.ContainsKey(pair.Value)) {
+ // alias is already mapped
+ continue;
+ }
+
+ // The type URI and alias are as yet unset, so go ahead and assign them.
+ this.SetAlias(pair.Value, pair.Key);
+ }
+ }
+
+ /// <summary>
+ /// Gets the Type Uri encoded by a given alias.
+ /// </summary>
+ /// <param name="alias">The alias.</param>
+ /// <returns>The Type URI.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">Thrown if the given alias does not have a matching TypeURI.</exception>
+ public string ResolveAlias(string alias) {
+ string typeUri = this.TryResolveAlias(alias);
+ if (typeUri == null) {
+ throw new ArgumentOutOfRangeException("alias");
+ }
+ return typeUri;
+ }
+
+ /// <summary>
+ /// Gets the Type Uri encoded by a given alias.
+ /// </summary>
+ /// <param name="alias">The alias.</param>
+ /// <returns>The Type URI for the given alias, or null if none for that alias exist.</returns>
+ public string TryResolveAlias(string alias) {
+ ErrorUtilities.VerifyNonZeroLength(alias, "alias");
+ string typeUri = null;
+ this.aliasToTypeUriMap.TryGetValue(alias, out typeUri);
+ return typeUri;
+ }
+
+ /// <summary>
+ /// Returns a value indicating whether an alias has already been assigned to a type URI.
+ /// </summary>
+ /// <param name="alias">The alias in question.</param>
+ /// <returns>True if the alias has already been assigned. False otherwise.</returns>
+ public bool IsAliasUsed(string alias) {
+ ErrorUtilities.VerifyNonZeroLength(alias, "alias");
+ return this.aliasToTypeUriMap.ContainsKey(alias);
+ }
+
+ /// <summary>
+ /// Determines whether a given TypeURI has an associated alias assigned to it.
+ /// </summary>
+ /// <param name="typeUri">The type URI.</param>
+ /// <returns>
+ /// <c>true</c> if the given type URI already has an alias assigned; <c>false</c> otherwise.
+ /// </returns>
+ public bool IsAliasAssignedTo(string typeUri) {
+ ErrorUtilities.VerifyArgumentNotNull(typeUri, "typeUri");
+ return this.typeUriToAliasMap.ContainsKey(typeUri);
+ }
+
+ /// <summary>
+ /// Assigns a new alias to a given Type URI.
+ /// </summary>
+ /// <param name="typeUri">The type URI to assign a new alias to.</param>
+ /// <returns>The newly generated alias.</returns>
+ private string AssignNewAlias(string typeUri) {
+ ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ ErrorUtilities.VerifyInternal(!this.typeUriToAliasMap.ContainsKey(typeUri), "Oops! This type URI already has an alias!");
+ string alias = string.Format(CultureInfo.InvariantCulture, this.aliasFormat, this.typeUriToAliasMap.Count + 1);
+ this.typeUriToAliasMap.Add(typeUri, alias);
+ this.aliasToTypeUriMap.Add(alias, typeUri);
+ return alias;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
index 5c0151d..3099777 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
@@ -1,223 +1,223 @@
-//-----------------------------------------------------------------------
-// <copyright file="ExtensionArgumentsManager.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Text;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// Manages the processing and construction of OpenID extensions parts.
- /// </summary>
- internal class ExtensionArgumentsManager {
- /// <summary>
- /// This contains a set of aliases that we must be willing to implicitly
- /// match to namespaces for backward compatibility with other OpenID libraries.
- /// </summary>
- private static readonly Dictionary<string, string> typeUriToAliasAffinity = new Dictionary<string, string> {
- { Extensions.SimpleRegistration.Constants.sreg_ns, Extensions.SimpleRegistration.Constants.sreg_compatibility_alias },
- // TODO: re-enable these lines.
- ////{ Extensions.ProviderAuthenticationPolicy.Constants.TypeUri, Extensions.ProviderAuthenticationPolicy.Constants.pape_compatibility_alias },
- };
-
- /// <summary>
- /// The version of OpenID that the message is using.
- /// </summary>
- private Protocol protocol;
-
- /// <summary>
- /// Whether extensions are being read or written.
- /// </summary>
- private bool isReadMode;
-
- /// <summary>
- /// The alias manager that will track Type URI to alias mappings.
- /// </summary>
- private AliasManager aliasManager = new AliasManager();
-
- /// <summary>
- /// A complex dictionary where the key is the Type URI of the extension,
- /// and the value is another dictionary of the name/value args of the extension.
- /// </summary>
- private Dictionary<string, IDictionary<string, string>> extensions = new Dictionary<string, IDictionary<string, string>>();
-
- /// <summary>
- /// Prevents a default instance of the <see cref="ExtensionArgumentsManager"/> class from being created.
- /// </summary>
- private ExtensionArgumentsManager() { }
-
- /// <summary>
- /// Creates a <see cref="ExtensionArgumentsManager"/> instance to process incoming extensions.
- /// </summary>
- /// <param name="query">The parameters in the OpenID message.</param>
- /// <returns>The newly created instance of <see cref="ExtensionArgumentsManager"/>.</returns>
- public static ExtensionArgumentsManager CreateIncomingExtensions(IDictionary<string, string> query) {
- ErrorUtilities.VerifyArgumentNotNull(query, "query");
- var mgr = new ExtensionArgumentsManager();
- mgr.protocol = Protocol.Detect(query);
- mgr.isReadMode = true;
- string aliasPrefix = mgr.protocol.openid.ns + ".";
-
- // First pass looks for namespace aliases
- foreach (var pair in query) {
- if (pair.Key.StartsWith(aliasPrefix, StringComparison.Ordinal)) {
- mgr.aliasManager.SetAlias(pair.Key.Substring(aliasPrefix.Length), pair.Value);
- }
- }
-
- // For backwards compatibility, add certain aliases if they aren't defined.
- foreach (var pair in typeUriToAliasAffinity) {
- if (!mgr.aliasManager.IsAliasAssignedTo(pair.Key) &&
- !mgr.aliasManager.IsAliasUsed(pair.Value)) {
- mgr.aliasManager.SetAlias(pair.Value, pair.Key);
- }
- }
-
- // Second pass looks for extensions using those aliases
- foreach (var pair in query) {
- if (!pair.Key.StartsWith(mgr.protocol.openid.Prefix, StringComparison.Ordinal)) {
- continue;
- }
- string possibleAlias = pair.Key.Substring(mgr.protocol.openid.Prefix.Length);
- int periodIndex = possibleAlias.IndexOf(".", StringComparison.Ordinal);
- if (periodIndex >= 0) {
- possibleAlias = possibleAlias.Substring(0, periodIndex);
- }
- string typeUri;
- if ((typeUri = mgr.aliasManager.TryResolveAlias(possibleAlias)) != null) {
- if (!mgr.extensions.ContainsKey(typeUri)) {
- mgr.extensions[typeUri] = new Dictionary<string, string>();
- }
- string key = periodIndex >= 0 ? pair.Key.Substring(mgr.protocol.openid.Prefix.Length + possibleAlias.Length + 1) : string.Empty;
- mgr.extensions[typeUri].Add(key, pair.Value);
- }
- }
- return mgr;
- }
-
- /// <summary>
- /// Creates a <see cref="ExtensionArgumentsManager"/> instance to prepare outgoing extensions.
- /// </summary>
- /// <param name="protocol">The protocol version used for the outgoing message.</param>
- /// <returns>
- /// The newly created instance of <see cref="ExtensionArgumentsManager"/>.
- /// </returns>
- public static ExtensionArgumentsManager CreateOutgoingExtensions(Protocol protocol) {
- var mgr = new ExtensionArgumentsManager();
- mgr.protocol = protocol;
-
- // Affinity for certain alias for backwards compatibility
- foreach (var pair in typeUriToAliasAffinity) {
- mgr.aliasManager.SetAlias(pair.Value, pair.Key);
- }
- return mgr;
- }
-
- /// <summary>
- /// Gets the actual arguments to add to a querystring or other response,
- /// where type URI, alias, and actual key/values are all defined.
- /// </summary>
- /// <param name="includeOpenIdPrefix">
- /// <c>true</c> if the generated parameter names should include the 'openid.' prefix.
- /// This should be <c>true</c> for all but direct response messages.
- /// </param>
- /// <returns>A dictionary of key=value pairs to add to the message to carry the extension.</returns>
- public IDictionary<string, string> GetArgumentsToSend(bool includeOpenIdPrefix) {
- if (this.isReadMode) {
- throw new InvalidOperationException();
- }
- Dictionary<string, string> args = new Dictionary<string, string>();
- foreach (var typeUriAndExtension in this.extensions) {
- string typeUri = typeUriAndExtension.Key;
- var extensionArgs = typeUriAndExtension.Value;
- if (extensionArgs.Count == 0) {
- continue;
- }
- string alias = this.aliasManager.GetAlias(typeUri);
-
- // send out the alias declaration
- string openidPrefix = includeOpenIdPrefix ? this.protocol.openid.Prefix : string.Empty;
- args.Add(openidPrefix + this.protocol.openidnp.ns + "." + alias, typeUri);
- string prefix = openidPrefix + alias;
- foreach (var pair in extensionArgs) {
- string key = prefix;
- if (pair.Key.Length > 0) {
- key += "." + pair.Key;
- }
- args.Add(key, pair.Value);
- }
- }
- return args;
- }
-
- /// <summary>
- /// Adds query parameters for OpenID extensions to the request directed
- /// at the OpenID provider.
- /// </summary>
- /// <param name="extensionTypeUri">The extension type URI.</param>
- /// <param name="arguments">The arguments for this extension to add to the message.</param>
- public void AddExtensionArguments(string extensionTypeUri, IDictionary<string, string> arguments) {
- if (this.isReadMode) {
- throw new InvalidOperationException();
- }
- ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
- ErrorUtilities.VerifyArgumentNotNull(arguments, "arguments");
- if (arguments.Count == 0) {
- return;
- }
-
- IDictionary<string, string> extensionArgs;
- if (!this.extensions.TryGetValue(extensionTypeUri, out extensionArgs)) {
- this.extensions.Add(extensionTypeUri, extensionArgs = new Dictionary<string, string>(arguments.Count));
- }
-
- ErrorUtilities.VerifyProtocol(extensionArgs.Count == 0, OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extensionTypeUri);
- foreach (var pair in arguments) {
- extensionArgs.Add(pair.Key, pair.Value);
- }
- }
-
- /// <summary>
- /// Gets the fields carried by a given OpenId extension.
- /// </summary>
- /// <param name="extensionTypeUri">The type URI of the extension whose fields are being queried for.</param>
- /// <returns>
- /// The fields included in the given extension, or null if the extension is not present.
- /// </returns>
- public IDictionary<string, string> GetExtensionArguments(string extensionTypeUri) {
- ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
- if (!this.isReadMode) {
- throw new InvalidOperationException();
- }
-
- IDictionary<string, string> extensionArgs;
- this.extensions.TryGetValue(extensionTypeUri, out extensionArgs);
- return extensionArgs;
- }
-
- /// <summary>
- /// Gets whether any arguments for a given extension are present.
- /// </summary>
- /// <param name="extensionTypeUri">The extension Type URI in question.</param>
- /// <returns><c>true</c> if this extension is present; <c>false</c> otherwise.</returns>
- public bool ContainsExtension(string extensionTypeUri) {
- if (!this.isReadMode) {
- throw new InvalidOperationException();
- }
- return this.extensions.ContainsKey(extensionTypeUri);
- }
-
- /// <summary>
- /// Gets the type URIs of all discovered extensions in the message.
- /// </summary>
- /// <returns>A sequence of the type URIs.</returns>
- public IEnumerable<string> GetExtensionTypeUris() {
- return this.extensions.Keys;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionArgumentsManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Manages the processing and construction of OpenID extensions parts.
+ /// </summary>
+ internal class ExtensionArgumentsManager {
+ /// <summary>
+ /// This contains a set of aliases that we must be willing to implicitly
+ /// match to namespaces for backward compatibility with other OpenID libraries.
+ /// </summary>
+ private static readonly Dictionary<string, string> typeUriToAliasAffinity = new Dictionary<string, string> {
+ { Extensions.SimpleRegistration.Constants.sreg_ns, Extensions.SimpleRegistration.Constants.sreg_compatibility_alias },
+ // TODO: re-enable these lines.
+ ////{ Extensions.ProviderAuthenticationPolicy.Constants.TypeUri, Extensions.ProviderAuthenticationPolicy.Constants.pape_compatibility_alias },
+ };
+
+ /// <summary>
+ /// The version of OpenID that the message is using.
+ /// </summary>
+ private Protocol protocol;
+
+ /// <summary>
+ /// Whether extensions are being read or written.
+ /// </summary>
+ private bool isReadMode;
+
+ /// <summary>
+ /// The alias manager that will track Type URI to alias mappings.
+ /// </summary>
+ private AliasManager aliasManager = new AliasManager();
+
+ /// <summary>
+ /// A complex dictionary where the key is the Type URI of the extension,
+ /// and the value is another dictionary of the name/value args of the extension.
+ /// </summary>
+ private Dictionary<string, IDictionary<string, string>> extensions = new Dictionary<string, IDictionary<string, string>>();
+
+ /// <summary>
+ /// Prevents a default instance of the <see cref="ExtensionArgumentsManager"/> class from being created.
+ /// </summary>
+ private ExtensionArgumentsManager() { }
+
+ /// <summary>
+ /// Creates a <see cref="ExtensionArgumentsManager"/> instance to process incoming extensions.
+ /// </summary>
+ /// <param name="query">The parameters in the OpenID message.</param>
+ /// <returns>The newly created instance of <see cref="ExtensionArgumentsManager"/>.</returns>
+ public static ExtensionArgumentsManager CreateIncomingExtensions(IDictionary<string, string> query) {
+ ErrorUtilities.VerifyArgumentNotNull(query, "query");
+ var mgr = new ExtensionArgumentsManager();
+ mgr.protocol = Protocol.Detect(query);
+ mgr.isReadMode = true;
+ string aliasPrefix = mgr.protocol.openid.ns + ".";
+
+ // First pass looks for namespace aliases
+ foreach (var pair in query) {
+ if (pair.Key.StartsWith(aliasPrefix, StringComparison.Ordinal)) {
+ mgr.aliasManager.SetAlias(pair.Key.Substring(aliasPrefix.Length), pair.Value);
+ }
+ }
+
+ // For backwards compatibility, add certain aliases if they aren't defined.
+ foreach (var pair in typeUriToAliasAffinity) {
+ if (!mgr.aliasManager.IsAliasAssignedTo(pair.Key) &&
+ !mgr.aliasManager.IsAliasUsed(pair.Value)) {
+ mgr.aliasManager.SetAlias(pair.Value, pair.Key);
+ }
+ }
+
+ // Second pass looks for extensions using those aliases
+ foreach (var pair in query) {
+ if (!pair.Key.StartsWith(mgr.protocol.openid.Prefix, StringComparison.Ordinal)) {
+ continue;
+ }
+ string possibleAlias = pair.Key.Substring(mgr.protocol.openid.Prefix.Length);
+ int periodIndex = possibleAlias.IndexOf(".", StringComparison.Ordinal);
+ if (periodIndex >= 0) {
+ possibleAlias = possibleAlias.Substring(0, periodIndex);
+ }
+ string typeUri;
+ if ((typeUri = mgr.aliasManager.TryResolveAlias(possibleAlias)) != null) {
+ if (!mgr.extensions.ContainsKey(typeUri)) {
+ mgr.extensions[typeUri] = new Dictionary<string, string>();
+ }
+ string key = periodIndex >= 0 ? pair.Key.Substring(mgr.protocol.openid.Prefix.Length + possibleAlias.Length + 1) : string.Empty;
+ mgr.extensions[typeUri].Add(key, pair.Value);
+ }
+ }
+ return mgr;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="ExtensionArgumentsManager"/> instance to prepare outgoing extensions.
+ /// </summary>
+ /// <param name="protocol">The protocol version used for the outgoing message.</param>
+ /// <returns>
+ /// The newly created instance of <see cref="ExtensionArgumentsManager"/>.
+ /// </returns>
+ public static ExtensionArgumentsManager CreateOutgoingExtensions(Protocol protocol) {
+ var mgr = new ExtensionArgumentsManager();
+ mgr.protocol = protocol;
+
+ // Affinity for certain alias for backwards compatibility
+ foreach (var pair in typeUriToAliasAffinity) {
+ mgr.aliasManager.SetAlias(pair.Value, pair.Key);
+ }
+ return mgr;
+ }
+
+ /// <summary>
+ /// Gets the actual arguments to add to a querystring or other response,
+ /// where type URI, alias, and actual key/values are all defined.
+ /// </summary>
+ /// <param name="includeOpenIdPrefix">
+ /// <c>true</c> if the generated parameter names should include the 'openid.' prefix.
+ /// This should be <c>true</c> for all but direct response messages.
+ /// </param>
+ /// <returns>A dictionary of key=value pairs to add to the message to carry the extension.</returns>
+ public IDictionary<string, string> GetArgumentsToSend(bool includeOpenIdPrefix) {
+ if (this.isReadMode) {
+ throw new InvalidOperationException();
+ }
+ Dictionary<string, string> args = new Dictionary<string, string>();
+ foreach (var typeUriAndExtension in this.extensions) {
+ string typeUri = typeUriAndExtension.Key;
+ var extensionArgs = typeUriAndExtension.Value;
+ if (extensionArgs.Count == 0) {
+ continue;
+ }
+ string alias = this.aliasManager.GetAlias(typeUri);
+
+ // send out the alias declaration
+ string openidPrefix = includeOpenIdPrefix ? this.protocol.openid.Prefix : string.Empty;
+ args.Add(openidPrefix + this.protocol.openidnp.ns + "." + alias, typeUri);
+ string prefix = openidPrefix + alias;
+ foreach (var pair in extensionArgs) {
+ string key = prefix;
+ if (pair.Key.Length > 0) {
+ key += "." + pair.Key;
+ }
+ args.Add(key, pair.Value);
+ }
+ }
+ return args;
+ }
+
+ /// <summary>
+ /// Adds query parameters for OpenID extensions to the request directed
+ /// at the OpenID provider.
+ /// </summary>
+ /// <param name="extensionTypeUri">The extension type URI.</param>
+ /// <param name="arguments">The arguments for this extension to add to the message.</param>
+ public void AddExtensionArguments(string extensionTypeUri, IDictionary<string, string> arguments) {
+ if (this.isReadMode) {
+ throw new InvalidOperationException();
+ }
+ ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
+ ErrorUtilities.VerifyArgumentNotNull(arguments, "arguments");
+ if (arguments.Count == 0) {
+ return;
+ }
+
+ IDictionary<string, string> extensionArgs;
+ if (!this.extensions.TryGetValue(extensionTypeUri, out extensionArgs)) {
+ this.extensions.Add(extensionTypeUri, extensionArgs = new Dictionary<string, string>(arguments.Count));
+ }
+
+ ErrorUtilities.VerifyProtocol(extensionArgs.Count == 0, OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extensionTypeUri);
+ foreach (var pair in arguments) {
+ extensionArgs.Add(pair.Key, pair.Value);
+ }
+ }
+
+ /// <summary>
+ /// Gets the fields carried by a given OpenId extension.
+ /// </summary>
+ /// <param name="extensionTypeUri">The type URI of the extension whose fields are being queried for.</param>
+ /// <returns>
+ /// The fields included in the given extension, or null if the extension is not present.
+ /// </returns>
+ public IDictionary<string, string> GetExtensionArguments(string extensionTypeUri) {
+ ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
+ if (!this.isReadMode) {
+ throw new InvalidOperationException();
+ }
+
+ IDictionary<string, string> extensionArgs;
+ this.extensions.TryGetValue(extensionTypeUri, out extensionArgs);
+ return extensionArgs;
+ }
+
+ /// <summary>
+ /// Gets whether any arguments for a given extension are present.
+ /// </summary>
+ /// <param name="extensionTypeUri">The extension Type URI in question.</param>
+ /// <returns><c>true</c> if this extension is present; <c>false</c> otherwise.</returns>
+ public bool ContainsExtension(string extensionTypeUri) {
+ if (!this.isReadMode) {
+ throw new InvalidOperationException();
+ }
+ return this.extensions.ContainsKey(extensionTypeUri);
+ }
+
+ /// <summary>
+ /// Gets the type URIs of all discovered extensions in the message.
+ /// </summary>
+ /// <returns>A sequence of the type URIs.</returns>
+ public IEnumerable<string> GetExtensionTypeUris() {
+ return this.extensions.Keys;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
index 7899a54..7855147 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
@@ -1,127 +1,127 @@
-//-----------------------------------------------------------------------
-// <copyright file="ExtensionBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// A hand base class for built-in extensions.
- /// </summary>
- public class ExtensionBase : IOpenIdMessageExtension {
- /// <summary>
- /// Backing store for the <see cref="IOpenIdMessageExtension.TypeUri"/> property.
- /// </summary>
- private string typeUri;
-
- /// <summary>
- /// Backing store for the <see cref="IOpenIdMessageExtension.AdditionalSupportedTypeUris"/> property.
- /// </summary>
- private IEnumerable<string> additionalSupportedTypeUris;
-
- /// <summary>
- /// Backing store for the <see cref="IMessage.ExtraData"/> property.
- /// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ExtensionBase"/> class.
- /// </summary>
- /// <param name="version">The version of the extension.</param>
- /// <param name="typeUri">The type URI to use in the OpenID message.</param>
- /// <param name="additionalSupportedTypeUris">The additional supported type URIs by which this extension might be recognized.</param>
- protected ExtensionBase(Version version, string typeUri, IEnumerable<string> additionalSupportedTypeUris) {
- this.Version = version;
- this.typeUri = typeUri;
- this.additionalSupportedTypeUris = additionalSupportedTypeUris;
- }
-
- #region IOpenIdProtocolMessageExtension Members
-
- /// <summary>
- /// Gets the TypeURI the extension uses in the OpenID protocol and in XRDS advertisements.
- /// </summary>
- string IOpenIdMessageExtension.TypeUri {
- get { return this.typeUri; }
- }
-
- /// <summary>
- /// Gets the additional TypeURIs that are supported by this extension, in preferred order.
- /// May be empty if none other than <see cref="IOpenIdMessageExtension.TypeUri"/> is supported, but
- /// should not be null.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// Useful for reading in messages with an older version of an extension.
- /// The value in the <see cref="IOpenIdMessageExtension.TypeUri"/> property is always checked before
- /// trying this list.
- /// If you do support multiple versions of an extension using this method,
- /// consider adding a CreateResponse method to your request extension class
- /// so that the response can have the context it needs to remain compatible
- /// given the version of the extension in the request message.
- /// The <see cref="SimpleRegistration.ClaimsRequest.CreateResponse"/> for an example.
- /// </remarks>
- IEnumerable<string> IOpenIdMessageExtension.AdditionalSupportedTypeUris {
- get { return this.additionalSupportedTypeUris; }
- }
-
- #endregion
-
- #region IMessage Members
-
- /// <summary>
- /// Gets the version of the protocol or extension this message is prepared to implement.
- /// </summary>
- public Version Version { get; private set; }
-
- /// <summary>
- /// Gets the extra, non-standard Protocol parameters included in the message.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IDictionary<string, string> IMessage.ExtraData {
- get { return this.extraData; }
- }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- void IMessage.EnsureValidMessage() {
- this.EnsureValidMessage();
- }
-
- #endregion
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- protected virtual void EnsureValidMessage() {
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// A hand base class for built-in extensions.
+ /// </summary>
+ public class ExtensionBase : IOpenIdMessageExtension {
+ /// <summary>
+ /// Backing store for the <see cref="IOpenIdMessageExtension.TypeUri"/> property.
+ /// </summary>
+ private string typeUri;
+
+ /// <summary>
+ /// Backing store for the <see cref="IOpenIdMessageExtension.AdditionalSupportedTypeUris"/> property.
+ /// </summary>
+ private IEnumerable<string> additionalSupportedTypeUris;
+
+ /// <summary>
+ /// Backing store for the <see cref="IMessage.ExtraData"/> property.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExtensionBase"/> class.
+ /// </summary>
+ /// <param name="version">The version of the extension.</param>
+ /// <param name="typeUri">The type URI to use in the OpenID message.</param>
+ /// <param name="additionalSupportedTypeUris">The additional supported type URIs by which this extension might be recognized.</param>
+ protected ExtensionBase(Version version, string typeUri, IEnumerable<string> additionalSupportedTypeUris) {
+ this.Version = version;
+ this.typeUri = typeUri;
+ this.additionalSupportedTypeUris = additionalSupportedTypeUris;
+ }
+
+ #region IOpenIdProtocolMessageExtension Members
+
+ /// <summary>
+ /// Gets the TypeURI the extension uses in the OpenID protocol and in XRDS advertisements.
+ /// </summary>
+ string IOpenIdMessageExtension.TypeUri {
+ get { return this.typeUri; }
+ }
+
+ /// <summary>
+ /// Gets the additional TypeURIs that are supported by this extension, in preferred order.
+ /// May be empty if none other than <see cref="IOpenIdMessageExtension.TypeUri"/> is supported, but
+ /// should not be null.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// Useful for reading in messages with an older version of an extension.
+ /// The value in the <see cref="IOpenIdMessageExtension.TypeUri"/> property is always checked before
+ /// trying this list.
+ /// If you do support multiple versions of an extension using this method,
+ /// consider adding a CreateResponse method to your request extension class
+ /// so that the response can have the context it needs to remain compatible
+ /// given the version of the extension in the request message.
+ /// The <see cref="SimpleRegistration.ClaimsRequest.CreateResponse"/> for an example.
+ /// </remarks>
+ IEnumerable<string> IOpenIdMessageExtension.AdditionalSupportedTypeUris {
+ get { return this.additionalSupportedTypeUris; }
+ }
+
+ #endregion
+
+ #region IMessage Members
+
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ public Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.extraData; }
+ }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void IMessage.EnsureValidMessage() {
+ this.EnsureValidMessage();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected virtual void EnsureValidMessage() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactory.cs b/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactory.cs
index 6248f4b..dce7fc3 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactory.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactory.cs
@@ -1,84 +1,84 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdExtensionFactory.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.ChannelElements;
- using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// An OpenID extension factory that supports registration so that third-party
- /// extensions can add themselves to this library's supported extension list.
- /// </summary>
- internal class OpenIdExtensionFactory : IOpenIdExtensionFactory {
- /// <summary>
- /// A collection of the registered OpenID extensions.
- /// </summary>
- private List<CreateDelegate> registeredExtensions = new List<CreateDelegate>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="OpenIdExtensionFactory"/> class.
- /// </summary>
- internal OpenIdExtensionFactory() {
- this.RegisterExtension(ClaimsRequest.Factory);
- this.RegisterExtension(ClaimsResponse.Factory);
- }
-
- /// <summary>
- /// A delegate that individual extensions may register with this factory.
- /// </summary>
- /// <param name="typeUri">The type URI of the extension.</param>
- /// <param name="data">The parameters associated specifically with this extension.</param>
- /// <param name="baseMessage">The OpenID message carrying this extension.</param>
- /// <returns>
- /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes
- /// the extension described in the input parameters; <c>null</c> otherwise.
- /// </returns>
- internal delegate IOpenIdMessageExtension CreateDelegate(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage);
-
- #region IOpenIdExtensionFactory Members
-
- /// <summary>
- /// Creates a new instance of some extension based on the received extension parameters.
- /// </summary>
- /// <param name="typeUri">The type URI of the extension.</param>
- /// <param name="data">The parameters associated specifically with this extension.</param>
- /// <param name="baseMessage">The OpenID message carrying this extension.</param>
- /// <returns>
- /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes
- /// the extension described in the input parameters; <c>null</c> otherwise.
- /// </returns>
- /// <remarks>
- /// This factory method need only initialize properties in the instantiated extension object
- /// that are not bound using <see cref="MessagePartAttribute"/>.
- /// </remarks>
- public IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage) {
- foreach (var factoryMethod in this.registeredExtensions) {
- IOpenIdMessageExtension result = factoryMethod(typeUri, data, baseMessage);
- if (result != null) {
- return result;
- }
- }
-
- return null;
- }
-
- #endregion
-
- /// <summary>
- /// Registers a new extension delegate.
- /// </summary>
- /// <param name="creator">The factory method that can create the extension.</param>
- internal void RegisterExtension(CreateDelegate creator) {
- this.registeredExtensions.Add(creator);
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdExtensionFactory.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// An OpenID extension factory that supports registration so that third-party
+ /// extensions can add themselves to this library's supported extension list.
+ /// </summary>
+ internal class OpenIdExtensionFactory : IOpenIdExtensionFactory {
+ /// <summary>
+ /// A collection of the registered OpenID extensions.
+ /// </summary>
+ private List<CreateDelegate> registeredExtensions = new List<CreateDelegate>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdExtensionFactory"/> class.
+ /// </summary>
+ internal OpenIdExtensionFactory() {
+ this.RegisterExtension(ClaimsRequest.Factory);
+ this.RegisterExtension(ClaimsResponse.Factory);
+ }
+
+ /// <summary>
+ /// A delegate that individual extensions may register with this factory.
+ /// </summary>
+ /// <param name="typeUri">The type URI of the extension.</param>
+ /// <param name="data">The parameters associated specifically with this extension.</param>
+ /// <param name="baseMessage">The OpenID message carrying this extension.</param>
+ /// <returns>
+ /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes
+ /// the extension described in the input parameters; <c>null</c> otherwise.
+ /// </returns>
+ internal delegate IOpenIdMessageExtension CreateDelegate(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage);
+
+ #region IOpenIdExtensionFactory Members
+
+ /// <summary>
+ /// Creates a new instance of some extension based on the received extension parameters.
+ /// </summary>
+ /// <param name="typeUri">The type URI of the extension.</param>
+ /// <param name="data">The parameters associated specifically with this extension.</param>
+ /// <param name="baseMessage">The OpenID message carrying this extension.</param>
+ /// <returns>
+ /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes
+ /// the extension described in the input parameters; <c>null</c> otherwise.
+ /// </returns>
+ /// <remarks>
+ /// This factory method need only initialize properties in the instantiated extension object
+ /// that are not bound using <see cref="MessagePartAttribute"/>.
+ /// </remarks>
+ public IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage) {
+ foreach (var factoryMethod in this.registeredExtensions) {
+ IOpenIdMessageExtension result = factoryMethod(typeUri, data, baseMessage);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Registers a new extension delegate.
+ /// </summary>
+ /// <param name="creator">The factory method that can create the extension.</param>
+ internal void RegisterExtension(CreateDelegate creator) {
+ this.registeredExtensions.Add(creator);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
index 34b5658..0424f09 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
@@ -1,311 +1,311 @@
-//-----------------------------------------------------------------------
-// <copyright file="ClaimsRequest.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// Carries the request/require/none demand state of the simple registration fields.
- /// </summary>
- public sealed class ClaimsRequest : ExtensionBase {
- /// <summary>
- /// The factory method that may be used in deserialization of this message.
- /// </summary>
- internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
- if (typeUri == Constants.sreg_ns && baseMessage is SignedResponseRequest) {
- return new ClaimsRequest();
- }
-
- return null;
- };
-
- /// <summary>
- /// Additional type URIs that this extension is sometimes known by remote parties.
- /// </summary>
- private static readonly string[] additionalTypeUris = new string[] {
- Constants.sreg_ns10,
- Constants.sreg_ns11other,
- };
-
- /// <summary>
- /// The type URI that this particular (deserialized) extension was read in using,
- /// allowing a response to alter be crafted using the same type URI.
- /// </summary>
- private string typeUriDeserializedFrom;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ClaimsRequest"/> class.
- /// </summary>
- public ClaimsRequest()
- : base(new Version(1, 0), Constants.sreg_ns, additionalTypeUris) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ClaimsRequest"/> class
- /// by deserializing from a message.
- /// </summary>
- /// <param name="typeUri">The type URI this extension was recognized by in the OpenID message.</param>
- internal ClaimsRequest(string typeUri)
- : this() {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
-
- this.typeUriDeserializedFrom = typeUri;
- }
-
- /// <summary>
- /// Gets or sets the URL the consumer site provides for the authenticating user to review
- /// for how his claims will be used by the consumer web site.
- /// </summary>
- [MessagePart(Constants.policy_url, IsRequired = false)]
- public Uri PolicyUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the nickname of the user.
- /// </summary>
- public DemandLevel Nickname { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the email of the user.
- /// </summary>
- public DemandLevel Email { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the full name of the user.
- /// </summary>
- public DemandLevel FullName { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the birthdate of the user.
- /// </summary>
- public DemandLevel BirthDate { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the gender of the user.
- /// </summary>
- public DemandLevel Gender { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the postal code of the user.
- /// </summary>
- public DemandLevel PostalCode { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the Country of the user.
- /// </summary>
- public DemandLevel Country { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the language of the user.
- /// </summary>
- public DemandLevel Language { get; set; }
-
- /// <summary>
- /// Gets or sets the level of interest a relying party has in the time zone of the user.
- /// </summary>
- public DemandLevel TimeZone { get; set; }
-
- /// <summary>
- /// Gets or sets the value of the sreg.required parameter.
- /// </summary>
- /// <value>A comma-delimited list of sreg fields.</value>
- [MessagePart(Constants.required, AllowEmpty = true)]
- private string RequiredList {
- get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Require)); }
- set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Require); }
- }
-
- /// <summary>
- /// Gets or sets the value of the sreg.optional parameter.
- /// </summary>
- /// <value>A comma-delimited list of sreg fields.</value>
- [MessagePart(Constants.optional, AllowEmpty = true)]
- private string OptionalList {
- get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Request)); }
- set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Request); }
- }
-
- /// <summary>
- /// Tests equality between two <see cref="ClaimsRequest"/> structs.
- /// </summary>
- /// <param name="one">One instance to compare.</param>
- /// <param name="other">Another instance to compare.</param>
- /// <returns>The result of the operator.</returns>
- public static bool operator ==(ClaimsRequest one, ClaimsRequest other) {
- return one.EqualsNullSafe(other);
- }
-
- /// <summary>
- /// Tests inequality between two <see cref="ClaimsRequest"/> structs.
- /// </summary>
- /// <param name="one">One instance to compare.</param>
- /// <param name="other">Another instance to compare.</param>
- /// <returns>The result of the operator.</returns>
- public static bool operator !=(ClaimsRequest one, ClaimsRequest other) {
- return !(one == other);
- }
-
- /// <summary>
- /// Tests equality between two <see cref="ClaimsRequest"/> structs.
- /// </summary>
- /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
- /// <returns>
- /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
- /// </returns>
- /// <exception cref="T:System.NullReferenceException">
- /// The <paramref name="obj"/> parameter is null.
- /// </exception>
- public override bool Equals(object obj) {
- ClaimsRequest other = obj as ClaimsRequest;
- if (other == null) {
- return false;
- }
-
- return
- this.BirthDate.Equals(other.BirthDate) &&
- this.Country.Equals(other.Country) &&
- this.Language.Equals(other.Language) &&
- this.Email.Equals(other.Email) &&
- this.FullName.Equals(other.FullName) &&
- this.Gender.Equals(other.Gender) &&
- this.Nickname.Equals(other.Nickname) &&
- this.PostalCode.Equals(other.PostalCode) &&
- this.TimeZone.Equals(other.TimeZone) &&
- this.PolicyUrl.EqualsNullSafe(other.PolicyUrl);
- }
-
- /// <summary>
- /// Serves as a hash function for a particular type.
- /// </summary>
- /// <returns>
- /// A hash code for the current <see cref="T:System.Object"/>.
- /// </returns>
- public override int GetHashCode() {
- // It's important that if Equals returns true that the hash code also equals,
- // so returning base.GetHashCode() is a BAD option.
- // Return 1 is simple and poor for dictionary storage, but considering that every
- // ClaimsRequest formulated at a single RP will likely have all the same fields,
- // even a good hash code function will likely generate the same hash code. So
- // we just cut to the chase and return a simple one.
- return 1;
- }
-
- /// <summary>
- /// Renders the requested information as a string.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
- /// </returns>
- public override string ToString() {
- string format = @"Nickname = '{0}'
-Email = '{1}'
-FullName = '{2}'
-Birthdate = '{3}'
-Gender = '{4}'
-PostalCode = '{5}'
-Country = '{6}'
-Language = '{7}'
-TimeZone = '{8}'";
- return string.Format(CultureInfo.CurrentCulture, format, this.Nickname, this.Email, this.FullName, this.BirthDate, this.Gender, this.PostalCode, this.Country, this.Language, this.TimeZone);
- }
-
- /// <summary>
- /// Prepares a Simple Registration response extension that is compatible with the
- /// version of Simple Registration used in the request message.
- /// </summary>
- /// <returns>The newly created <see cref="ClaimsResponse"/> instance.</returns>
- public ClaimsResponse CreateResponse() {
- if (this.typeUriDeserializedFrom == null) {
- throw new InvalidOperationException(OpenIdStrings.CallDeserializeBeforeCreateResponse);
- }
-
- return new ClaimsResponse(this.typeUriDeserializedFrom);
- }
-
- /// <summary>
- /// Sets the profile request properties according to a list of
- /// field names that might have been passed in the OpenId query dictionary.
- /// </summary>
- /// <param name="fieldNames">
- /// The list of field names that should receive a given
- /// <paramref name="requestLevel"/>. These field names should match
- /// the OpenId specification for field names, omitting the 'openid.sreg' prefix.
- /// </param>
- /// <param name="requestLevel">The none/request/require state of the listed fields.</param>
- private void SetProfileRequestFromList(ICollection<string> fieldNames, DemandLevel requestLevel) {
- foreach (string field in fieldNames) {
- switch (field) {
- case Constants.nickname:
- this.Nickname = requestLevel;
- break;
- case Constants.email:
- this.Email = requestLevel;
- break;
- case Constants.fullname:
- this.FullName = requestLevel;
- break;
- case Constants.dob:
- this.BirthDate = requestLevel;
- break;
- case Constants.gender:
- this.Gender = requestLevel;
- break;
- case Constants.postcode:
- this.PostalCode = requestLevel;
- break;
- case Constants.country:
- this.Country = requestLevel;
- break;
- case Constants.language:
- this.Language = requestLevel;
- break;
- case Constants.timezone:
- this.TimeZone = requestLevel;
- break;
- default:
- Logger.WarnFormat("OpenIdProfileRequest.SetProfileRequestFromList: Unrecognized field name '{0}'.", field);
- break;
- }
- }
- }
-
- /// <summary>
- /// Assembles the profile parameter names that have a given <see cref="DemandLevel"/>.
- /// </summary>
- /// <param name="level">The demand level (request, require, none).</param>
- /// <returns>An array of the profile parameter names that meet the criteria.</returns>
- private string[] AssembleProfileFields(DemandLevel level) {
- List<string> fields = new List<string>(10);
- if (this.Nickname == level) {
- fields.Add(Constants.nickname);
- } if (this.Email == level) {
- fields.Add(Constants.email);
- } if (this.FullName == level) {
- fields.Add(Constants.fullname);
- } if (this.BirthDate == level) {
- fields.Add(Constants.dob);
- } if (this.Gender == level) {
- fields.Add(Constants.gender);
- } if (this.PostalCode == level) {
- fields.Add(Constants.postcode);
- } if (this.Country == level) {
- fields.Add(Constants.country);
- } if (this.Language == level) {
- fields.Add(Constants.language);
- } if (this.TimeZone == level) {
- fields.Add(Constants.timezone);
- }
-
- return fields.ToArray();
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ClaimsRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Carries the request/require/none demand state of the simple registration fields.
+ /// </summary>
+ public sealed class ClaimsRequest : ExtensionBase {
+ /// <summary>
+ /// The factory method that may be used in deserialization of this message.
+ /// </summary>
+ internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
+ if (typeUri == Constants.sreg_ns && baseMessage is SignedResponseRequest) {
+ return new ClaimsRequest();
+ }
+
+ return null;
+ };
+
+ /// <summary>
+ /// Additional type URIs that this extension is sometimes known by remote parties.
+ /// </summary>
+ private static readonly string[] additionalTypeUris = new string[] {
+ Constants.sreg_ns10,
+ Constants.sreg_ns11other,
+ };
+
+ /// <summary>
+ /// The type URI that this particular (deserialized) extension was read in using,
+ /// allowing a response to alter be crafted using the same type URI.
+ /// </summary>
+ private string typeUriDeserializedFrom;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClaimsRequest"/> class.
+ /// </summary>
+ public ClaimsRequest()
+ : base(new Version(1, 0), Constants.sreg_ns, additionalTypeUris) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClaimsRequest"/> class
+ /// by deserializing from a message.
+ /// </summary>
+ /// <param name="typeUri">The type URI this extension was recognized by in the OpenID message.</param>
+ internal ClaimsRequest(string typeUri)
+ : this() {
+ ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+
+ this.typeUriDeserializedFrom = typeUri;
+ }
+
+ /// <summary>
+ /// Gets or sets the URL the consumer site provides for the authenticating user to review
+ /// for how his claims will be used by the consumer web site.
+ /// </summary>
+ [MessagePart(Constants.policy_url, IsRequired = false)]
+ public Uri PolicyUrl { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the nickname of the user.
+ /// </summary>
+ public DemandLevel Nickname { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the email of the user.
+ /// </summary>
+ public DemandLevel Email { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the full name of the user.
+ /// </summary>
+ public DemandLevel FullName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the birthdate of the user.
+ /// </summary>
+ public DemandLevel BirthDate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the gender of the user.
+ /// </summary>
+ public DemandLevel Gender { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the postal code of the user.
+ /// </summary>
+ public DemandLevel PostalCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the Country of the user.
+ /// </summary>
+ public DemandLevel Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the language of the user.
+ /// </summary>
+ public DemandLevel Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level of interest a relying party has in the time zone of the user.
+ /// </summary>
+ public DemandLevel TimeZone { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value of the sreg.required parameter.
+ /// </summary>
+ /// <value>A comma-delimited list of sreg fields.</value>
+ [MessagePart(Constants.required, AllowEmpty = true)]
+ private string RequiredList {
+ get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Require)); }
+ set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Require); }
+ }
+
+ /// <summary>
+ /// Gets or sets the value of the sreg.optional parameter.
+ /// </summary>
+ /// <value>A comma-delimited list of sreg fields.</value>
+ [MessagePart(Constants.optional, AllowEmpty = true)]
+ private string OptionalList {
+ get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Request)); }
+ set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Request); }
+ }
+
+ /// <summary>
+ /// Tests equality between two <see cref="ClaimsRequest"/> structs.
+ /// </summary>
+ /// <param name="one">One instance to compare.</param>
+ /// <param name="other">Another instance to compare.</param>
+ /// <returns>The result of the operator.</returns>
+ public static bool operator ==(ClaimsRequest one, ClaimsRequest other) {
+ return one.EqualsNullSafe(other);
+ }
+
+ /// <summary>
+ /// Tests inequality between two <see cref="ClaimsRequest"/> structs.
+ /// </summary>
+ /// <param name="one">One instance to compare.</param>
+ /// <param name="other">Another instance to compare.</param>
+ /// <returns>The result of the operator.</returns>
+ public static bool operator !=(ClaimsRequest one, ClaimsRequest other) {
+ return !(one == other);
+ }
+
+ /// <summary>
+ /// Tests equality between two <see cref="ClaimsRequest"/> structs.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
+ public override bool Equals(object obj) {
+ ClaimsRequest other = obj as ClaimsRequest;
+ if (other == null) {
+ return false;
+ }
+
+ return
+ this.BirthDate.Equals(other.BirthDate) &&
+ this.Country.Equals(other.Country) &&
+ this.Language.Equals(other.Language) &&
+ this.Email.Equals(other.Email) &&
+ this.FullName.Equals(other.FullName) &&
+ this.Gender.Equals(other.Gender) &&
+ this.Nickname.Equals(other.Nickname) &&
+ this.PostalCode.Equals(other.PostalCode) &&
+ this.TimeZone.Equals(other.TimeZone) &&
+ this.PolicyUrl.EqualsNullSafe(other.PolicyUrl);
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a particular type.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ // It's important that if Equals returns true that the hash code also equals,
+ // so returning base.GetHashCode() is a BAD option.
+ // Return 1 is simple and poor for dictionary storage, but considering that every
+ // ClaimsRequest formulated at a single RP will likely have all the same fields,
+ // even a good hash code function will likely generate the same hash code. So
+ // we just cut to the chase and return a simple one.
+ return 1;
+ }
+
+ /// <summary>
+ /// Renders the requested information as a string.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override string ToString() {
+ string format = @"Nickname = '{0}'
+Email = '{1}'
+FullName = '{2}'
+Birthdate = '{3}'
+Gender = '{4}'
+PostalCode = '{5}'
+Country = '{6}'
+Language = '{7}'
+TimeZone = '{8}'";
+ return string.Format(CultureInfo.CurrentCulture, format, this.Nickname, this.Email, this.FullName, this.BirthDate, this.Gender, this.PostalCode, this.Country, this.Language, this.TimeZone);
+ }
+
+ /// <summary>
+ /// Prepares a Simple Registration response extension that is compatible with the
+ /// version of Simple Registration used in the request message.
+ /// </summary>
+ /// <returns>The newly created <see cref="ClaimsResponse"/> instance.</returns>
+ public ClaimsResponse CreateResponse() {
+ if (this.typeUriDeserializedFrom == null) {
+ throw new InvalidOperationException(OpenIdStrings.CallDeserializeBeforeCreateResponse);
+ }
+
+ return new ClaimsResponse(this.typeUriDeserializedFrom);
+ }
+
+ /// <summary>
+ /// Sets the profile request properties according to a list of
+ /// field names that might have been passed in the OpenId query dictionary.
+ /// </summary>
+ /// <param name="fieldNames">
+ /// The list of field names that should receive a given
+ /// <paramref name="requestLevel"/>. These field names should match
+ /// the OpenId specification for field names, omitting the 'openid.sreg' prefix.
+ /// </param>
+ /// <param name="requestLevel">The none/request/require state of the listed fields.</param>
+ private void SetProfileRequestFromList(ICollection<string> fieldNames, DemandLevel requestLevel) {
+ foreach (string field in fieldNames) {
+ switch (field) {
+ case Constants.nickname:
+ this.Nickname = requestLevel;
+ break;
+ case Constants.email:
+ this.Email = requestLevel;
+ break;
+ case Constants.fullname:
+ this.FullName = requestLevel;
+ break;
+ case Constants.dob:
+ this.BirthDate = requestLevel;
+ break;
+ case Constants.gender:
+ this.Gender = requestLevel;
+ break;
+ case Constants.postcode:
+ this.PostalCode = requestLevel;
+ break;
+ case Constants.country:
+ this.Country = requestLevel;
+ break;
+ case Constants.language:
+ this.Language = requestLevel;
+ break;
+ case Constants.timezone:
+ this.TimeZone = requestLevel;
+ break;
+ default:
+ Logger.WarnFormat("OpenIdProfileRequest.SetProfileRequestFromList: Unrecognized field name '{0}'.", field);
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Assembles the profile parameter names that have a given <see cref="DemandLevel"/>.
+ /// </summary>
+ /// <param name="level">The demand level (request, require, none).</param>
+ /// <returns>An array of the profile parameter names that meet the criteria.</returns>
+ private string[] AssembleProfileFields(DemandLevel level) {
+ List<string> fields = new List<string>(10);
+ if (this.Nickname == level) {
+ fields.Add(Constants.nickname);
+ } if (this.Email == level) {
+ fields.Add(Constants.email);
+ } if (this.FullName == level) {
+ fields.Add(Constants.fullname);
+ } if (this.BirthDate == level) {
+ fields.Add(Constants.dob);
+ } if (this.Gender == level) {
+ fields.Add(Constants.gender);
+ } if (this.PostalCode == level) {
+ fields.Add(Constants.postcode);
+ } if (this.Country == level) {
+ fields.Add(Constants.country);
+ } if (this.Language == level) {
+ fields.Add(Constants.language);
+ } if (this.TimeZone == level) {
+ fields.Add(Constants.timezone);
+ }
+
+ return fields.ToArray();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
index 033b531..082fd52 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
@@ -1,324 +1,324 @@
-//-----------------------------------------------------------------------
-// <copyright file="ClaimsResponse.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.Net.Mail;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Xml.Serialization;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// A struct storing Simple Registration field values describing an
- /// authenticating user.
- /// </summary>
- public sealed class ClaimsResponse : ExtensionBase {
- /// <summary>
- /// The factory method that may be used in deserialization of this message.
- /// </summary>
- internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
- if (typeUri == Constants.sreg_ns && baseMessage is IndirectSignedResponse) {
- return new ClaimsResponse(typeUri);
- }
-
- return null;
- };
-
- /// <summary>
- /// The allowed format for birthdates.
- /// </summary>
- private static readonly Regex birthDateValidator = new Regex(@"^\d\d\d\d-\d\d-\d\d$");
-
- /// <summary>
- /// Storage for the raw string birthdate value.
- /// </summary>
- private string birthDateRaw;
-
- /// <summary>
- /// Backing field for the <see cref="BirthDate"/> property.
- /// </summary>
- private DateTime? birthDate;
-
- /// <summary>
- /// The TypeURI that must be used in the response, based on the one used in the request.
- /// </summary>
- private string typeUriToUse;
-
- /// <summary>
- /// Backing field for the <see cref="Culture"/> property.
- /// </summary>
- private CultureInfo culture;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ClaimsResponse"/> class.
- /// </summary>
- internal ClaimsResponse()
- : this(Constants.sreg_ns) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ClaimsResponse"/> class.
- /// </summary>
- /// <param name="typeUriToUse">
- /// The type URI that must be used to identify this extension in the response message.
- /// This value should be the same one the relying party used to send the extension request.
- /// </param>
- internal ClaimsResponse(string typeUriToUse)
- : base(new Version(1, 0), typeUriToUse, EmptyList<string>.Instance) {
- ErrorUtilities.VerifyNonZeroLength(typeUriToUse, "typeUriToUse");
- this.typeUriToUse = typeUriToUse;
- }
-
- /// <summary>
- /// Gets or sets the nickname the user goes by.
- /// </summary>
- [MessagePart(Constants.nickname)]
- public string Nickname { get; set; }
-
- /// <summary>
- /// Gets or sets the user's email address.
- /// </summary>
- [MessagePart(Constants.email)]
- public string Email { get; set; }
-
- /// <summary>
- /// Gets or sets the full name of a user as a single string.
- /// </summary>
- [MessagePart(Constants.fullname)]
- public string FullName { get; set; }
-
- /// <summary>
- /// Gets or sets the user's birthdate.
- /// </summary>
- public DateTime? BirthDate {
- get {
- return this.birthDate;
- }
-
- set {
- this.birthDate = value;
-
- // Don't use property accessor for peer property to avoid infinite loop between the two proeprty accessors.
- if (value.HasValue) {
- this.birthDateRaw = value.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
- } else {
- this.birthDateRaw = null;
- }
- }
- }
-
- /// <summary>
- /// Gets or sets the raw birth date string given by the extension.
- /// </summary>
- /// <value>A string in the format yyyy-MM-dd.</value>
- [MessagePart(Constants.dob)]
- public string BirthDateRaw {
- get {
- return this.birthDateRaw;
- }
-
- set {
- if (value != null) {
- if (!birthDateValidator.IsMatch(value)) {
- throw new ArgumentException(OpenIdStrings.SregInvalidBirthdate, "value");
- }
-
- // Update the BirthDate property, if possible.
- // Don't use property accessor for peer property to avoid infinite loop between the two proeprty accessors.
- // Some valid sreg dob values like "2000-00-00" will not work as a DateTime struct,
- // in which case we null it out, but don't show any error.
- DateTime newBirthDate;
- if (DateTime.TryParse(value, out newBirthDate)) {
- this.birthDate = newBirthDate;
- } else {
- Logger.WarnFormat("Simple Registration birthdate '{0}' could not be parsed into a DateTime and may not include month and/or day information. Setting BirthDate property to null.", value);
- this.birthDate = null;
- }
- } else {
- this.birthDate = null;
- }
-
- this.birthDateRaw = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the gender of the user.
- /// </summary>
- [MessagePart(Constants.gender, Encoder = typeof(GenderEncoder))]
- public Gender? Gender { get; set; }
-
- /// <summary>
- /// Gets or sets the zip code / postal code of the user.
- /// </summary>
- [MessagePart(Constants.postcode)]
- public string PostalCode { get; set; }
-
- /// <summary>
- /// Gets or sets the country of the user.
- /// </summary>
- [MessagePart(Constants.country)]
- public string Country { get; set; }
-
- /// <summary>
- /// Gets or sets the primary/preferred language of the user.
- /// </summary>
- [MessagePart(Constants.language)]
- public string Language { get; set; }
-
- /// <summary>
- /// Gets or sets the user's timezone.
- /// </summary>
- [MessagePart(Constants.timezone)]
- public string TimeZone { get; set; }
-
- /// <summary>
- /// Gets a combination of the user's full name and email address.
- /// </summary>
- public MailAddress MailAddress {
- get {
- if (string.IsNullOrEmpty(this.Email)) {
- return null;
- } else if (string.IsNullOrEmpty(this.FullName)) {
- return new MailAddress(this.Email);
- } else {
- return new MailAddress(this.Email, this.FullName);
- }
- }
- }
-
- /// <summary>
- /// Gets or sets a combination o the language and country of the user.
- /// </summary>
- [XmlIgnore]
- public CultureInfo Culture {
- get {
- if (this.culture == null && !string.IsNullOrEmpty(this.Language)) {
- string cultureString = string.Empty;
- cultureString = this.Language;
- if (!string.IsNullOrEmpty(this.Country)) {
- cultureString += "-" + this.Country;
- }
- this.culture = CultureInfo.GetCultureInfo(cultureString);
- }
-
- return this.culture;
- }
-
- set {
- this.culture = value;
- this.Language = (value != null) ? value.TwoLetterISOLanguageName : null;
- int indexOfHyphen = (value != null) ? value.Name.IndexOf('-') : -1;
- this.Country = indexOfHyphen > 0 ? value.Name.Substring(indexOfHyphen + 1) : null;
- }
- }
-
- #region IClientScriptExtension Members
-
- // TODO: re-enable this
- ////string IClientScriptExtensionResponse.InitializeJavaScriptData(IDictionary<string, string> sreg, IAuthenticationResponse response, string typeUri) {
- //// StringBuilder builder = new StringBuilder();
- //// builder.Append("{ ");
-
- //// string nickname, email, fullName, dob, genderString, postalCode, country, language, timeZone;
- //// if (sreg.TryGetValue(Constants.nickname, out nickname)) {
- //// builder.Append(createAddFieldJS(Constants.nickname, nickname));
- //// }
- //// if (sreg.TryGetValue(Constants.email, out email)) {
- //// builder.Append(createAddFieldJS(Constants.email, email));
- //// }
- //// if (sreg.TryGetValue(Constants.fullname, out fullName)) {
- //// builder.Append(createAddFieldJS(Constants.fullname, fullName));
- //// }
- //// if (sreg.TryGetValue(Constants.dob, out dob)) {
- //// builder.Append(createAddFieldJS(Constants.dob, dob));
- //// }
- //// if (sreg.TryGetValue(Constants.gender, out genderString)) {
- //// builder.Append(createAddFieldJS(Constants.gender, genderString));
- //// }
- //// if (sreg.TryGetValue(Constants.postcode, out postalCode)) {
- //// builder.Append(createAddFieldJS(Constants.postcode, postalCode));
- //// }
- //// if (sreg.TryGetValue(Constants.country, out country)) {
- //// builder.Append(createAddFieldJS(Constants.country, country));
- //// }
- //// if (sreg.TryGetValue(Constants.language, out language)) {
- //// builder.Append(createAddFieldJS(Constants.language, language));
- //// }
- //// if (sreg.TryGetValue(Constants.timezone, out timeZone)) {
- //// builder.Append(createAddFieldJS(Constants.timezone, timeZone));
- //// }
- //// if (builder[builder.Length - 1] == ',') builder.Length -= 1;
- //// builder.Append("}");
- //// return builder.ToString();
- ////}
-
- #endregion
-
- /// <summary>
- /// Tests equality of two <see cref="ClaimsResponse"/> objects.
- /// </summary>
- /// <param name="one">One instance to compare.</param>
- /// <param name="other">Another instance to compare.</param>
- /// <returns>The result of the operator.</returns>
- public static bool operator ==(ClaimsResponse one, ClaimsResponse other) {
- return one.EqualsNullSafe(other);
- }
-
- /// <summary>
- /// Tests inequality of two <see cref="ClaimsResponse"/> objects.
- /// </summary>
- /// <param name="one">One instance to compare.</param>
- /// <param name="other">Another instance to compare.</param>
- /// <returns>The result of the operator.</returns>
- public static bool operator !=(ClaimsResponse one, ClaimsResponse other) {
- return !(one == other);
- }
-
- /// <summary>
- /// Tests equality of two <see cref="ClaimsResponse"/> objects.
- /// </summary>
- /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
- /// <returns>
- /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
- /// </returns>
- /// <exception cref="T:System.NullReferenceException">
- /// The <paramref name="obj"/> parameter is null.
- /// </exception>
- public override bool Equals(object obj) {
- ClaimsResponse other = obj as ClaimsResponse;
- if (other == null) {
- return false;
- }
-
- return
- this.BirthDateRaw.EqualsNullSafe(other.BirthDateRaw) &&
- this.Country.EqualsNullSafe(other.Country) &&
- this.Language.EqualsNullSafe(other.Language) &&
- this.Email.EqualsNullSafe(other.Email) &&
- this.FullName.EqualsNullSafe(other.FullName) &&
- this.Gender.Equals(other.Gender) &&
- this.Nickname.EqualsNullSafe(other.Nickname) &&
- this.PostalCode.EqualsNullSafe(other.PostalCode) &&
- this.TimeZone.EqualsNullSafe(other.TimeZone);
- }
-
- /// <summary>
- /// Serves as a hash function for a particular type.
- /// </summary>
- /// <returns>
- /// A hash code for the current <see cref="T:System.Object"/>.
- /// </returns>
- public override int GetHashCode() {
- return (this.Nickname != null) ? this.Nickname.GetHashCode() : base.GetHashCode();
- }
- }
+//-----------------------------------------------------------------------
+// <copyright file="ClaimsResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Net.Mail;
+ using System.Text;
+ using System.Text.RegularExpressions;
+ using System.Xml.Serialization;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// A struct storing Simple Registration field values describing an
+ /// authenticating user.
+ /// </summary>
+ public sealed class ClaimsResponse : ExtensionBase {
+ /// <summary>
+ /// The factory method that may be used in deserialization of this message.
+ /// </summary>
+ internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
+ if (typeUri == Constants.sreg_ns && baseMessage is IndirectSignedResponse) {
+ return new ClaimsResponse(typeUri);
+ }
+
+ return null;
+ };
+
+ /// <summary>
+ /// The allowed format for birthdates.
+ /// </summary>
+ private static readonly Regex birthDateValidator = new Regex(@"^\d\d\d\d-\d\d-\d\d$");
+
+ /// <summary>
+ /// Storage for the raw string birthdate value.
+ /// </summary>
+ private string birthDateRaw;
+
+ /// <summary>
+ /// Backing field for the <see cref="BirthDate"/> property.
+ /// </summary>
+ private DateTime? birthDate;
+
+ /// <summary>
+ /// The TypeURI that must be used in the response, based on the one used in the request.
+ /// </summary>
+ private string typeUriToUse;
+
+ /// <summary>
+ /// Backing field for the <see cref="Culture"/> property.
+ /// </summary>
+ private CultureInfo culture;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClaimsResponse"/> class.
+ /// </summary>
+ internal ClaimsResponse()
+ : this(Constants.sreg_ns) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClaimsResponse"/> class.
+ /// </summary>
+ /// <param name="typeUriToUse">
+ /// The type URI that must be used to identify this extension in the response message.
+ /// This value should be the same one the relying party used to send the extension request.
+ /// </param>
+ internal ClaimsResponse(string typeUriToUse)
+ : base(new Version(1, 0), typeUriToUse, EmptyList<string>.Instance) {
+ ErrorUtilities.VerifyNonZeroLength(typeUriToUse, "typeUriToUse");
+ this.typeUriToUse = typeUriToUse;
+ }
+
+ /// <summary>
+ /// Gets or sets the nickname the user goes by.
+ /// </summary>
+ [MessagePart(Constants.nickname)]
+ public string Nickname { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user's email address.
+ /// </summary>
+ [MessagePart(Constants.email)]
+ public string Email { get; set; }
+
+ /// <summary>
+ /// Gets or sets the full name of a user as a single string.
+ /// </summary>
+ [MessagePart(Constants.fullname)]
+ public string FullName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user's birthdate.
+ /// </summary>
+ public DateTime? BirthDate {
+ get {
+ return this.birthDate;
+ }
+
+ set {
+ this.birthDate = value;
+
+ // Don't use property accessor for peer property to avoid infinite loop between the two proeprty accessors.
+ if (value.HasValue) {
+ this.birthDateRaw = value.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
+ } else {
+ this.birthDateRaw = null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the raw birth date string given by the extension.
+ /// </summary>
+ /// <value>A string in the format yyyy-MM-dd.</value>
+ [MessagePart(Constants.dob)]
+ public string BirthDateRaw {
+ get {
+ return this.birthDateRaw;
+ }
+
+ set {
+ if (value != null) {
+ if (!birthDateValidator.IsMatch(value)) {
+ throw new ArgumentException(OpenIdStrings.SregInvalidBirthdate, "value");
+ }
+
+ // Update the BirthDate property, if possible.
+ // Don't use property accessor for peer property to avoid infinite loop between the two proeprty accessors.
+ // Some valid sreg dob values like "2000-00-00" will not work as a DateTime struct,
+ // in which case we null it out, but don't show any error.
+ DateTime newBirthDate;
+ if (DateTime.TryParse(value, out newBirthDate)) {
+ this.birthDate = newBirthDate;
+ } else {
+ Logger.WarnFormat("Simple Registration birthdate '{0}' could not be parsed into a DateTime and may not include month and/or day information. Setting BirthDate property to null.", value);
+ this.birthDate = null;
+ }
+ } else {
+ this.birthDate = null;
+ }
+
+ this.birthDateRaw = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the gender of the user.
+ /// </summary>
+ [MessagePart(Constants.gender, Encoder = typeof(GenderEncoder))]
+ public Gender? Gender { get; set; }
+
+ /// <summary>
+ /// Gets or sets the zip code / postal code of the user.
+ /// </summary>
+ [MessagePart(Constants.postcode)]
+ public string PostalCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country of the user.
+ /// </summary>
+ [MessagePart(Constants.country)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets the primary/preferred language of the user.
+ /// </summary>
+ [MessagePart(Constants.language)]
+ public string Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user's timezone.
+ /// </summary>
+ [MessagePart(Constants.timezone)]
+ public string TimeZone { get; set; }
+
+ /// <summary>
+ /// Gets a combination of the user's full name and email address.
+ /// </summary>
+ public MailAddress MailAddress {
+ get {
+ if (string.IsNullOrEmpty(this.Email)) {
+ return null;
+ } else if (string.IsNullOrEmpty(this.FullName)) {
+ return new MailAddress(this.Email);
+ } else {
+ return new MailAddress(this.Email, this.FullName);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a combination o the language and country of the user.
+ /// </summary>
+ [XmlIgnore]
+ public CultureInfo Culture {
+ get {
+ if (this.culture == null && !string.IsNullOrEmpty(this.Language)) {
+ string cultureString = string.Empty;
+ cultureString = this.Language;
+ if (!string.IsNullOrEmpty(this.Country)) {
+ cultureString += "-" + this.Country;
+ }
+ this.culture = CultureInfo.GetCultureInfo(cultureString);
+ }
+
+ return this.culture;
+ }
+
+ set {
+ this.culture = value;
+ this.Language = (value != null) ? value.TwoLetterISOLanguageName : null;
+ int indexOfHyphen = (value != null) ? value.Name.IndexOf('-') : -1;
+ this.Country = indexOfHyphen > 0 ? value.Name.Substring(indexOfHyphen + 1) : null;
+ }
+ }
+
+ #region IClientScriptExtension Members
+
+ // TODO: re-enable this
+ ////string IClientScriptExtensionResponse.InitializeJavaScriptData(IDictionary<string, string> sreg, IAuthenticationResponse response, string typeUri) {
+ //// StringBuilder builder = new StringBuilder();
+ //// builder.Append("{ ");
+
+ //// string nickname, email, fullName, dob, genderString, postalCode, country, language, timeZone;
+ //// if (sreg.TryGetValue(Constants.nickname, out nickname)) {
+ //// builder.Append(createAddFieldJS(Constants.nickname, nickname));
+ //// }
+ //// if (sreg.TryGetValue(Constants.email, out email)) {
+ //// builder.Append(createAddFieldJS(Constants.email, email));
+ //// }
+ //// if (sreg.TryGetValue(Constants.fullname, out fullName)) {
+ //// builder.Append(createAddFieldJS(Constants.fullname, fullName));
+ //// }
+ //// if (sreg.TryGetValue(Constants.dob, out dob)) {
+ //// builder.Append(createAddFieldJS(Constants.dob, dob));
+ //// }
+ //// if (sreg.TryGetValue(Constants.gender, out genderString)) {
+ //// builder.Append(createAddFieldJS(Constants.gender, genderString));
+ //// }
+ //// if (sreg.TryGetValue(Constants.postcode, out postalCode)) {
+ //// builder.Append(createAddFieldJS(Constants.postcode, postalCode));
+ //// }
+ //// if (sreg.TryGetValue(Constants.country, out country)) {
+ //// builder.Append(createAddFieldJS(Constants.country, country));
+ //// }
+ //// if (sreg.TryGetValue(Constants.language, out language)) {
+ //// builder.Append(createAddFieldJS(Constants.language, language));
+ //// }
+ //// if (sreg.TryGetValue(Constants.timezone, out timeZone)) {
+ //// builder.Append(createAddFieldJS(Constants.timezone, timeZone));
+ //// }
+ //// if (builder[builder.Length - 1] == ',') builder.Length -= 1;
+ //// builder.Append("}");
+ //// return builder.ToString();
+ ////}
+
+ #endregion
+
+ /// <summary>
+ /// Tests equality of two <see cref="ClaimsResponse"/> objects.
+ /// </summary>
+ /// <param name="one">One instance to compare.</param>
+ /// <param name="other">Another instance to compare.</param>
+ /// <returns>The result of the operator.</returns>
+ public static bool operator ==(ClaimsResponse one, ClaimsResponse other) {
+ return one.EqualsNullSafe(other);
+ }
+
+ /// <summary>
+ /// Tests inequality of two <see cref="ClaimsResponse"/> objects.
+ /// </summary>
+ /// <param name="one">One instance to compare.</param>
+ /// <param name="other">Another instance to compare.</param>
+ /// <returns>The result of the operator.</returns>
+ public static bool operator !=(ClaimsResponse one, ClaimsResponse other) {
+ return !(one == other);
+ }
+
+ /// <summary>
+ /// Tests equality of two <see cref="ClaimsResponse"/> objects.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
+ public override bool Equals(object obj) {
+ ClaimsResponse other = obj as ClaimsResponse;
+ if (other == null) {
+ return false;
+ }
+
+ return
+ this.BirthDateRaw.EqualsNullSafe(other.BirthDateRaw) &&
+ this.Country.EqualsNullSafe(other.Country) &&
+ this.Language.EqualsNullSafe(other.Language) &&
+ this.Email.EqualsNullSafe(other.Email) &&
+ this.FullName.EqualsNullSafe(other.FullName) &&
+ this.Gender.Equals(other.Gender) &&
+ this.Nickname.EqualsNullSafe(other.Nickname) &&
+ this.PostalCode.EqualsNullSafe(other.PostalCode) &&
+ this.TimeZone.EqualsNullSafe(other.TimeZone);
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a particular type.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ return (this.Nickname != null) ? this.Nickname.GetHashCode() : base.GetHashCode();
+ }
+ }
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs
index 2d8413f..544ba77 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs
@@ -1,38 +1,38 @@
-// <auto-generated/> // disable StyleCop on this file
-//-----------------------------------------------------------------------
-// <copyright file="Constants.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- /// <summary>
- /// Simple Registration constants
- /// </summary>
- internal static class Constants {
- internal const string sreg_ns = "http://openid.net/extensions/sreg/1.1";
- internal const string sreg_ns10 = "http://openid.net/sreg/1.0";
- internal const string sreg_ns11other = "http://openid.net/sreg/1.1";
- internal const string sreg_compatibility_alias = "sreg";
- internal const string policy_url = "policy_url";
- internal const string optional = "optional";
- internal const string required = "required";
- internal const string nickname = "nickname";
- internal const string email = "email";
- internal const string fullname = "fullname";
- internal const string dob = "dob";
- internal const string gender = "gender";
- internal const string postcode = "postcode";
- internal const string country = "country";
- internal const string language = "language";
- internal const string timezone = "timezone";
- internal static class Genders {
- internal const string Male = "M";
- internal const string Female = "F";
- }
- }
-}
+// <auto-generated/> // disable StyleCop on this file
+//-----------------------------------------------------------------------
+// <copyright file="Constants.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ /// <summary>
+ /// Simple Registration constants
+ /// </summary>
+ internal static class Constants {
+ internal const string sreg_ns = "http://openid.net/extensions/sreg/1.1";
+ internal const string sreg_ns10 = "http://openid.net/sreg/1.0";
+ internal const string sreg_ns11other = "http://openid.net/sreg/1.1";
+ internal const string sreg_compatibility_alias = "sreg";
+ internal const string policy_url = "policy_url";
+ internal const string optional = "optional";
+ internal const string required = "required";
+ internal const string nickname = "nickname";
+ internal const string email = "email";
+ internal const string fullname = "fullname";
+ internal const string dob = "dob";
+ internal const string gender = "gender";
+ internal const string postcode = "postcode";
+ internal const string country = "country";
+ internal const string language = "language";
+ internal const string timezone = "timezone";
+ internal static class Genders {
+ internal const string Male = "M";
+ internal const string Female = "F";
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs
index 49e3a10..7129270 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs
@@ -1,32 +1,32 @@
-//-----------------------------------------------------------------------
-// <copyright file="DemandLevel.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
- /// <summary>
- /// Specifies what level of interest a relying party has in obtaining the value
- /// of a given field offered by the Simple Registration extension.
- /// </summary>
- public enum DemandLevel {
- /// <summary>
- /// The relying party has no interest in obtaining this field.
- /// </summary>
- NoRequest,
-
- /// <summary>
- /// The relying party would like the value of this field, but wants
- /// the Provider to display the field to the user as optionally provided.
- /// </summary>
- Request,
-
- /// <summary>
- /// The relying party considers this a required field as part of
- /// authentication. The Provider and/or user agent MAY still choose to
- /// not provide the value of the field however, according to the
- /// Simple Registration extension specification.
- /// </summary>
- Require,
- }
+//-----------------------------------------------------------------------
+// <copyright file="DemandLevel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ /// <summary>
+ /// Specifies what level of interest a relying party has in obtaining the value
+ /// of a given field offered by the Simple Registration extension.
+ /// </summary>
+ public enum DemandLevel {
+ /// <summary>
+ /// The relying party has no interest in obtaining this field.
+ /// </summary>
+ NoRequest,
+
+ /// <summary>
+ /// The relying party would like the value of this field, but wants
+ /// the Provider to display the field to the user as optionally provided.
+ /// </summary>
+ Request,
+
+ /// <summary>
+ /// The relying party considers this a required field as part of
+ /// authentication. The Provider and/or user agent MAY still choose to
+ /// not provide the value of the field however, according to the
+ /// Simple Registration extension specification.
+ /// </summary>
+ Require,
+ }
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs
index 0f7f586..3cc47c6 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs
@@ -1,70 +1,70 @@
-//-----------------------------------------------------------------------
-// <copyright file="Gender.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
- using System;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
-
- /// <summary>
- /// Indicates the gender of a user.
- /// </summary>
- public enum Gender {
- /// <summary>
- /// The user is male.
- /// </summary>
- Male,
-
- /// <summary>
- /// The user is female.
- /// </summary>
- Female,
- }
-
- /// <summary>
- /// Encodes/decodes the Simple Registration Gender type to its string representation.
- /// </summary>
- internal class GenderEncoder : IMessagePartEncoder {
- #region IMessagePartEncoder Members
-
- /// <summary>
- /// Encodes the specified value.
- /// </summary>
- /// <param name="value">The value. Guaranteed to never be null.</param>
- /// <returns>
- /// The <paramref name="value"/> in string form, ready for message transport.
- /// </returns>
- public string Encode(object value) {
- Gender? gender = (Gender?)value;
- if (gender.HasValue) {
- switch (gender.Value) {
- case Gender.Male: return Constants.Genders.Male;
- case Gender.Female: return Constants.Genders.Female;
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Decodes the specified value.
- /// </summary>
- /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
- /// <returns>
- /// The deserialized form of the given string.
- /// </returns>
- /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
- public object Decode(string value) {
- switch (value) {
- case Constants.Genders.Male: return SimpleRegistration.Gender.Male;
- case Constants.Genders.Female: return SimpleRegistration.Gender.Female;
- default: throw new FormatException();
- }
- }
-
- #endregion
- }
+//-----------------------------------------------------------------------
+// <copyright file="Gender.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ using System;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ /// <summary>
+ /// Indicates the gender of a user.
+ /// </summary>
+ public enum Gender {
+ /// <summary>
+ /// The user is male.
+ /// </summary>
+ Male,
+
+ /// <summary>
+ /// The user is female.
+ /// </summary>
+ Female,
+ }
+
+ /// <summary>
+ /// Encodes/decodes the Simple Registration Gender type to its string representation.
+ /// </summary>
+ internal class GenderEncoder : IMessagePartEncoder {
+ #region IMessagePartEncoder Members
+
+ /// <summary>
+ /// Encodes the specified value.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>
+ /// The <paramref name="value"/> in string form, ready for message transport.
+ /// </returns>
+ public string Encode(object value) {
+ Gender? gender = (Gender?)value;
+ if (gender.HasValue) {
+ switch (gender.Value) {
+ case Gender.Male: return Constants.Genders.Male;
+ case Gender.Female: return Constants.Genders.Female;
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Decodes the specified value.
+ /// </summary>
+ /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
+ /// <returns>
+ /// The deserialized form of the given string.
+ /// </returns>
+ /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
+ public object Decode(string value) {
+ switch (value) {
+ case Constants.Genders.Male: return SimpleRegistration.Gender.Male;
+ case Constants.Genders.Female: return SimpleRegistration.Gender.Female;
+ default: throw new FormatException();
+ }
+ }
+
+ #endregion
+ }
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
index aaaa221..f2806c4 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
@@ -1,151 +1,151 @@
-//-----------------------------------------------------------------------
-// <copyright file="AssociateRequest.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.RelyingParty;
-
- /// <summary>
- /// An OpenID direct request from Relying Party to Provider to initiate an association.
- /// </summary>
- [DebuggerDisplay("OpenID {Version} {Mode} {AssociationType} {SessionType}")]
- internal abstract class AssociateRequest : RequestBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="AssociateRequest"/> class.
- /// </summary>
- /// <param name="version">The OpenID version this message must comply with.</param>
- /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
- protected AssociateRequest(Version version, Uri providerEndpoint)
- : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.associate), MessageTransport.Direct) {
- }
-
- /// <summary>
- /// Gets or sets the preferred association type. The association type defines the algorithm to be used to sign subsequent messages.
- /// </summary>
- /// <value>Value: A valid association type from Section 8.3.</value>
- [MessagePart("openid.assoc_type", IsRequired = true, AllowEmpty = false)]
- internal string AssociationType { get; set; }
-
- /// <summary>
- /// Gets or sets the preferred association session type. This defines the method used to encrypt the association's MAC key in transit.
- /// </summary>
- /// <value>Value: A valid association session type from Section 8.4 (Association Session Types). </value>
- /// <remarks>Note: Unless using transport layer encryption, "no-encryption" MUST NOT be used. </remarks>
- [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = true)]
- [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
- internal string SessionType { get; set; }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public override void EnsureValidMessage() {
- base.EnsureValidMessage();
-
- ErrorUtilities.VerifyProtocol(
- !string.Equals(this.SessionType, Protocol.Args.SessionType.NoEncryption, StringComparison.Ordinal) || this.Recipient.IsTransportSecure(),
- OpenIdStrings.NoEncryptionSessionRequiresHttps,
- this);
- }
-
- /// <summary>
- /// Creates an association request message that is appropriate for a given Provider.
- /// </summary>
- /// <param name="securityRequirements">The set of requirements the selected association type must comply to.</param>
- /// <param name="provider">The provider to create an association with.</param>
- /// <returns>
- /// The message to send to the Provider to request an association.
- /// Null if no association could be created that meet the security requirements
- /// and the provider OpenID version.
- /// </returns>
- internal static AssociateRequest Create(SecuritySettings securityRequirements, ProviderEndpointDescription provider) {
- ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
-
- AssociateRequest associateRequest;
-
- // Apply our knowledge of the endpoint's transport, OpenID version, and
- // security requirements to decide the best association
- bool unencryptedAllowed = provider.Endpoint.IsTransportSecure();
- bool useDiffieHellman = !unencryptedAllowed;
- string associationType, sessionType;
- if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), securityRequirements, useDiffieHellman, out associationType, out sessionType)) {
- // There are no associations that meet all requirements.
- Logger.Warn("Security requirements and protocol combination knock out all possible association types. Dumb mode forced.");
- return null;
- }
-
- if (unencryptedAllowed) {
- associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint);
- associateRequest.AssociationType = associationType;
- } else {
- var diffieHellmanAssociateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint);
- diffieHellmanAssociateRequest.AssociationType = associationType;
- diffieHellmanAssociateRequest.SessionType = sessionType;
- diffieHellmanAssociateRequest.InitializeRequest();
- associateRequest = diffieHellmanAssociateRequest;
- }
-
- return associateRequest;
- }
-
- /// <summary>
- /// Creates a Provider's response to an incoming association request.
- /// </summary>
- /// <param name="associationStore">The association store where a new association (if created) will be stored. Must not be null.</param>
- /// <returns>
- /// The appropriate association response that is ready to be sent back to the Relying Party.
- /// </returns>
- /// <remarks>
- /// <para>If an association is created, it will be automatically be added to the provided
- /// association store.</para>
- /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
- /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
- /// </remarks>
- internal IProtocolMessage CreateResponse(IAssociationStore<AssociationRelyingPartyType> associationStore) {
- ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
-
- var response = this.CreateResponseCore();
-
- // Create and store the association if this is a successful response.
- var successResponse = response as AssociateSuccessfulResponse;
- if (successResponse != null) {
- Association association = successResponse.CreateAssociation(this);
- associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association);
- }
-
- return response;
- }
-
- /// <summary>
- /// Creates a Provider's response to an incoming association request.
- /// </summary>
- /// <returns>
- /// The appropriate association response message.
- /// </returns>
- /// <remarks>
- /// <para>If an association can be successfully created, the
- /// <see cref="AssociateSuccessfulResponse.CreateAssociation"/> method must not be
- /// called by this method.</para>
- /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
- /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
- /// </remarks>
- protected abstract IProtocolMessage CreateResponseCore();
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="AssociateRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ /// <summary>
+ /// An OpenID direct request from Relying Party to Provider to initiate an association.
+ /// </summary>
+ [DebuggerDisplay("OpenID {Version} {Mode} {AssociationType} {SessionType}")]
+ internal abstract class AssociateRequest : RequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssociateRequest"/> class.
+ /// </summary>
+ /// <param name="version">The OpenID version this message must comply with.</param>
+ /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
+ protected AssociateRequest(Version version, Uri providerEndpoint)
+ : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.associate), MessageTransport.Direct) {
+ }
+
+ /// <summary>
+ /// Gets or sets the preferred association type. The association type defines the algorithm to be used to sign subsequent messages.
+ /// </summary>
+ /// <value>Value: A valid association type from Section 8.3.</value>
+ [MessagePart("openid.assoc_type", IsRequired = true, AllowEmpty = false)]
+ internal string AssociationType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the preferred association session type. This defines the method used to encrypt the association's MAC key in transit.
+ /// </summary>
+ /// <value>Value: A valid association session type from Section 8.4 (Association Session Types). </value>
+ /// <remarks>Note: Unless using transport layer encryption, "no-encryption" MUST NOT be used. </remarks>
+ [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = true)]
+ [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
+ internal string SessionType { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+
+ ErrorUtilities.VerifyProtocol(
+ !string.Equals(this.SessionType, Protocol.Args.SessionType.NoEncryption, StringComparison.Ordinal) || this.Recipient.IsTransportSecure(),
+ OpenIdStrings.NoEncryptionSessionRequiresHttps,
+ this);
+ }
+
+ /// <summary>
+ /// Creates an association request message that is appropriate for a given Provider.
+ /// </summary>
+ /// <param name="securityRequirements">The set of requirements the selected association type must comply to.</param>
+ /// <param name="provider">The provider to create an association with.</param>
+ /// <returns>
+ /// The message to send to the Provider to request an association.
+ /// Null if no association could be created that meet the security requirements
+ /// and the provider OpenID version.
+ /// </returns>
+ internal static AssociateRequest Create(SecuritySettings securityRequirements, ProviderEndpointDescription provider) {
+ ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
+ ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+
+ AssociateRequest associateRequest;
+
+ // Apply our knowledge of the endpoint's transport, OpenID version, and
+ // security requirements to decide the best association
+ bool unencryptedAllowed = provider.Endpoint.IsTransportSecure();
+ bool useDiffieHellman = !unencryptedAllowed;
+ string associationType, sessionType;
+ if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), securityRequirements, useDiffieHellman, out associationType, out sessionType)) {
+ // There are no associations that meet all requirements.
+ Logger.Warn("Security requirements and protocol combination knock out all possible association types. Dumb mode forced.");
+ return null;
+ }
+
+ if (unencryptedAllowed) {
+ associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint);
+ associateRequest.AssociationType = associationType;
+ } else {
+ var diffieHellmanAssociateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint);
+ diffieHellmanAssociateRequest.AssociationType = associationType;
+ diffieHellmanAssociateRequest.SessionType = sessionType;
+ diffieHellmanAssociateRequest.InitializeRequest();
+ associateRequest = diffieHellmanAssociateRequest;
+ }
+
+ return associateRequest;
+ }
+
+ /// <summary>
+ /// Creates a Provider's response to an incoming association request.
+ /// </summary>
+ /// <param name="associationStore">The association store where a new association (if created) will be stored. Must not be null.</param>
+ /// <returns>
+ /// The appropriate association response that is ready to be sent back to the Relying Party.
+ /// </returns>
+ /// <remarks>
+ /// <para>If an association is created, it will be automatically be added to the provided
+ /// association store.</para>
+ /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
+ /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
+ /// </remarks>
+ internal IProtocolMessage CreateResponse(IAssociationStore<AssociationRelyingPartyType> associationStore) {
+ ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
+
+ var response = this.CreateResponseCore();
+
+ // Create and store the association if this is a successful response.
+ var successResponse = response as AssociateSuccessfulResponse;
+ if (successResponse != null) {
+ Association association = successResponse.CreateAssociation(this);
+ associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association);
+ }
+
+ return response;
+ }
+
+ /// <summary>
+ /// Creates a Provider's response to an incoming association request.
+ /// </summary>
+ /// <returns>
+ /// The appropriate association response message.
+ /// </returns>
+ /// <remarks>
+ /// <para>If an association can be successfully created, the
+ /// <see cref="AssociateSuccessfulResponse.CreateAssociation"/> method must not be
+ /// called by this method.</para>
+ /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
+ /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
+ /// </remarks>
+ protected abstract IProtocolMessage CreateResponseCore();
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs
index 9563a8a..424d041 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs
@@ -1,146 +1,146 @@
-//-----------------------------------------------------------------------
-// <copyright file="AssociateSuccessfulResponse.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// The base class that all successful association response messages derive from.
- /// </summary>
- /// <remarks>
- /// Association response messages are described in OpenID 2.0 section 8.2. This type covers section 8.2.1.
- /// </remarks>
- [DebuggerDisplay("OpenID {Version} associate response {AssociationHandle} {AssociationType} {SessionType}")]
- internal abstract class AssociateSuccessfulResponse : DirectResponseBase {
- /// <summary>
- /// A flag indicating whether an association has already been created.
- /// </summary>
- private bool associationCreated;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="AssociateSuccessfulResponse"/> class.
- /// </summary>
- /// <param name="originatingRequest">The originating request.</param>
- internal AssociateSuccessfulResponse(AssociateRequest originatingRequest)
- : base(originatingRequest) {
- }
-
- /// <summary>
- /// Gets or sets the association handle is used as a key to refer to this association in subsequent messages.
- /// </summary>
- /// <value>A string 255 characters or less in length. It MUST consist only of ASCII characters in the range 33-126 inclusive (printable non-whitespace characters). </value>
- [MessagePart("assoc_handle", IsRequired = true, AllowEmpty = false)]
- internal string AssociationHandle { get; set; }
-
- /// <summary>
- /// Gets or sets the preferred association type. The association type defines the algorithm to be used to sign subsequent messages.
- /// </summary>
- /// <value>Value: A valid association type from Section 8.3.</value>
- [MessagePart("assoc_type", IsRequired = true, AllowEmpty = false)]
- internal string AssociationType { get; set; }
-
- /// <summary>
- /// Gets or sets the value of the "openid.session_type" parameter from the request.
- /// If the OP is unwilling or unable to support this association type, it MUST return an
- /// unsuccessful response (Unsuccessful Response Parameters).
- /// </summary>
- /// <value>Value: A valid association session type from Section 8.4 (Association Session Types). </value>
- /// <remarks>Note: Unless using transport layer encryption, "no-encryption" MUST NOT be used. </remarks>
- [MessagePart("session_type", IsRequired = false, AllowEmpty = true)]
- [MessagePart("session_type", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
- internal string SessionType { get; set; }
-
- /// <summary>
- /// Gets or sets the lifetime, in seconds, of this association. The Relying Party MUST NOT use the association after this time has passed.
- /// </summary>
- /// <value>An integer, represented in base 10 ASCII. </value>
- [MessagePart("expires_in", IsRequired = true)]
- internal long ExpiresIn { get; set; }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public override void EnsureValidMessage() {
- base.EnsureValidMessage();
-
- if (this.Version.Major < 2) {
- ErrorUtilities.VerifyProtocol(
- string.IsNullOrEmpty(this.SessionType) || string.Equals(this.SessionType, this.Protocol.Args.SessionType.DH_SHA1, StringComparison.Ordinal),
- MessagingStrings.UnexpectedMessagePartValueForConstant,
- GetType().Name,
- Protocol.openid.session_type,
- this.Protocol.Args.SessionType.DH_SHA1,
- this.SessionType);
- }
- }
-
- /// <summary>
- /// Called to create the Association based on a request previously given by the Relying Party.
- /// </summary>
- /// <param name="request">The prior request for an association.</param>
- /// <returns>The created association.</returns>
- /// <remarks>
- /// <para>The response message is updated to include the details of the created association by this method,
- /// but the resulting association is <i>not</i> added to the association store and must be done by the caller.</para>
- /// <para>This method is called by both the Provider and the Relying Party, but actually performs
- /// quite different operations in either scenario.</para>
- /// </remarks>
- internal Association CreateAssociation(AssociateRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyInternal(!this.associationCreated, "The association has already been created.");
- Association association;
-
- // If this message is outgoing, then we need to initialize some common
- // properties based on the created association.
- if (this.Incoming) {
- association = this.CreateAssociationAtRelyingParty(request);
- } else {
- association = this.CreateAssociationAtProvider(request);
- this.ExpiresIn = association.SecondsTillExpiration;
- this.AssociationHandle = association.Handle;
- }
-
- this.associationCreated = true;
-
- return association;
- }
-
- /// <summary>
- /// Called to create the Association based on a request previously given by the Relying Party.
- /// </summary>
- /// <param name="request">The prior request for an association.</param>
- /// <returns>The created association.</returns>
- /// <remarks>
- /// <para>The caller will update this message's <see cref="ExpiresIn"/> and <see cref="AssociationHandle"/>
- /// properties based on the <see cref="Association"/> returned by this method, but any other
- /// association type specific properties must be set by this method.</para>
- /// <para>The response message is updated to include the details of the created association by this method,
- /// but the resulting association is <i>not</i> added to the association store and must be done by the caller.</para>
- /// </remarks>
- protected abstract Association CreateAssociationAtProvider(AssociateRequest request);
-
- /// <summary>
- /// Called to create the Association based on a request previously given by the Relying Party.
- /// </summary>
- /// <param name="request">The prior request for an association.</param>
- /// <returns>The created association.</returns>
- protected abstract Association CreateAssociationAtRelyingParty(AssociateRequest request);
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="AssociateSuccessfulResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The base class that all successful association response messages derive from.
+ /// </summary>
+ /// <remarks>
+ /// Association response messages are described in OpenID 2.0 section 8.2. This type covers section 8.2.1.
+ /// </remarks>
+ [DebuggerDisplay("OpenID {Version} associate response {AssociationHandle} {AssociationType} {SessionType}")]
+ internal abstract class AssociateSuccessfulResponse : DirectResponseBase {
+ /// <summary>
+ /// A flag indicating whether an association has already been created.
+ /// </summary>
+ private bool associationCreated;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssociateSuccessfulResponse"/> class.
+ /// </summary>
+ /// <param name="originatingRequest">The originating request.</param>
+ internal AssociateSuccessfulResponse(AssociateRequest originatingRequest)
+ : base(originatingRequest) {
+ }
+
+ /// <summary>
+ /// Gets or sets the association handle is used as a key to refer to this association in subsequent messages.
+ /// </summary>
+ /// <value>A string 255 characters or less in length. It MUST consist only of ASCII characters in the range 33-126 inclusive (printable non-whitespace characters). </value>
+ [MessagePart("assoc_handle", IsRequired = true, AllowEmpty = false)]
+ internal string AssociationHandle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the preferred association type. The association type defines the algorithm to be used to sign subsequent messages.
+ /// </summary>
+ /// <value>Value: A valid association type from Section 8.3.</value>
+ [MessagePart("assoc_type", IsRequired = true, AllowEmpty = false)]
+ internal string AssociationType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value of the "openid.session_type" parameter from the request.
+ /// If the OP is unwilling or unable to support this association type, it MUST return an
+ /// unsuccessful response (Unsuccessful Response Parameters).
+ /// </summary>
+ /// <value>Value: A valid association session type from Section 8.4 (Association Session Types). </value>
+ /// <remarks>Note: Unless using transport layer encryption, "no-encryption" MUST NOT be used. </remarks>
+ [MessagePart("session_type", IsRequired = false, AllowEmpty = true)]
+ [MessagePart("session_type", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
+ internal string SessionType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lifetime, in seconds, of this association. The Relying Party MUST NOT use the association after this time has passed.
+ /// </summary>
+ /// <value>An integer, represented in base 10 ASCII. </value>
+ [MessagePart("expires_in", IsRequired = true)]
+ internal long ExpiresIn { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+
+ if (this.Version.Major < 2) {
+ ErrorUtilities.VerifyProtocol(
+ string.IsNullOrEmpty(this.SessionType) || string.Equals(this.SessionType, this.Protocol.Args.SessionType.DH_SHA1, StringComparison.Ordinal),
+ MessagingStrings.UnexpectedMessagePartValueForConstant,
+ GetType().Name,
+ Protocol.openid.session_type,
+ this.Protocol.Args.SessionType.DH_SHA1,
+ this.SessionType);
+ }
+ }
+
+ /// <summary>
+ /// Called to create the Association based on a request previously given by the Relying Party.
+ /// </summary>
+ /// <param name="request">The prior request for an association.</param>
+ /// <returns>The created association.</returns>
+ /// <remarks>
+ /// <para>The response message is updated to include the details of the created association by this method,
+ /// but the resulting association is <i>not</i> added to the association store and must be done by the caller.</para>
+ /// <para>This method is called by both the Provider and the Relying Party, but actually performs
+ /// quite different operations in either scenario.</para>
+ /// </remarks>
+ internal Association CreateAssociation(AssociateRequest request) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ ErrorUtilities.VerifyInternal(!this.associationCreated, "The association has already been created.");
+ Association association;
+
+ // If this message is outgoing, then we need to initialize some common
+ // properties based on the created association.
+ if (this.Incoming) {
+ association = this.CreateAssociationAtRelyingParty(request);
+ } else {
+ association = this.CreateAssociationAtProvider(request);
+ this.ExpiresIn = association.SecondsTillExpiration;
+ this.AssociationHandle = association.Handle;
+ }
+
+ this.associationCreated = true;
+
+ return association;
+ }
+
+ /// <summary>
+ /// Called to create the Association based on a request previously given by the Relying Party.
+ /// </summary>
+ /// <param name="request">The prior request for an association.</param>
+ /// <returns>The created association.</returns>
+ /// <remarks>
+ /// <para>The caller will update this message's <see cref="ExpiresIn"/> and <see cref="AssociationHandle"/>
+ /// properties based on the <see cref="Association"/> returned by this method, but any other
+ /// association type specific properties must be set by this method.</para>
+ /// <para>The response message is updated to include the details of the created association by this method,
+ /// but the resulting association is <i>not</i> added to the association store and must be done by the caller.</para>
+ /// </remarks>
+ protected abstract Association CreateAssociationAtProvider(AssociateRequest request);
+
+ /// <summary>
+ /// Called to create the Association based on a request previously given by the Relying Party.
+ /// </summary>
+ /// <param name="request">The prior request for an association.</param>
+ /// <returns>The created association.</returns>
+ protected abstract Association CreateAssociationAtRelyingParty(AssociateRequest request);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs
index fd5a66b..e24dbf5 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateUnsuccessfulResponse.cs
@@ -1,50 +1,50 @@
-//-----------------------------------------------------------------------
-// <copyright file="AssociateUnsuccessfulResponse.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// The Provider's response to a Relying Party that requested an association that the Provider does not support.
- /// </summary>
- /// <remarks>
- /// This message type described in OpenID 2.0 section 8.2.4.
- /// </remarks>
- [DebuggerDisplay("OpenID {Version} associate (failed) response")]
- internal class AssociateUnsuccessfulResponse : DirectErrorResponse {
- /// <summary>
- /// A hard-coded string indicating an error occurred.
- /// </summary>
- /// <value>"unsupported-type" </value>
- [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection")]
- [MessagePart("error_code", IsRequired = true, AllowEmpty = false)]
-#pragma warning disable 0414 // read by reflection
- private readonly string Error = "unsupported-type";
-#pragma warning restore 0414
-
- /// <summary>
- /// Initializes a new instance of the <see cref="AssociateUnsuccessfulResponse"/> class.
- /// </summary>
- /// <param name="originatingRequest">The originating request.</param>
- internal AssociateUnsuccessfulResponse(AssociateRequest originatingRequest)
- : base(originatingRequest) {
- }
-
- /// <summary>
- /// Gets or sets an association type supported by the OP from Section 8.3 (Association Types).
- /// </summary>
- [MessagePart("assoc_type", IsRequired = false, AllowEmpty = false)]
- internal string AssociationType { get; set; }
-
- /// <summary>
- /// Gets or sets a valid association session type from Section 8.4 (Association Session Types) that the OP supports.
- /// </summary>
- [MessagePart("session_type", IsRequired = false, AllowEmpty = false)]
- internal string SessionType { get; set; }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="AssociateUnsuccessfulResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The Provider's response to a Relying Party that requested an association that the Provider does not support.
+ /// </summary>
+ /// <remarks>
+ /// This message type described in OpenID 2.0 section 8.2.4.
+ /// </remarks>
+ [DebuggerDisplay("OpenID {Version} associate (failed) response")]
+ internal class AssociateUnsuccessfulResponse : DirectErrorResponse {
+ /// <summary>
+ /// A hard-coded string indicating an error occurred.
+ /// </summary>
+ /// <value>"unsupported-type" </value>
+ [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection")]
+ [MessagePart("error_code", IsRequired = true, AllowEmpty = false)]
+#pragma warning disable 0414 // read by reflection
+ private readonly string Error = "unsupported-type";
+#pragma warning restore 0414
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssociateUnsuccessfulResponse"/> class.
+ /// </summary>
+ /// <param name="originatingRequest">The originating request.</param>
+ internal AssociateUnsuccessfulResponse(AssociateRequest originatingRequest)
+ : base(originatingRequest) {
+ }
+
+ /// <summary>
+ /// Gets or sets an association type supported by the OP from Section 8.3 (Association Types).
+ /// </summary>
+ [MessagePart("assoc_type", IsRequired = false, AllowEmpty = false)]
+ internal string AssociationType { get; set; }
+
+ /// <summary>
+ /// Gets or sets a valid association session type from Section 8.4 (Association Session Types) that the OP supports.
+ /// </summary>
+ [MessagePart("session_type", IsRequired = false, AllowEmpty = false)]
+ internal string SessionType { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
index ae90882..22da6d4 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
@@ -1,75 +1,75 @@
-//-----------------------------------------------------------------------
-// <copyright file="CheckAuthenticationRequest.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId.ChannelElements;
-
- /// <summary>
- /// A message a Relying Party sends to a Provider to confirm the validity
- /// of a positive assertion that was signed by a Provider-only secret.
- /// </summary>
- /// <remarks>
- /// The significant payload of this message depends entirely upon the
- /// assertion message, and therefore is all in the
- /// <see cref="DotNetOpenAuth.Messaging.IMessage.ExtraData"/> property bag.
- /// </remarks>
- internal class CheckAuthenticationRequest : RequestBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class.
- /// </summary>
- /// <param name="version">The OpenID version this message must comply with.</param>
- /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
- internal CheckAuthenticationRequest(Version version, Uri providerEndpoint)
- : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class
- /// based on the contents of some signed message whose signature must be verified.
- /// </summary>
- /// <param name="message">The message whose signature should be verified.</param>
- internal CheckAuthenticationRequest(IndirectSignedResponse message)
- : base(message.Version, message.ProviderEndpoint, GetProtocolConstant(message.Version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
- // Copy all message parts from the id_res message into this one,
- // except for the openid.mode parameter.
- MessageDictionary checkPayload = new MessageDictionary(message);
- MessageDictionary thisPayload = new MessageDictionary(this);
- foreach (var pair in checkPayload) {
- if (!string.Equals(pair.Key, this.Protocol.openid.mode)) {
- thisPayload[pair.Key] = pair.Value;
- }
- }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether the signature being verified by this request
- /// is in fact valid.
- /// </summary>
- /// <value><c>true</c> if the signature is valid; otherwise, <c>false</c>.</value>
- /// <remarks>
- /// This property is automatically set as the message is received by the channel's
- /// signing binding element.
- /// </remarks>
- internal bool IsValid { get; set; }
-
- /// <summary>
- /// Gets or sets the ReturnTo that existed in the original signed message.
- /// </summary>
- /// <remarks>
- /// This exists strictly for convenience in recreating the <see cref="IndirectSignedResponse"/>
- /// message.
- /// </remarks>
- [MessagePart("openid.return_to", IsRequired = true, AllowEmpty = false)]
- [MessagePart("openid.return_to", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")]
- internal Uri ReturnTo { get; set; }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="CheckAuthenticationRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ /// <summary>
+ /// A message a Relying Party sends to a Provider to confirm the validity
+ /// of a positive assertion that was signed by a Provider-only secret.
+ /// </summary>
+ /// <remarks>
+ /// The significant payload of this message depends entirely upon the
+ /// assertion message, and therefore is all in the
+ /// <see cref="DotNetOpenAuth.Messaging.IMessage.ExtraData"/> property bag.
+ /// </remarks>
+ internal class CheckAuthenticationRequest : RequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class.
+ /// </summary>
+ /// <param name="version">The OpenID version this message must comply with.</param>
+ /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
+ internal CheckAuthenticationRequest(Version version, Uri providerEndpoint)
+ : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class
+ /// based on the contents of some signed message whose signature must be verified.
+ /// </summary>
+ /// <param name="message">The message whose signature should be verified.</param>
+ internal CheckAuthenticationRequest(IndirectSignedResponse message)
+ : base(message.Version, message.ProviderEndpoint, GetProtocolConstant(message.Version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
+ // Copy all message parts from the id_res message into this one,
+ // except for the openid.mode parameter.
+ MessageDictionary checkPayload = new MessageDictionary(message);
+ MessageDictionary thisPayload = new MessageDictionary(this);
+ foreach (var pair in checkPayload) {
+ if (!string.Equals(pair.Key, this.Protocol.openid.mode)) {
+ thisPayload[pair.Key] = pair.Value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the signature being verified by this request
+ /// is in fact valid.
+ /// </summary>
+ /// <value><c>true</c> if the signature is valid; otherwise, <c>false</c>.</value>
+ /// <remarks>
+ /// This property is automatically set as the message is received by the channel's
+ /// signing binding element.
+ /// </remarks>
+ internal bool IsValid { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ReturnTo that existed in the original signed message.
+ /// </summary>
+ /// <remarks>
+ /// This exists strictly for convenience in recreating the <see cref="IndirectSignedResponse"/>
+ /// message.
+ /// </remarks>
+ [MessagePart("openid.return_to", IsRequired = true, AllowEmpty = false)]
+ [MessagePart("openid.return_to", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")]
+ internal Uri ReturnTo { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs b/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
index 503a1d8..ca4be62 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
@@ -1,148 +1,148 @@
-//-----------------------------------------------------------------------
-// <copyright file="DirectResponseBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A common base class for OpenID direct message responses.
- /// </summary>
- [DebuggerDisplay("OpenID {Version} response")]
- internal class DirectResponseBase : IDirectResponseProtocolMessage {
- /// <summary>
- /// The openid.ns parameter in the message.
- /// </summary>
- /// <value>"http://specs.openid.net/auth/2.0" </value>
- /// <remarks>
- /// OpenID 2.0 Section 5.1.2:
- /// This particular value MUST be present for the response to be a valid OpenID 2.0 response.
- /// Future versions of the specification may define different values in order to allow message
- /// recipients to properly interpret the request.
- /// </remarks>
- [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
- [MessagePart("ns", IsRequired = true, AllowEmpty = false)]
-#pragma warning disable 0414 // read by reflection
- private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
-#pragma warning restore 0414
-
- /// <summary>
- /// Backing store for the <see cref="OriginatingRequest"/> properties.
- /// </summary>
- private IDirectedProtocolMessage originatingRequest;
-
- /// <summary>
- /// Backing store for the <see cref="Incoming"/> properties.
- /// </summary>
- private bool incoming;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DirectResponseBase"/> class.
- /// </summary>
- /// <param name="originatingRequest">The originating request.</param>
- protected DirectResponseBase(IDirectedProtocolMessage originatingRequest) {
- ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
-
- this.originatingRequest = originatingRequest;
- this.Version = originatingRequest.Version;
- }
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- /// <value>Version 2.0</value>
- public Version Version { get; private set; }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- /// <value><see cref="MessageProtections.None"/></value>
- public MessageProtections RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- /// <value><see cref="MessageTransport.Direct"/></value>
- public MessageTransport Transport {
- get { return MessageTransport.Direct; }
- }
-
- /// <summary>
- /// Gets the extra, non-OAuth parameters included in the message.
- /// </summary>
- /// <value>An empty dictionary.</value>
- public IDictionary<string, string> ExtraData {
- get { return EmptyDictionary<string, string>.Instance; }
- }
-
- #endregion
-
- #region IDirectResponseProtocolMessage Members
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- protected internal bool Incoming {
- get { return this.incoming; }
- }
-
- /// <summary>
- /// Gets the protocol used by this message.
- /// </summary>
- protected Protocol Protocol {
- get { return Protocol.Lookup(this.Version); }
- }
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- protected IDirectedProtocolMessage OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- #region IProtocolMessage methods
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public virtual void EnsureValidMessage() {
- }
-
- #endregion
-
- /// <summary>
- /// Sets a flag indicating that this message is received (as opposed to sent).
- /// </summary>
- internal void SetAsIncoming() {
- this.incoming = true;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="DirectResponseBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A common base class for OpenID direct message responses.
+ /// </summary>
+ [DebuggerDisplay("OpenID {Version} response")]
+ internal class DirectResponseBase : IDirectResponseProtocolMessage {
+ /// <summary>
+ /// The openid.ns parameter in the message.
+ /// </summary>
+ /// <value>"http://specs.openid.net/auth/2.0" </value>
+ /// <remarks>
+ /// OpenID 2.0 Section 5.1.2:
+ /// This particular value MUST be present for the response to be a valid OpenID 2.0 response.
+ /// Future versions of the specification may define different values in order to allow message
+ /// recipients to properly interpret the request.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
+ [MessagePart("ns", IsRequired = true, AllowEmpty = false)]
+#pragma warning disable 0414 // read by reflection
+ private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
+#pragma warning restore 0414
+
+ /// <summary>
+ /// Backing store for the <see cref="OriginatingRequest"/> properties.
+ /// </summary>
+ private IDirectedProtocolMessage originatingRequest;
+
+ /// <summary>
+ /// Backing store for the <see cref="Incoming"/> properties.
+ /// </summary>
+ private bool incoming;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DirectResponseBase"/> class.
+ /// </summary>
+ /// <param name="originatingRequest">The originating request.</param>
+ protected DirectResponseBase(IDirectedProtocolMessage originatingRequest) {
+ ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
+
+ this.originatingRequest = originatingRequest;
+ this.Version = originatingRequest.Version;
+ }
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ /// <value>Version 2.0</value>
+ public Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public MessageProtections RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ /// <value><see cref="MessageTransport.Direct"/></value>
+ public MessageTransport Transport {
+ get { return MessageTransport.Direct; }
+ }
+
+ /// <summary>
+ /// Gets the extra, non-OAuth parameters included in the message.
+ /// </summary>
+ /// <value>An empty dictionary.</value>
+ public IDictionary<string, string> ExtraData {
+ get { return EmptyDictionary<string, string>.Instance; }
+ }
+
+ #endregion
+
+ #region IDirectResponseProtocolMessage Members
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ protected internal bool Incoming {
+ get { return this.incoming; }
+ }
+
+ /// <summary>
+ /// Gets the protocol used by this message.
+ /// </summary>
+ protected Protocol Protocol {
+ get { return Protocol.Lookup(this.Version); }
+ }
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ protected IDirectedProtocolMessage OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #region IProtocolMessage methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public virtual void EnsureValidMessage() {
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Sets a flag indicating that this message is received (as opposed to sent).
+ /// </summary>
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
index 2644f44..3c5bd6a 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
@@ -1,77 +1,77 @@
-//-----------------------------------------------------------------------
-// <copyright file="IndirectResponseBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A common base class from which indirect response messages should derive.
- /// </summary>
- internal class IndirectResponseBase : RequestBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class.
- /// </summary>
- /// <param name="request">The request that caused this response message to be constructed.</param>
- /// <param name="mode">The value of the openid.mode parameter.</param>
- protected IndirectResponseBase(SignedResponseRequest request, string mode)
- : base(GetVersion(request), GetReturnTo(request), mode, MessageTransport.Indirect) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
-
- this.OriginatingRequest = request;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class
- /// for unsolicited assertion scenarios.
- /// </summary>
- /// <param name="version">The OpenID version supported at the Relying Party.</param>
- /// <param name="relyingPartyReturnTo">
- /// The URI at which the Relying Party receives OpenID indirect messages.
- /// </param>
- /// <param name="mode">The value to use for the openid.mode parameter.</param>
- protected IndirectResponseBase(Version version, Uri relyingPartyReturnTo, string mode)
- : base(version, relyingPartyReturnTo, mode, MessageTransport.Indirect) {
- }
-
- /// <summary>
- /// Gets the originating request message, if applicable.
- /// </summary>
- protected SignedResponseRequest OriginatingRequest { get; private set; }
-
- /// <summary>
- /// Gets the <see cref="IMessage.Version"/> property of a message.
- /// </summary>
- /// <param name="message">The message to fetch the protocol version from.</param>
- /// <returns>The value of the <see cref="IMessage.Version"/> property.</returns>
- /// <remarks>
- /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
- /// instead of a <see cref="NullReferenceException"/>.
- /// </remarks>
- protected static Version GetVersion(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- return message.Version;
- }
-
- /// <summary>
- /// Gets the <see cref="SignedResponseRequest.ReturnTo"/> property of a message.
- /// </summary>
- /// <param name="message">The message to fetch the ReturnTo from.</param>
- /// <returns>The value of the <see cref="SignedResponseRequest.ReturnTo"/> property.</returns>
- /// <remarks>
- /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
- /// instead of a <see cref="NullReferenceException"/>.
- /// </remarks>
- private static Uri GetReturnTo(SignedResponseRequest message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyProtocol(message.ReturnTo != null, OpenIdStrings.ReturnToRequiredForResponse);
- return message.ReturnTo;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IndirectResponseBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A common base class from which indirect response messages should derive.
+ /// </summary>
+ internal class IndirectResponseBase : RequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class.
+ /// </summary>
+ /// <param name="request">The request that caused this response message to be constructed.</param>
+ /// <param name="mode">The value of the openid.mode parameter.</param>
+ protected IndirectResponseBase(SignedResponseRequest request, string mode)
+ : base(GetVersion(request), GetReturnTo(request), mode, MessageTransport.Indirect) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ this.OriginatingRequest = request;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class
+ /// for unsolicited assertion scenarios.
+ /// </summary>
+ /// <param name="version">The OpenID version supported at the Relying Party.</param>
+ /// <param name="relyingPartyReturnTo">
+ /// The URI at which the Relying Party receives OpenID indirect messages.
+ /// </param>
+ /// <param name="mode">The value to use for the openid.mode parameter.</param>
+ protected IndirectResponseBase(Version version, Uri relyingPartyReturnTo, string mode)
+ : base(version, relyingPartyReturnTo, mode, MessageTransport.Indirect) {
+ }
+
+ /// <summary>
+ /// Gets the originating request message, if applicable.
+ /// </summary>
+ protected SignedResponseRequest OriginatingRequest { get; private set; }
+
+ /// <summary>
+ /// Gets the <see cref="IMessage.Version"/> property of a message.
+ /// </summary>
+ /// <param name="message">The message to fetch the protocol version from.</param>
+ /// <returns>The value of the <see cref="IMessage.Version"/> property.</returns>
+ /// <remarks>
+ /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
+ /// instead of a <see cref="NullReferenceException"/>.
+ /// </remarks>
+ protected static Version GetVersion(IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ return message.Version;
+ }
+
+ /// <summary>
+ /// Gets the <see cref="SignedResponseRequest.ReturnTo"/> property of a message.
+ /// </summary>
+ /// <param name="message">The message to fetch the ReturnTo from.</param>
+ /// <returns>The value of the <see cref="SignedResponseRequest.ReturnTo"/> property.</returns>
+ /// <remarks>
+ /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
+ /// instead of a <see cref="NullReferenceException"/>.
+ /// </remarks>
+ private static Uri GetReturnTo(SignedResponseRequest message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ ErrorUtilities.VerifyProtocol(message.ReturnTo != null, OpenIdStrings.ReturnToRequiredForResponse);
+ return message.ReturnTo;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/PositiveAssertionResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/PositiveAssertionResponse.cs
index ee46a18..ed0516c 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/PositiveAssertionResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/PositiveAssertionResponse.cs
@@ -1,74 +1,74 @@
-//-----------------------------------------------------------------------
-// <copyright file="PositiveAssertionResponse.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.Linq;
- using System.Net.Security;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId.ChannelElements;
-
- /// <summary>
- /// An identity assertion from a Provider to a Relying Party, stating that the
- /// user operating the user agent is in fact some specific user known to the Provider.
- /// </summary>
- [DebuggerDisplay("OpenID {Version} {Mode} {LocalIdentifier}")]
- internal class PositiveAssertionResponse : IndirectSignedResponse {
- /// <summary>
- /// Initializes a new instance of the <see cref="PositiveAssertionResponse"/> class.
- /// </summary>
- /// <param name="request">
- /// The authentication request that caused this assertion to be generated.
- /// </param>
- internal PositiveAssertionResponse(CheckIdRequest request)
- : base(request) {
- this.ClaimedIdentifier = request.ClaimedIdentifier;
- this.LocalIdentifier = request.LocalIdentifier;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PositiveAssertionResponse"/> class
- /// for unsolicited assertions.
- /// </summary>
- /// <param name="version">The OpenID version to use.</param>
- /// <param name="relyingPartyReturnTo">The return_to URL of the Relying Party.
- /// This value will commonly be from <see cref="SignedResponseRequest.ReturnTo"/>,
- /// but for unsolicited assertions may come from the Provider performing RP discovery
- /// to find the appropriate return_to URL to use.</param>
- internal PositiveAssertionResponse(Version version, Uri relyingPartyReturnTo)
- : base(version, relyingPartyReturnTo) {
- }
-
- /// <summary>
- /// Gets or sets the Claimed Identifier.
- /// </summary>
- /// <remarks>
- /// <para>"openid.claimed_id" and "openid.identity" SHALL be either both present or both absent.
- /// If neither value is present, the assertion is not about an identifier,
- /// and will contain other information in its payload, using extensions (Extensions). </para>
- /// </remarks>
- [MessagePart("openid.claimed_id", IsRequired = true, AllowEmpty = false, RequiredProtection = ProtectionLevel.Sign, MinVersion = "2.0")]
- internal Identifier ClaimedIdentifier { get; set; }
-
- /// <summary>
- /// Gets or sets the OP Local Identifier.
- /// </summary>
- /// <value>The OP-Local Identifier. </value>
- /// <remarks>
- /// <para>OpenID Providers MAY assist the end user in selecting the Claimed
- /// and OP-Local Identifiers about which the assertion is made.
- /// The openid.identity field MAY be omitted if an extension is in use that
- /// makes the response meaningful without it (see openid.claimed_id above). </para>
- /// </remarks>
- [MessagePart("openid.identity", IsRequired = true, AllowEmpty = false, RequiredProtection = ProtectionLevel.Sign)]
- internal Identifier LocalIdentifier { get; set; }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="PositiveAssertionResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Globalization;
+ using System.Linq;
+ using System.Net.Security;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ /// <summary>
+ /// An identity assertion from a Provider to a Relying Party, stating that the
+ /// user operating the user agent is in fact some specific user known to the Provider.
+ /// </summary>
+ [DebuggerDisplay("OpenID {Version} {Mode} {LocalIdentifier}")]
+ internal class PositiveAssertionResponse : IndirectSignedResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PositiveAssertionResponse"/> class.
+ /// </summary>
+ /// <param name="request">
+ /// The authentication request that caused this assertion to be generated.
+ /// </param>
+ internal PositiveAssertionResponse(CheckIdRequest request)
+ : base(request) {
+ this.ClaimedIdentifier = request.ClaimedIdentifier;
+ this.LocalIdentifier = request.LocalIdentifier;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PositiveAssertionResponse"/> class
+ /// for unsolicited assertions.
+ /// </summary>
+ /// <param name="version">The OpenID version to use.</param>
+ /// <param name="relyingPartyReturnTo">The return_to URL of the Relying Party.
+ /// This value will commonly be from <see cref="SignedResponseRequest.ReturnTo"/>,
+ /// but for unsolicited assertions may come from the Provider performing RP discovery
+ /// to find the appropriate return_to URL to use.</param>
+ internal PositiveAssertionResponse(Version version, Uri relyingPartyReturnTo)
+ : base(version, relyingPartyReturnTo) {
+ }
+
+ /// <summary>
+ /// Gets or sets the Claimed Identifier.
+ /// </summary>
+ /// <remarks>
+ /// <para>"openid.claimed_id" and "openid.identity" SHALL be either both present or both absent.
+ /// If neither value is present, the assertion is not about an identifier,
+ /// and will contain other information in its payload, using extensions (Extensions). </para>
+ /// </remarks>
+ [MessagePart("openid.claimed_id", IsRequired = true, AllowEmpty = false, RequiredProtection = ProtectionLevel.Sign, MinVersion = "2.0")]
+ internal Identifier ClaimedIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the OP Local Identifier.
+ /// </summary>
+ /// <value>The OP-Local Identifier. </value>
+ /// <remarks>
+ /// <para>OpenID Providers MAY assist the end user in selecting the Claimed
+ /// and OP-Local Identifiers about which the assertion is made.
+ /// The openid.identity field MAY be omitted if an extension is in use that
+ /// makes the response meaningful without it (see openid.claimed_id above). </para>
+ /// </remarks>
+ [MessagePart("openid.identity", IsRequired = true, AllowEmpty = false, RequiredProtection = ProtectionLevel.Sign)]
+ internal Identifier LocalIdentifier { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
index c8735ca..3339e2e 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
@@ -1,186 +1,186 @@
-//-----------------------------------------------------------------------
-// <copyright file="RequestBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A common base class for OpenID request messages and indirect responses (since they are ultimately requests).
- /// </summary>
- [DebuggerDisplay("OpenID {Version} {Mode}")]
- internal class RequestBase : IDirectedProtocolMessage {
- /// <summary>
- /// The openid.ns parameter in the message.
- /// </summary>
- /// <value>"http://specs.openid.net/auth/2.0" </value>
- /// <remarks>
- /// This particular value MUST be present for the request to be a valid OpenID Authentication 2.0 request. Future versions of the specification may define different values in order to allow message recipients to properly interpret the request.
- /// </remarks>
- [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
- [MessagePart("openid.ns", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
-#pragma warning disable 0414 // read by reflection
- private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
-#pragma warning restore 0414
-
- /// <summary>
- /// Backing store for the <see cref="Incoming"/> property.
- /// </summary>
- private bool incoming;
-
- /// <summary>
- /// Backing store for the <see cref="ExtraData"/> property.
- /// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RequestBase"/> class.
- /// </summary>
- /// <param name="version">The OpenID version this message must comply with.</param>
- /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
- /// <param name="mode">The value for the openid.mode parameter.</param>
- /// <param name="transport">A value indicating whether the message will be transmitted directly or indirectly.</param>
- protected RequestBase(Version version, Uri providerEndpoint, string mode, MessageTransport transport) {
- if (providerEndpoint == null) {
- throw new ArgumentNullException("providerEndpoint");
- }
- if (String.IsNullOrEmpty(mode)) {
- throw new ArgumentNullException("mode");
- }
-
- this.Recipient = providerEndpoint;
- this.Mode = mode;
- this.Transport = transport;
- this.Version = version;
- }
-
- /// <summary>
- /// Gets the value of the openid.mode parameter.
- /// </summary>
- [MessagePart("openid.mode", IsRequired = true, AllowEmpty = false)]
- public string Mode { get; private set; }
-
- #region IDirectedProtocolMessage Members
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- /// <value>
- /// For direct messages this is the OpenID mandated POST.
- /// For indirect messages both GET and POST are allowed.
- /// </value>
- HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
- get {
- HttpDeliveryMethods methods = HttpDeliveryMethods.PostRequest;
- if (this.Transport == MessageTransport.Indirect) {
- methods |= HttpDeliveryMethods.GetRequest;
- }
- return methods;
- }
- }
-
- /// <summary>
- /// Gets the recipient of the message.
- /// </summary>
- /// <value>The OP endpoint, or the RP return_to.</value>
- public Uri Recipient {
- get;
- private set;
- }
-
- #endregion
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- /// <value>Version 2.0</value>
- public Version Version { get; private set; }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- /// <value><see cref="MessageProtections.None"/></value>
- public virtual MessageProtections RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- /// <value><see cref="MessageTransport.Direct"/></value>
- public MessageTransport Transport { get; private set; }
-
- /// <summary>
- /// Gets the extra parameters included in the message.
- /// </summary>
- /// <value>An empty dictionary.</value>
- public IDictionary<string, string> ExtraData {
- get { return this.extraData; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- protected internal bool Incoming {
- get { return this.incoming; }
- }
-
- /// <summary>
- /// Gets the protocol used by this message.
- /// </summary>
- protected Protocol Protocol {
- get { return Protocol.Lookup(this.Version); }
- }
-
- #region IProtocolMessage Methods
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public virtual void EnsureValidMessage() {
- }
-
- #endregion
-
- /// <summary>
- /// Sets a flag indicating that this message is received (as opposed to sent).
- /// </summary>
- internal void SetAsIncoming() {
- this.incoming = true;
- }
-
- /// <summary>
- /// Gets some string from a given version of the OpenID protocol.
- /// </summary>
- /// <param name="protocolVersion">The protocol version to use for lookup.</param>
- /// <param name="mode">A function that can retrieve the desired protocol constant.</param>
- /// <returns>The value of the constant.</returns>
- /// <remarks>
- /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
- /// instead of a <see cref="NullReferenceException"/>.
- /// </remarks>
- protected static string GetProtocolConstant(Version protocolVersion, Func<Protocol, string> mode) {
- ErrorUtilities.VerifyArgumentNotNull(protocolVersion, "protocolVersion");
- return mode(Protocol.Lookup(protocolVersion));
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="RequestBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A common base class for OpenID request messages and indirect responses (since they are ultimately requests).
+ /// </summary>
+ [DebuggerDisplay("OpenID {Version} {Mode}")]
+ internal class RequestBase : IDirectedProtocolMessage {
+ /// <summary>
+ /// The openid.ns parameter in the message.
+ /// </summary>
+ /// <value>"http://specs.openid.net/auth/2.0" </value>
+ /// <remarks>
+ /// This particular value MUST be present for the request to be a valid OpenID Authentication 2.0 request. Future versions of the specification may define different values in order to allow message recipients to properly interpret the request.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
+ [MessagePart("openid.ns", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
+#pragma warning disable 0414 // read by reflection
+ private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
+#pragma warning restore 0414
+
+ /// <summary>
+ /// Backing store for the <see cref="Incoming"/> property.
+ /// </summary>
+ private bool incoming;
+
+ /// <summary>
+ /// Backing store for the <see cref="ExtraData"/> property.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RequestBase"/> class.
+ /// </summary>
+ /// <param name="version">The OpenID version this message must comply with.</param>
+ /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
+ /// <param name="mode">The value for the openid.mode parameter.</param>
+ /// <param name="transport">A value indicating whether the message will be transmitted directly or indirectly.</param>
+ protected RequestBase(Version version, Uri providerEndpoint, string mode, MessageTransport transport) {
+ if (providerEndpoint == null) {
+ throw new ArgumentNullException("providerEndpoint");
+ }
+ if (String.IsNullOrEmpty(mode)) {
+ throw new ArgumentNullException("mode");
+ }
+
+ this.Recipient = providerEndpoint;
+ this.Mode = mode;
+ this.Transport = transport;
+ this.Version = version;
+ }
+
+ /// <summary>
+ /// Gets the value of the openid.mode parameter.
+ /// </summary>
+ [MessagePart("openid.mode", IsRequired = true, AllowEmpty = false)]
+ public string Mode { get; private set; }
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ /// <value>
+ /// For direct messages this is the OpenID mandated POST.
+ /// For indirect messages both GET and POST are allowed.
+ /// </value>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get {
+ HttpDeliveryMethods methods = HttpDeliveryMethods.PostRequest;
+ if (this.Transport == MessageTransport.Indirect) {
+ methods |= HttpDeliveryMethods.GetRequest;
+ }
+ return methods;
+ }
+ }
+
+ /// <summary>
+ /// Gets the recipient of the message.
+ /// </summary>
+ /// <value>The OP endpoint, or the RP return_to.</value>
+ public Uri Recipient {
+ get;
+ private set;
+ }
+
+ #endregion
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ /// <value>Version 2.0</value>
+ public Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public virtual MessageProtections RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ /// <value><see cref="MessageTransport.Direct"/></value>
+ public MessageTransport Transport { get; private set; }
+
+ /// <summary>
+ /// Gets the extra parameters included in the message.
+ /// </summary>
+ /// <value>An empty dictionary.</value>
+ public IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ protected internal bool Incoming {
+ get { return this.incoming; }
+ }
+
+ /// <summary>
+ /// Gets the protocol used by this message.
+ /// </summary>
+ protected Protocol Protocol {
+ get { return Protocol.Lookup(this.Version); }
+ }
+
+ #region IProtocolMessage Methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public virtual void EnsureValidMessage() {
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Sets a flag indicating that this message is received (as opposed to sent).
+ /// </summary>
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+
+ /// <summary>
+ /// Gets some string from a given version of the OpenID protocol.
+ /// </summary>
+ /// <param name="protocolVersion">The protocol version to use for lookup.</param>
+ /// <param name="mode">A function that can retrieve the desired protocol constant.</param>
+ /// <returns>The value of the constant.</returns>
+ /// <remarks>
+ /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
+ /// instead of a <see cref="NullReferenceException"/>.
+ /// </remarks>
+ protected static string GetProtocolConstant(Version protocolVersion, Func<Protocol, string> mode) {
+ ErrorUtilities.VerifyArgumentNotNull(protocolVersion, "protocolVersion");
+ return mode(Protocol.Lookup(protocolVersion));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
index 212c325..cf3d50d 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
@@ -1,92 +1,92 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdUtilities.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.ChannelElements;
-
- /// <summary>
- /// A set of utilities especially useful to OpenID.
- /// </summary>
- internal static class OpenIdUtilities {
- /// <summary>
- /// Gets the OpenID protocol instance for the version in a message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>The OpenID protocol instance.</returns>
- internal static Protocol GetProtocol(this IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- return Protocol.Lookup(message.Version);
- }
-
- /// <summary>
- /// Changes the position of some element in a list.
- /// </summary>
- /// <typeparam name="T">The type of elements stored in the list.</typeparam>
- /// <param name="list">The list to be modified.</param>
- /// <param name="position">The new position for the given element.</param>
- /// <param name="value">The element to move within the list.</param>
- /// <exception cref="InternalErrorException">Thrown if the element does not already exist in the list.</exception>
- internal static void MoveTo<T>(this IList<T> list, int position, T value) {
- ErrorUtilities.VerifyInternal(list.Remove(value), "Unable to find element in list.");
- list.Insert(position, value);
- }
-
- /// <summary>
- /// Initializes the private secret if it has not yet been set.
- /// </summary>
- /// <param name="secretStore">The secret store.</param>
- internal static void InitializeSecretIfUnset(this IPrivateSecretStore secretStore) {
- ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
-
- if (secretStore.PrivateSecret == null) {
- secretStore.PrivateSecret = MessagingUtilities.GetCryptoRandomData(ReturnToSignatureBindingElement.OptimalPrivateSecretLength);
-
- // Log that we created a new private secret.
- // If this happens frequently, it's a sign that the store for this secret is not
- // properly saving the value, and the result will be slower performance for
- // Relying Parties (at best) and failed authentications (at worst).
- Logger.Info("Generated and saved private secret. This should generally happen only at web application initialization time.");
- }
- }
-
- /// <summary>
- /// Corrects any URI decoding the Provider may have inappropriately done
- /// to our return_to URL, resulting in an otherwise corrupted base64 encoded value.
- /// </summary>
- /// <param name="value">The base64 encoded value.</param>
- /// <returns>
- /// The value; corrected if corruption had occurred.
- /// </returns>
- /// <remarks>
- /// AOL may have incorrectly URI-decoded the token for us in the return_to,
- /// resulting in a token URI-decoded twice by the time we see it, and no
- /// longer being a valid base64 string.
- /// It turns out that the only symbols from base64 that is also encoded
- /// in URI encoding rules are the + and / characters.
- /// AOL decodes the %2b sequence to the + character
- /// and the %2f sequence to the / character (it shouldn't decode at all).
- /// When we do our own URI decoding, the + character becomes a space (corrupting base64)
- /// but the / character remains a /, so no further corruption happens to this character.
- /// So to correct this we just need to change any spaces we find in the token
- /// back to + characters.
- /// </remarks>
- internal static string FixDoublyUriDecodedBase64String(string value) {
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
-
- if (value.Contains(" ")) {
- Logger.Error("Deserializing a corrupted token. The OpenID Provider may have inappropriately decoded the return_to URL before sending it back to us.");
- value = value.Replace(' ', '+'); // Undo any extra decoding the Provider did
- }
-
- return value;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdUtilities.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ /// <summary>
+ /// A set of utilities especially useful to OpenID.
+ /// </summary>
+ internal static class OpenIdUtilities {
+ /// <summary>
+ /// Gets the OpenID protocol instance for the version in a message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>The OpenID protocol instance.</returns>
+ internal static Protocol GetProtocol(this IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ return Protocol.Lookup(message.Version);
+ }
+
+ /// <summary>
+ /// Changes the position of some element in a list.
+ /// </summary>
+ /// <typeparam name="T">The type of elements stored in the list.</typeparam>
+ /// <param name="list">The list to be modified.</param>
+ /// <param name="position">The new position for the given element.</param>
+ /// <param name="value">The element to move within the list.</param>
+ /// <exception cref="InternalErrorException">Thrown if the element does not already exist in the list.</exception>
+ internal static void MoveTo<T>(this IList<T> list, int position, T value) {
+ ErrorUtilities.VerifyInternal(list.Remove(value), "Unable to find element in list.");
+ list.Insert(position, value);
+ }
+
+ /// <summary>
+ /// Initializes the private secret if it has not yet been set.
+ /// </summary>
+ /// <param name="secretStore">The secret store.</param>
+ internal static void InitializeSecretIfUnset(this IPrivateSecretStore secretStore) {
+ ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
+
+ if (secretStore.PrivateSecret == null) {
+ secretStore.PrivateSecret = MessagingUtilities.GetCryptoRandomData(ReturnToSignatureBindingElement.OptimalPrivateSecretLength);
+
+ // Log that we created a new private secret.
+ // If this happens frequently, it's a sign that the store for this secret is not
+ // properly saving the value, and the result will be slower performance for
+ // Relying Parties (at best) and failed authentications (at worst).
+ Logger.Info("Generated and saved private secret. This should generally happen only at web application initialization time.");
+ }
+ }
+
+ /// <summary>
+ /// Corrects any URI decoding the Provider may have inappropriately done
+ /// to our return_to URL, resulting in an otherwise corrupted base64 encoded value.
+ /// </summary>
+ /// <param name="value">The base64 encoded value.</param>
+ /// <returns>
+ /// The value; corrected if corruption had occurred.
+ /// </returns>
+ /// <remarks>
+ /// AOL may have incorrectly URI-decoded the token for us in the return_to,
+ /// resulting in a token URI-decoded twice by the time we see it, and no
+ /// longer being a valid base64 string.
+ /// It turns out that the only symbols from base64 that is also encoded
+ /// in URI encoding rules are the + and / characters.
+ /// AOL decodes the %2b sequence to the + character
+ /// and the %2f sequence to the / character (it shouldn't decode at all).
+ /// When we do our own URI decoding, the + character becomes a space (corrupting base64)
+ /// but the / character remains a /, so no further corruption happens to this character.
+ /// So to correct this we just need to change any spaces we find in the token
+ /// back to + characters.
+ /// </remarks>
+ internal static string FixDoublyUriDecodedBase64String(string value) {
+ ErrorUtilities.VerifyArgumentNotNull(value, "value");
+
+ if (value.Contains(" ")) {
+ Logger.Error("Deserializing a corrupted token. The OpenID Provider may have inappropriately decoded the return_to URL before sending it back to us.");
+ value = value.Replace(' ', '+'); // Undo any extra decoding the Provider did
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs b/src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs
index 2d7d6dd..aa24130 100644
--- a/src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs
+++ b/src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs
@@ -1,30 +1,30 @@
-//-----------------------------------------------------------------------
-// <copyright file="PrivateSecretMemoryStore.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId {
- using DotNetOpenAuth.OpenId.ChannelElements;
-
- /// <summary>
- /// The stock in-memory store for private secrets needed by Relying Parties.
- /// </summary>
- /// <remarks>
- /// This class is only good for NON-web farm/garden environments.
- /// Multi-process web applications must implement their own store
- /// that shares state across all instances of the web application
- /// so that signatures made on one server can be verified on another.
- /// </remarks>
- internal class PrivateSecretMemoryStore : IPrivateSecretStore {
- #region IPrivateSecretStore Members
-
- /// <summary>
- /// Gets or sets a secret key that can be used for signing.
- /// </summary>
- /// <value>A 64-byte binary value, which may contain null bytes.</value>
- public byte[] PrivateSecret { get; set; }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="PrivateSecretMemoryStore.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ /// <summary>
+ /// The stock in-memory store for private secrets needed by Relying Parties.
+ /// </summary>
+ /// <remarks>
+ /// This class is only good for NON-web farm/garden environments.
+ /// Multi-process web applications must implement their own store
+ /// that shares state across all instances of the web application
+ /// so that signatures made on one server can be verified on another.
+ /// </remarks>
+ internal class PrivateSecretMemoryStore : IPrivateSecretStore {
+ #region IPrivateSecretStore Members
+
+ /// <summary>
+ /// Gets or sets a secret key that can be used for signing.
+ /// </summary>
+ /// <value>A 64-byte binary value, which may contain null bytes.</value>
+ public byte[] PrivateSecret { get; set; }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
index 15ba2e9..29d04ba 100644
--- a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
+++ b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
@@ -41,8 +41,8 @@ namespace DotNetOpenAuth.OpenId {
ErrorUtilities.VerifyArgumentNotNull(serviceTypeURIs, "serviceTypeURIs");
this.Endpoint = providerEndpoint;
- this.Capabilities = new ReadOnlyCollection<string>(serviceTypeURIs.ToList());
-
+ this.Capabilities = new ReadOnlyCollection<string>(serviceTypeURIs.ToList());
+
Protocol opIdentifierProtocol = Protocol.FindBestVersion(p => p.OPIdentifierServiceTypeURI, serviceTypeURIs);
Protocol claimedIdentifierProviderVersion = Protocol.FindBestVersion(p => p.ClaimedIdentifierServiceTypeURI, serviceTypeURIs);
if (opIdentifierProtocol != null) {
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
index 5c757e4..b3a56ef 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
@@ -78,10 +78,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <remarks>
/// Used for deserializing <see cref="ServiceEndpoint"/> from authentication responses.
/// </remarks>
- private ServiceEndpoint(Uri providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, Protocol protocol) {
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
- ErrorUtilities.VerifyArgumentNotNull(claimedIdentifier, "claimedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(providerLocalIdentifier, "providerLocalIdentifier");
+ private ServiceEndpoint(Uri providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, Protocol protocol) {
+ ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
+ ErrorUtilities.VerifyArgumentNotNull(claimedIdentifier, "claimedIdentifier");
+ ErrorUtilities.VerifyArgumentNotNull(providerLocalIdentifier, "providerLocalIdentifier");
ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
this.ClaimedIdentifier = claimedIdentifier;
diff --git a/src/DotNetOpenAuth/Util.cs b/src/DotNetOpenAuth/Util.cs
index 8b4d011..6c885bf 100644
--- a/src/DotNetOpenAuth/Util.cs
+++ b/src/DotNetOpenAuth/Util.cs
@@ -1,221 +1,221 @@
-//-----------------------------------------------------------------------
-// <copyright file="Util.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-namespace DotNetOpenAuth {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Net;
- using System.Reflection;
- using System.Text;
-
- /// <summary>
- /// A grab-bag utility class.
- /// </summary>
- internal static class Util {
- /// <summary>
- /// A set of escaping mappings that help secure a string from javscript execution.
- /// </summary>
- /// <remarks>
- /// The characters to escape here are inspired by
- /// http://code.google.com/p/doctype/wiki/ArticleXSSInJavaScript
- /// </remarks>
- private static readonly Dictionary<string, string> javascriptStaticStringEscaping = new Dictionary<string, string> {
- { "\\", @"\\" }, // this WAS just above the & substitution but we moved it here to prevent double-escaping
- { "\t", @"\t" },
- { "\n", @"\n" },
- { "\r", @"\r" },
- { "\u0085", @"\u0085" },
- { "\u2028", @"\u2028" },
- { "\u2029", @"\u2029" },
- { "'", @"\x27" },
- { "\"", @"\x22" },
- { "&", @"\x26" },
- { "<", @"\x3c" },
- { ">", @"\x3e" },
- { "=", @"\x3d" },
- };
-
- /// <summary>
- /// Gets a human-readable description of the library name and version, including
- /// whether the build is an official or private one.
- /// </summary>
- public static string LibraryVersion {
- get {
- string assemblyFullName = Assembly.GetExecutingAssembly().FullName;
- bool official = assemblyFullName.Contains("PublicKeyToken=2780ccd10d57b246");
-
- // We use InvariantCulture since this is used for logging.
- return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
- }
- }
-
- /// <summary>
- /// Tests for equality between two objects. Safely handles the case where one or both are null.
- /// </summary>
- /// <typeparam name="T">The type of objects been checked for equality.</typeparam>
- /// <param name="first">The first object.</param>
- /// <param name="second">The second object.</param>
- /// <returns><c>true</c> if the two objects are equal; <c>false</c> otherwise.</returns>
- internal static bool EqualsNullSafe<T>(this T first, T second) where T : class {
- // If one is null and the other is not...
- if (object.ReferenceEquals(first, null) ^ object.ReferenceEquals(second, null)) {
- return false;
- }
-
- // If both are null... (we only check one because we already know both are either null or non-null)
- if (object.ReferenceEquals(first, null)) {
- return true;
- }
-
- // Neither are null. Delegate to the Equals method.
- return first.Equals(second);
- }
-
- /// <summary>
- /// Prepares what SHOULD be simply a string value for safe injection into Javascript
- /// by using appropriate character escaping.
- /// </summary>
- /// <param name="value">The untrusted string value to be escaped to protected against XSS attacks.</param>
- /// <returns>The escaped string.</returns>
- internal static string GetSafeJavascriptValue(string value) {
- if (value == null) {
- return "null";
- }
-
- // We use a StringBuilder because we have potentially many replacements to do,
- // and we don't want to create a new string for every intermediate replacement step.
- StringBuilder builder = new StringBuilder(value);
- foreach (var pair in javascriptStaticStringEscaping) {
- builder.Replace(pair.Key, pair.Value);
- }
- builder.Insert(0, '\'');
- builder.Append('\'');
- return builder.ToString();
- }
-
- /// <summary>
- /// Prepares a dictionary for printing as a string.
- /// </summary>
- /// <typeparam name="K">The type of the key.</typeparam>
- /// <typeparam name="V">The type of the value.</typeparam>
- /// <param name="pairs">The dictionary or sequence of name-value pairs.</param>
- /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
- /// <remarks>
- /// The work isn't done until (and if) the
- /// <see cref="Object.ToString"/> method is actually called, which makes it great
- /// for logging complex objects without being in a conditional block.
- /// </remarks>
- internal static object ToStringDeferred<K, V>(this IEnumerable<KeyValuePair<K, V>> pairs) {
- return new DelayedToString<IEnumerable<KeyValuePair<K, V>>>(
- pairs,
- p => {
- var dictionary = pairs as IDictionary<K, V>;
- StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
- foreach (var pair in pairs) {
- sb.AppendFormat("\t{0}: {1}{2}", pair.Key, pair.Value, Environment.NewLine);
- }
- return sb.ToString();
- });
- }
-
- /// <summary>
- /// Offers deferred ToString processing for a list of elements, that are assumed
- /// to generate just a single-line string.
- /// </summary>
- /// <typeparam name="T">The type of elements contained in the list.</typeparam>
- /// <param name="list">The list of elements.</param>
- /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
- internal static object ToStringDeferred<T>(this IEnumerable<T> list) {
- return ToStringDeferred<T>(list, false);
- }
-
- /// <summary>
- /// Offers deferred ToString processing for a list of elements.
- /// </summary>
- /// <typeparam name="T">The type of elements contained in the list.</typeparam>
- /// <param name="list">The list of elements.</param>
- /// <param name="multiLineElements">if set to <c>true</c>, special formatting will be applied to the output to make it clear where one element ends and the next begins.</param>
- /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
- internal static object ToStringDeferred<T>(this IEnumerable<T> list, bool multiLineElements) {
- return new DelayedToString<IEnumerable<T>>(
- list,
- l => {
- StringBuilder sb = new StringBuilder();
- if (multiLineElements) {
- sb.AppendLine("[{");
- foreach (T obj in l) {
- // Prepare the string repersentation of the object
- string objString = obj != null ? obj.ToString() : "<NULL>";
-
- // Indent every line printed
- objString = objString.Replace(Environment.NewLine, Environment.NewLine + "\t");
- sb.Append("\t");
- sb.Append(objString);
-
- if (!objString.EndsWith(Environment.NewLine)) {
- sb.AppendLine();
- }
- sb.AppendLine("}, {");
- }
- if (sb.Length > 2 + Environment.NewLine.Length) { // if anything was in the enumeration
- sb.Length -= 2 + Environment.NewLine.Length; // trim off the last ", {\r\n"
- } else {
- sb.Length -= 1 + Environment.NewLine.Length; // trim off the opening {
- }
- sb.Append("]");
- return sb.ToString();
- } else {
- sb.Append("{");
- foreach (T obj in l) {
- sb.Append(obj != null ? obj.ToString() : "<NULL>");
- sb.AppendLine(",");
- }
- if (sb.Length > 1) {
- sb.Length -= 1;
- }
- sb.Append("}");
- return sb.ToString();
- }
- });
- }
-
- /// <summary>
- /// Manages an individual deferred ToString call.
- /// </summary>
- /// <typeparam name="T">The type of object to be serialized as a string.</typeparam>
- private class DelayedToString<T> {
- /// <summary>
- /// The object that will be serialized if called upon.
- /// </summary>
- private T obj;
-
- /// <summary>
- /// The method used to serialize <see cref="obj"/> to string form.
- /// </summary>
- private Func<T, string> toString;
-
- /// <summary>
- /// Initializes a new instance of the DelayedToString class.
- /// </summary>
- /// <param name="obj">The object that may be serialized to string form.</param>
- /// <param name="toString">The method that will serialize the object if called upon.</param>
- public DelayedToString(T obj, Func<T, string> toString) {
- this.obj = obj;
- this.toString = toString;
- }
-
- /// <summary>
- /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
- /// </returns>
- public override string ToString() {
- return this.toString(this.obj);
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="Util.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace DotNetOpenAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Net;
+ using System.Reflection;
+ using System.Text;
+
+ /// <summary>
+ /// A grab-bag utility class.
+ /// </summary>
+ internal static class Util {
+ /// <summary>
+ /// A set of escaping mappings that help secure a string from javscript execution.
+ /// </summary>
+ /// <remarks>
+ /// The characters to escape here are inspired by
+ /// http://code.google.com/p/doctype/wiki/ArticleXSSInJavaScript
+ /// </remarks>
+ private static readonly Dictionary<string, string> javascriptStaticStringEscaping = new Dictionary<string, string> {
+ { "\\", @"\\" }, // this WAS just above the & substitution but we moved it here to prevent double-escaping
+ { "\t", @"\t" },
+ { "\n", @"\n" },
+ { "\r", @"\r" },
+ { "\u0085", @"\u0085" },
+ { "\u2028", @"\u2028" },
+ { "\u2029", @"\u2029" },
+ { "'", @"\x27" },
+ { "\"", @"\x22" },
+ { "&", @"\x26" },
+ { "<", @"\x3c" },
+ { ">", @"\x3e" },
+ { "=", @"\x3d" },
+ };
+
+ /// <summary>
+ /// Gets a human-readable description of the library name and version, including
+ /// whether the build is an official or private one.
+ /// </summary>
+ public static string LibraryVersion {
+ get {
+ string assemblyFullName = Assembly.GetExecutingAssembly().FullName;
+ bool official = assemblyFullName.Contains("PublicKeyToken=2780ccd10d57b246");
+
+ // We use InvariantCulture since this is used for logging.
+ return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
+ }
+ }
+
+ /// <summary>
+ /// Tests for equality between two objects. Safely handles the case where one or both are null.
+ /// </summary>
+ /// <typeparam name="T">The type of objects been checked for equality.</typeparam>
+ /// <param name="first">The first object.</param>
+ /// <param name="second">The second object.</param>
+ /// <returns><c>true</c> if the two objects are equal; <c>false</c> otherwise.</returns>
+ internal static bool EqualsNullSafe<T>(this T first, T second) where T : class {
+ // If one is null and the other is not...
+ if (object.ReferenceEquals(first, null) ^ object.ReferenceEquals(second, null)) {
+ return false;
+ }
+
+ // If both are null... (we only check one because we already know both are either null or non-null)
+ if (object.ReferenceEquals(first, null)) {
+ return true;
+ }
+
+ // Neither are null. Delegate to the Equals method.
+ return first.Equals(second);
+ }
+
+ /// <summary>
+ /// Prepares what SHOULD be simply a string value for safe injection into Javascript
+ /// by using appropriate character escaping.
+ /// </summary>
+ /// <param name="value">The untrusted string value to be escaped to protected against XSS attacks.</param>
+ /// <returns>The escaped string.</returns>
+ internal static string GetSafeJavascriptValue(string value) {
+ if (value == null) {
+ return "null";
+ }
+
+ // We use a StringBuilder because we have potentially many replacements to do,
+ // and we don't want to create a new string for every intermediate replacement step.
+ StringBuilder builder = new StringBuilder(value);
+ foreach (var pair in javascriptStaticStringEscaping) {
+ builder.Replace(pair.Key, pair.Value);
+ }
+ builder.Insert(0, '\'');
+ builder.Append('\'');
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Prepares a dictionary for printing as a string.
+ /// </summary>
+ /// <typeparam name="K">The type of the key.</typeparam>
+ /// <typeparam name="V">The type of the value.</typeparam>
+ /// <param name="pairs">The dictionary or sequence of name-value pairs.</param>
+ /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
+ /// <remarks>
+ /// The work isn't done until (and if) the
+ /// <see cref="Object.ToString"/> method is actually called, which makes it great
+ /// for logging complex objects without being in a conditional block.
+ /// </remarks>
+ internal static object ToStringDeferred<K, V>(this IEnumerable<KeyValuePair<K, V>> pairs) {
+ return new DelayedToString<IEnumerable<KeyValuePair<K, V>>>(
+ pairs,
+ p => {
+ var dictionary = pairs as IDictionary<K, V>;
+ StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
+ foreach (var pair in pairs) {
+ sb.AppendFormat("\t{0}: {1}{2}", pair.Key, pair.Value, Environment.NewLine);
+ }
+ return sb.ToString();
+ });
+ }
+
+ /// <summary>
+ /// Offers deferred ToString processing for a list of elements, that are assumed
+ /// to generate just a single-line string.
+ /// </summary>
+ /// <typeparam name="T">The type of elements contained in the list.</typeparam>
+ /// <param name="list">The list of elements.</param>
+ /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
+ internal static object ToStringDeferred<T>(this IEnumerable<T> list) {
+ return ToStringDeferred<T>(list, false);
+ }
+
+ /// <summary>
+ /// Offers deferred ToString processing for a list of elements.
+ /// </summary>
+ /// <typeparam name="T">The type of elements contained in the list.</typeparam>
+ /// <param name="list">The list of elements.</param>
+ /// <param name="multiLineElements">if set to <c>true</c>, special formatting will be applied to the output to make it clear where one element ends and the next begins.</param>
+ /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
+ internal static object ToStringDeferred<T>(this IEnumerable<T> list, bool multiLineElements) {
+ return new DelayedToString<IEnumerable<T>>(
+ list,
+ l => {
+ StringBuilder sb = new StringBuilder();
+ if (multiLineElements) {
+ sb.AppendLine("[{");
+ foreach (T obj in l) {
+ // Prepare the string repersentation of the object
+ string objString = obj != null ? obj.ToString() : "<NULL>";
+
+ // Indent every line printed
+ objString = objString.Replace(Environment.NewLine, Environment.NewLine + "\t");
+ sb.Append("\t");
+ sb.Append(objString);
+
+ if (!objString.EndsWith(Environment.NewLine)) {
+ sb.AppendLine();
+ }
+ sb.AppendLine("}, {");
+ }
+ if (sb.Length > 2 + Environment.NewLine.Length) { // if anything was in the enumeration
+ sb.Length -= 2 + Environment.NewLine.Length; // trim off the last ", {\r\n"
+ } else {
+ sb.Length -= 1 + Environment.NewLine.Length; // trim off the opening {
+ }
+ sb.Append("]");
+ return sb.ToString();
+ } else {
+ sb.Append("{");
+ foreach (T obj in l) {
+ sb.Append(obj != null ? obj.ToString() : "<NULL>");
+ sb.AppendLine(",");
+ }
+ if (sb.Length > 1) {
+ sb.Length -= 1;
+ }
+ sb.Append("}");
+ return sb.ToString();
+ }
+ });
+ }
+
+ /// <summary>
+ /// Manages an individual deferred ToString call.
+ /// </summary>
+ /// <typeparam name="T">The type of object to be serialized as a string.</typeparam>
+ private class DelayedToString<T> {
+ /// <summary>
+ /// The object that will be serialized if called upon.
+ /// </summary>
+ private T obj;
+
+ /// <summary>
+ /// The method used to serialize <see cref="obj"/> to string form.
+ /// </summary>
+ private Func<T, string> toString;
+
+ /// <summary>
+ /// Initializes a new instance of the DelayedToString class.
+ /// </summary>
+ /// <param name="obj">The object that may be serialized to string form.</param>
+ /// <param name="toString">The method that will serialize the object if called upon.</param>
+ public DelayedToString(T obj, Func<T, string> toString) {
+ this.obj = obj;
+ this.toString = toString;
+ }
+
+ /// <summary>
+ /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override string ToString() {
+ return this.toString(this.obj);
+ }
+ }
+ }
+}