diff options
Diffstat (limited to 'src')
235 files changed, 7607 insertions, 4068 deletions
diff --git a/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs b/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs index 30fa284..0b84398 100644 --- a/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs +++ b/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs @@ -6,13 +6,13 @@ namespace DotNetOpenAuth.BuildTasks { using System; + using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Build.BuildEngine; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; - using System.Collections; public class AddProjectItems : Task { /// <summary> @@ -49,7 +49,10 @@ namespace DotNetOpenAuth.BuildTasks { BuildItem newItem = project.AddNewItem(itemType, projectItem.ItemSpec, false); var customMetadata = projectItem.CloneCustomMetadata(); foreach (DictionaryEntry entry in customMetadata) { - newItem.SetMetadata((string)entry.Key, (string)entry.Value); + string value = (string)entry.Value; + if (value.Length > 0) { + newItem.SetMetadata((string)entry.Key, value); + } } } diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs index f940a72..503e168 100644 --- a/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs +++ b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs @@ -1,47 +1,59 @@ -using System; -using System.Linq; -using System.IO; -using System.Xml; +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.IO; + using System.Linq; + using System.Xml; + using Microsoft.Build.BuildEngine; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Microsoft.Build.BuildEngine; - -namespace DotNetOpenAuth.BuildTasks { /// <summary> /// Replaces ProjectReference items in a set of projects with Reference items. /// </summary> public class ChangeProjectReferenceToAssemblyReference : Task { + private const string msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; + /// <summary> /// The projects to alter. /// </summary> [Required] public ITaskItem[] Projects { get; set; } + /// <summary> - /// The project reference to remove. + /// The project references to remove. /// </summary> [Required] - public string ProjectReference { get; set; } + public ITaskItem[] ProjectReferences { get; set; } + /// <summary> - /// The assembly reference to add. + /// The assembly references to add. /// </summary> [Required] - public string Reference { get; set; } + public ITaskItem[] References { get; set; } - const string msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; public override bool Execute() { - foreach (var project in Projects) { - Log.LogMessage(MessageImportance.Normal, "Changing P2P references to assembly references in \"{0}\".", project.ItemSpec); + if (this.ProjectReferences.Length != this.References.Length) { + this.Log.LogError("ProjectReferences and References arrays do not have matching lengths."); + } + foreach (var project in Projects) { Project doc = new Project(); doc.Load(project.ItemSpec); - - var projectReference = doc.EvaluatedItems.OfType<BuildItem>().Where( - item => item.Name == "ProjectReference" && item.Include == ProjectReference).Single(); - doc.RemoveItem(projectReference); - var newReference = doc.AddNewItem("Reference", Path.GetFileNameWithoutExtension(Reference), true); - newReference.SetMetadata("HintPath", Reference); + var projectReferences = doc.EvaluatedItems.OfType<BuildItem>().Where(item => item.Name == "ProjectReference"); + var matchingReferences = from reference in projectReferences + join refToRemove in this.ProjectReferences on reference.Include equals refToRemove.ItemSpec + let addIndex = Array.IndexOf(this.ProjectReferences, refToRemove) + select new { Remove = reference, Add = this.References[addIndex] }; + foreach (var matchingReference in matchingReferences) { + this.Log.LogMessage("Removing project reference to \"{0}\" from \"{1}\".", matchingReference.Remove.Include, project.ItemSpec); + doc.RemoveItem(matchingReference.Remove); + if (matchingReference.Add.ItemSpec != "REMOVE") { + this.Log.LogMessage("Adding assembly reference to \"{0}\" to \"{1}\".", matchingReference.Add.ItemSpec, project.ItemSpec); + var newReference = doc.AddNewItem("Reference", Path.GetFileNameWithoutExtension(matchingReference.Add.ItemSpec), true); + newReference.SetMetadata("HintPath", matchingReference.Add.ItemSpec); + } + } doc.Save(project.ItemSpec); } diff --git a/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs b/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs index e17d8f2..5b097ab 100644 --- a/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs +++ b/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs @@ -56,38 +56,51 @@ namespace DotNetOpenAuth.BuildTasks { for (int i = 0; i < this.SourceFiles.Length; i++) { string sourcePath = this.SourceFiles[i].ItemSpec; string destPath = this.DestinationFiles[i].ItemSpec; - bool skipUnchangedFiles = bool.Parse(this.SourceFiles[i].GetMetadata("SkipUnchangedFiles")); + bool skipUnchangedFiles; + bool.TryParse(this.SourceFiles[i].GetMetadata("SkipUnchangedFiles"), out skipUnchangedFiles); - // We deliberably consider newer destination files to be up-to-date rather than - // requiring equality because this task modifies the destination file while copying. - if (skipUnchangedFiles && File.GetLastWriteTimeUtc(sourcePath) < File.GetLastWriteTimeUtc(destPath)) { - Log.LogMessage(MessageImportance.Low, "Skipping \"{0}\" -> \"{1}\" because the destination is up to date.", sourcePath, destPath); - continue; + if (!Directory.Exists(Path.GetDirectoryName(destPath))) { + Directory.CreateDirectory(Path.GetDirectoryName(destPath)); } - Log.LogMessage(MessageImportance.Normal, "Transforming \"{0}\" -> \"{1}\"", sourcePath, destPath); + if (string.IsNullOrEmpty(this.SourceFiles[i].GetMetadata("BeforeTokens"))) { + // this is just a standard copy without token substitution + if (skipUnchangedFiles && File.GetLastWriteTimeUtc(sourcePath) == File.GetLastWriteTimeUtc(destPath)) { + Log.LogMessage(MessageImportance.Low, "Skipping \"{0}\" -> \"{1}\" because the destination is up to date.", sourcePath, destPath); + continue; + } - string[] beforeTokens = this.SourceFiles[i].GetMetadata("BeforeTokens").Split(';'); - string[] afterTokens = this.SourceFiles[i].GetMetadata("AfterTokens").Split(';'); - if (beforeTokens.Length != afterTokens.Length) { - Log.LogError("Unequal number of before and after tokens. Before: \"{0}\". After \"{1}\".", beforeTokens, afterTokens); - return false; - } + Log.LogMessage(MessageImportance.Normal, "Copying file from \"{0}\" to \"{1}\"", sourcePath, destPath); + File.Copy(sourcePath, destPath, true); + } else { + // We deliberably consider newer destination files to be up-to-date rather than + // requiring equality because this task modifies the destination file while copying. + if (skipUnchangedFiles && File.GetLastWriteTimeUtc(sourcePath) < File.GetLastWriteTimeUtc(destPath)) { + Log.LogMessage(MessageImportance.Low, "Skipping \"{0}\" -> \"{1}\" because the destination is up to date.", sourcePath, destPath); + continue; + } + + Log.LogMessage(MessageImportance.Normal, "Transforming \"{0}\" -> \"{1}\"", sourcePath, destPath); - using (StreamReader sr = File.OpenText(sourcePath)) { - if (!Directory.Exists(Path.GetDirectoryName(destPath))) { - Directory.CreateDirectory(Path.GetDirectoryName(destPath)); + string[] beforeTokens = this.SourceFiles[i].GetMetadata("BeforeTokens").Split(';'); + string[] afterTokens = this.SourceFiles[i].GetMetadata("AfterTokens").Split(';'); + if (beforeTokens.Length != afterTokens.Length) { + Log.LogError("Unequal number of before and after tokens. Before: \"{0}\". After \"{1}\".", beforeTokens, afterTokens); + return false; } - using (StreamWriter sw = File.CreateText(destPath)) { - StringBuilder line = new StringBuilder(); - while (!sr.EndOfStream) { - line.Length = 0; - line.Append(sr.ReadLine()); - for (int j = 0; j < beforeTokens.Length; j++) { - line.Replace(beforeTokens[j], afterTokens[j]); - } - sw.WriteLine(line); + using (StreamReader sr = File.OpenText(sourcePath)) { + using (StreamWriter sw = File.CreateText(destPath)) { + StringBuilder line = new StringBuilder(); + while (!sr.EndOfStream) { + line.Length = 0; + line.Append(sr.ReadLine()); + for (int j = 0; j < beforeTokens.Length; j++) { + line.Replace(beforeTokens[j], afterTokens[j]); + } + + sw.WriteLine(line); + } } } } diff --git a/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs b/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs index 0162c16..f49c9b1 100644 --- a/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs +++ b/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs @@ -15,6 +15,7 @@ namespace DotNetOpenAuth.BuildTasks { using Microsoft.Build.Utilities; public class DiscoverProjectTemplates : Task { + [Required] public ITaskItem[] TopLevelTemplates { get; set; } [Output] diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj index 5de32e7..365bec5 100644 --- a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj +++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> @@ -12,6 +12,21 @@ <AssemblyName>DotNetOpenAuth.BuildTasks</AssemblyName> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> + <PublishUrl>publish\</PublishUrl> + <Install>true</Install> + <InstallFrom>Disk</InstallFrom> + <UpdateEnabled>false</UpdateEnabled> + <UpdateMode>Foreground</UpdateMode> + <UpdateInterval>7</UpdateInterval> + <UpdateIntervalUnits>Days</UpdateIntervalUnits> + <UpdatePeriodically>false</UpdatePeriodically> + <UpdateRequired>false</UpdateRequired> + <MapFileExtensions>true</MapFileExtensions> + <ApplicationRevision>0</ApplicationRevision> + <ApplicationVersion>1.0.0.%2a</ApplicationVersion> + <IsWebBootstrapper>false</IsWebBootstrapper> + <UseApplicationTrust>false</UseApplicationTrust> + <BootstrapperEnabled>true</BootstrapperEnabled> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -48,6 +63,7 @@ </CodeContractsBaseLineFile> <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel> <CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -56,6 +72,7 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> <Reference Include="Microsoft.Build.Engine" /> @@ -89,15 +106,20 @@ <Compile Include="CreateWebApplication.cs" /> <Compile Include="DeleteWebApplication.cs" /> <Compile Include="DiscoverProjectTemplates.cs" /> + <Compile Include="DowngradeProjects.cs" /> <Compile Include="ECMAScriptPacker.cs" /> <Compile Include="FilterItems.cs" /> <Compile Include="FixupReferenceHintPaths.cs" /> <Compile Include="FixupShippingToolSamples.cs" /> + <Compile Include="HardLinkCopy.cs" /> <Compile Include="MergeProjectWithVSTemplate.cs" /> <Compile Include="GetBuildVersion.cs" /> <Compile Include="CheckAdminRights.cs" /> <Compile Include="JsPack.cs" /> + <Compile Include="NativeMethods.cs" /> <Compile Include="ParseMaster.cs" /> + <Compile Include="PathSegment.cs" /> + <Compile Include="Publicize.cs" /> <Compile Include="Purge.cs" /> <Compile Include="ReSignDelaySignedAssemblies.cs" /> <Compile Include="SetEnvironmentVariable.cs" /> @@ -110,6 +132,7 @@ <DependentUpon>TaskStrings.resx</DependentUpon> </Compile> <Compile Include="Trim.cs" /> + <Compile Include="Utilities.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="TaskStrings.resx"> @@ -117,6 +140,23 @@ <LastGenOutput>TaskStrings.Designer.cs</LastGenOutput> </EmbeddedResource> </ItemGroup> + <ItemGroup> + <BootstrapperPackage Include="Microsoft.Net.Client.3.5"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName> + <Install>false</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1</ProductName> + <Install>true</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1"> + <Visible>False</Visible> + <ProductName>Windows Installer 3.1</ProductName> + <Install>true</Install> + </BootstrapperPackage> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln index fca41e8..f3e3982 100644 --- a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln +++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln @@ -1,16 +1,27 @@ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.BuildTasks", "DotNetOpenAuth.BuildTasks.csproj", "{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}" -EndProject +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ABBE14A3-0404-4123-9093-E598C3DD3E9B}" ProjectSection(SolutionItems) = preProject ..\..\build.proj = ..\..\build.proj + ..\..\doc\doc.proj = ..\..\doc\doc.proj + ..\..\tools\DotNetOpenAuth.automated.props = ..\..\tools\DotNetOpenAuth.automated.props + ..\..\tools\DotNetOpenAuth.automated.targets = ..\..\tools\DotNetOpenAuth.automated.targets ..\..\lib\DotNetOpenAuth.BuildTasks.targets = ..\..\lib\DotNetOpenAuth.BuildTasks.targets ..\..\tools\DotNetOpenAuth.Common.Settings.targets = ..\..\tools\DotNetOpenAuth.Common.Settings.targets + ..\..\tools\DotNetOpenAuth.props = ..\..\tools\DotNetOpenAuth.props + ..\..\tools\DotNetOpenAuth.targets = ..\..\tools\DotNetOpenAuth.targets ..\..\tools\DotNetOpenAuth.Versioning.targets = ..\..\tools\DotNetOpenAuth.Versioning.targets + ..\..\tools\drop.proj = ..\..\tools\drop.proj + ..\..\projecttemplates\projecttemplates.proj = ..\..\projecttemplates\projecttemplates.proj + ..\..\samples\Samples.proj = ..\..\samples\Samples.proj + ..\..\samples\tools.proj = ..\..\samples\tools.proj + ..\..\vsi\vsi.proj = ..\..\vsi\vsi.proj + ..\..\vsix\vsix.proj = ..\..\vsix\vsix.proj EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.BuildTasks", "DotNetOpenAuth.BuildTasks.csproj", "{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/DotNetOpenAuth.BuildTasks/DowngradeProjects.cs b/src/DotNetOpenAuth.BuildTasks/DowngradeProjects.cs new file mode 100644 index 0000000..645522d --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/DowngradeProjects.cs @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------- +// <copyright file="DowngradeProjects.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using Microsoft.Build.BuildEngine; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// <summary> + /// Downgrades Visual Studio 2010 solutions and projects so that they load in Visual Studio 2008. + /// </summary> + public class DowngradeProjects : Task { + /// <summary> + /// Gets or sets the projects and solutions to downgrade. + /// </summary> + [Required] + public ITaskItem[] Projects { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether ASP.NET MVC 2 projects are downgraded to MVC 1.0. + /// </summary> + public bool DowngradeMvc2ToMvc1 { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + foreach (ITaskItem taskItem in this.Projects) { + switch (GetClassification(taskItem)) { + case ProjectClassification.VS2010Project: + this.Log.LogMessage(MessageImportance.Low, "Downgrading project \"{0}\".", taskItem.ItemSpec); + Project project = new Project(); + project.Load(taskItem.ItemSpec); + project.DefaultToolsVersion = "3.5"; + + if (this.DowngradeMvc2ToMvc1) { + string projectTypeGuids = project.GetEvaluatedProperty("ProjectTypeGuids"); + if (!string.IsNullOrEmpty(projectTypeGuids)) { + projectTypeGuids = projectTypeGuids.Replace("{F85E285D-A4E0-4152-9332-AB1D724D3325}", "{603c0e0b-db56-11dc-be95-000d561079b0}"); + project.SetProperty("ProjectTypeGuids", projectTypeGuids); + } + } + + // Web projects usually have an import that includes these substrings + foreach (Import import in project.Imports) { + import.ProjectPath = import.ProjectPath + .Replace("$(MSBuildExtensionsPath32)", "$(MSBuildExtensionsPath)") + .Replace("VisualStudio\\v10.0", "VisualStudio\\v9.0"); + } + + // VS2010 won't let you have a System.Core reference, but VS2008 requires it. + BuildItemGroup references = project.GetEvaluatedItemsByName("Reference"); + if (!references.Cast<BuildItem>().Any(item => item.FinalItemSpec.StartsWith("System.Core", StringComparison.OrdinalIgnoreCase))) { + project.AddNewItem("Reference", "System.Core"); + } + + project.Save(taskItem.ItemSpec); + break; + case ProjectClassification.VS2010Solution: + this.Log.LogMessage(MessageImportance.Low, "Downgrading solution \"{0}\".", taskItem.ItemSpec); + string[] contents = File.ReadAllLines(taskItem.ItemSpec); + if (contents[1] != "Microsoft Visual Studio Solution File, Format Version 11.00" || + contents[2] != "# Visual Studio 2010") { + this.Log.LogError("Unrecognized solution file header in \"{0}\".", taskItem.ItemSpec); + break; + } + + contents[1] = "Microsoft Visual Studio Solution File, Format Version 10.00"; + contents[2] = "# Visual Studio 2008"; + + for (int i = 3; i < contents.Length; i++) { + contents[i] = contents[i].Replace("TargetFrameworkMoniker = \".NETFramework,Version%3Dv", "TargetFramework = \""); + } + + File.WriteAllLines(taskItem.ItemSpec, contents); + break; + default: + this.Log.LogWarning("Unrecognized project type for \"{0}\".", taskItem.ItemSpec); + break; + } + } + + return !this.Log.HasLoggedErrors; + } + + private static ProjectClassification GetClassification(ITaskItem taskItem) { + if (Path.GetExtension(taskItem.ItemSpec).EndsWith("proj", StringComparison.OrdinalIgnoreCase)) { + return ProjectClassification.VS2010Project; + } else if (Path.GetExtension(taskItem.ItemSpec).Equals(".sln", StringComparison.OrdinalIgnoreCase)) { + return ProjectClassification.VS2010Solution; + } else { + return ProjectClassification.Unrecognized; + } + } + + private enum ProjectClassification { + VS2010Project, + VS2010Solution, + Unrecognized, + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs b/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs index 13a4b8f..babaab3 100644 --- a/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs +++ b/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs @@ -40,7 +40,13 @@ namespace DotNetOpenAuth.BuildTasks { // Figure out what the assembly names are of the references that are available. AssemblyName[] availableReferences = new AssemblyName[this.References.Length]; for (int i = 0; i < this.References.Length; i++) { - availableReferences[i] = AssemblyName.GetAssemblyName(this.References[i].ItemSpec); + if (File.Exists(this.References[i].ItemSpec)) { + availableReferences[i] = AssemblyName.GetAssemblyName(this.References[i].ItemSpec); + } else { + availableReferences[i] = new AssemblyName(Path.GetFileNameWithoutExtension(this.References[i].ItemSpec)) { + CodeBase = this.References[i].GetMetadata("FullPath"), + }; + } } foreach (var projectTaskItem in this.Projects) { diff --git a/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs b/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs index 92b0235..6c71740 100644 --- a/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs +++ b/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs @@ -6,13 +6,14 @@ namespace DotNetOpenAuth.BuildTasks { using System; + using System.Collections; using System.Collections.Generic; + using System.IO; using System.Linq; using System.Text; - using Microsoft.Build.Utilities; - using Microsoft.Build.Framework; - using System.IO; using Microsoft.Build.BuildEngine; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; /// <summary> /// Removes imports that only apply when a shipping tool sample builds as part of @@ -22,6 +23,10 @@ namespace DotNetOpenAuth.BuildTasks { [Required] public ITaskItem[] Projects { get; set; } + public string[] RemoveImportsStartingWith { get; set; } + + public ITaskItem[] AddReferences { get; set; } + /// <summary> /// Executes this instance. /// </summary> @@ -34,10 +39,21 @@ namespace DotNetOpenAuth.BuildTasks { Uri projectUri = new Uri(projectTaskItem.GetMetadata("FullPath")); project.Load(projectTaskItem.ItemSpec, ProjectLoadSettings.IgnoreMissingImports); - project.Imports.Cast<Import>() - .Where(import => import.ProjectPath.StartsWith(@"..\..\tools\", StringComparison.OrdinalIgnoreCase)) - .ToList() - .ForEach(import => project.Imports.RemoveImport(import)); + if (this.RemoveImportsStartingWith != null && this.RemoveImportsStartingWith.Length > 0) { + project.Imports.Cast<Import>() + .Where(import => this.RemoveImportsStartingWith.Any(start => import.ProjectPath.StartsWith(start, StringComparison.OrdinalIgnoreCase))) + .ToList() + .ForEach(import => project.Imports.RemoveImport(import)); + } + + if (this.AddReferences != null) { + foreach (var reference in this.AddReferences) { + BuildItem item = project.AddNewItem("Reference", reference.ItemSpec); + foreach (DictionaryEntry metadata in reference.CloneCustomMetadata()) { + item.SetMetadata((string)metadata.Key, (string)metadata.Value); + } + } + } project.Save(projectTaskItem.ItemSpec); } diff --git a/src/DotNetOpenAuth.BuildTasks/HardLinkCopy.cs b/src/DotNetOpenAuth.BuildTasks/HardLinkCopy.cs new file mode 100644 index 0000000..af2d1d0 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/HardLinkCopy.cs @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------- +// <copyright file="HardLinkCopy.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + using System.IO; + + public class HardLinkCopy : Task { + [Required] + public ITaskItem[] SourceFiles { get; set; } + + [Required] + public ITaskItem[] DestinationFiles { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + if (this.SourceFiles.Length != this.DestinationFiles.Length) { + this.Log.LogError("SourceFiles has {0} elements and DestinationFiles has {1} elements.", this.SourceFiles.Length, this.DestinationFiles.Length); + return false; + } + + for (int i = 0; i < this.SourceFiles.Length; i++) { + bool hardLink; + bool.TryParse(this.DestinationFiles[i].GetMetadata("HardLink"), out hardLink); + string sourceFile = this.SourceFiles[i].ItemSpec; + string destinationFile = this.DestinationFiles[i].ItemSpec; + this.Log.LogMessage( + MessageImportance.Low, + "Copying {0} -> {1}{2}.", + sourceFile, + destinationFile, + hardLink ? " as hard link" : string.Empty); + + if (!Directory.Exists(Path.GetDirectoryName(destinationFile))) { + Directory.CreateDirectory(Path.GetDirectoryName(destinationFile)); + } + + if (hardLink) { + if (File.Exists(destinationFile)) { + File.Delete(destinationFile); + } + NativeMethods.CreateHardLink(sourceFile, destinationFile); + } else { + File.Copy(sourceFile, destinationFile, true); + } + } + + return !this.Log.HasLoggedErrors; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs index 1a8a17d..d162cd6 100644 --- a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs +++ b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs @@ -7,18 +7,27 @@ namespace DotNetOpenAuth.BuildTasks { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.IO; using System.Linq; using System.Text; - using Microsoft.Build.Framework; - using Microsoft.Build.Utilities; using System.Xml.Linq; - using System.IO; using Microsoft.Build.BuildEngine; - using System.Diagnostics.Contracts; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; public class MergeProjectWithVSTemplate : Task { internal const string VSTemplateNamespace = "http://schemas.microsoft.com/developer/vstemplate/2005"; + internal const string VsixNamespace = "http://schemas.microsoft.com/developer/vsx-schema/2010"; + + /// <summary> + /// A dictionary where the key is the project name and the value is the path contribution. + /// </summary> + private Dictionary<string, string> vsixContributionToPath = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + [Required] public string[] ProjectItemTypes { get; set; } @@ -26,13 +35,44 @@ namespace DotNetOpenAuth.BuildTasks { public string[] ReplaceParametersExtensions { get; set; } [Required] - public ITaskItem[] Templates { get; set; } + public ITaskItem[] SourceTemplates { get; set; } + + [Required] + public ITaskItem[] SourceProjects { get; set; } + + [Required] + public ITaskItem[] DestinationTemplates { get; set; } + + public ITaskItem[] SourcePathExceptions { get; set; } + + /// <summary> + /// Gets or sets the maximum length a project item's relative path should + /// be limited to, artificially renaming them as necessary. + /// </summary> + /// <value>Use 0 to disable the renaming feature.</value> + public int MaximumRelativePathLength { get; set; } + + /// <summary> + /// Gets or sets the project item paths from the source project to copy to the destination location. + /// </summary> + [Output] + public ITaskItem[] ProjectItems { get; set; } /// <summary> /// Executes this instance. /// </summary> public override bool Execute() { - foreach(ITaskItem sourceTemplateTaskItem in this.Templates) { + if (this.DestinationTemplates.Length != this.SourceTemplates.Length) { + this.Log.LogError("SourceTemplates array has length {0} while DestinationTemplates array has length {1}, but must equal.", this.SourceTemplates.Length, this.DestinationTemplates.Length); + } + if (this.SourceProjects.Length != this.SourceTemplates.Length) { + this.Log.LogError("SourceTemplates array has length {0} while SourceProjects array has length {1}, but must equal.", this.SourceTemplates.Length, this.SourceProjects.Length); + } + + var projectItemsToCopy = new List<ITaskItem>(); + + for (int iTemplate = 0; iTemplate < this.SourceTemplates.Length; iTemplate++) { + ITaskItem sourceTemplateTaskItem = this.SourceTemplates[iTemplate]; var template = XElement.Load(sourceTemplateTaskItem.ItemSpec); var templateContentElement = template.Element(XName.Get("TemplateContent", VSTemplateNamespace)); var projectElement = templateContentElement.Element(XName.Get("Project", VSTemplateNamespace)); @@ -41,37 +81,65 @@ namespace DotNetOpenAuth.BuildTasks { continue; } - var projectPath = Path.Combine(Path.GetDirectoryName(sourceTemplateTaskItem.ItemSpec), projectElement.Attribute("File").Value); + var projectPath = this.SourceProjects[iTemplate].ItemSpec; + var projectDirectory = Path.GetDirectoryName(Path.Combine(Path.GetDirectoryName(sourceTemplateTaskItem.GetMetadata("FullPath")), projectElement.Attribute("File").Value)); Log.LogMessage("Merging project \"{0}\" with \"{1}\".", projectPath, sourceTemplateTaskItem.ItemSpec); var sourceProject = new Project(); sourceProject.Load(projectPath); + var projectItems = sourceProject.EvaluatedItems.Cast<BuildItem>().Where(item => this.ProjectItemTypes.Contains(item.Name)); + + // Figure out where every project item is in source, and where it will go in the destination, + // taking into account a given maximum path length that may require that we shorten the path. + PathSegment root = new PathSegment(); + root.Add(projectItems.Select(item => item.Include)); + root.EnsureSelfAndChildrenNoLongerThan(this.MaximumRelativePathLength); // Collect the project items from the project that are appropriate // to include in the .vstemplate file. - var itemsByFolder = from item in sourceProject.EvaluatedItems.Cast<BuildItem>() - where this.ProjectItemTypes.Contains(item.Name) - orderby item.Include - group item by Path.GetDirectoryName(item.Include); - foreach (var folder in itemsByFolder) { - XElement parentNode = FindOrCreateParent(folder.Key, projectElement); - - foreach (var item in folder) { - bool replaceParameters = this.ReplaceParametersExtensions.Contains(Path.GetExtension(item.Include)); + foreach (var folder in root.SelfAndDescendents.Where(path => !path.IsLeaf && path.LeafChildren.Any())) { + XElement parentNode = projectElement; + parentNode = FindOrCreateParent(folder.CurrentPath, projectElement); + if (folder.NameChanged) { + parentNode.SetAttributeValue("TargetFolderName", folder.OriginalName); + } + + foreach (var item in folder.LeafChildren) { var itemName = XName.Get("ProjectItem", VSTemplateNamespace); - var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.Include), StringComparison.OrdinalIgnoreCase)); + // The project item MAY be hard-coded in the .vstemplate file, under the original name. + var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.OriginalName), StringComparison.OrdinalIgnoreCase)); if (projectItem == null) { - projectItem = new XElement(itemName, Path.GetFileName(item.Include)); + projectItem = new XElement(itemName, item.CurrentName); parentNode.Add(projectItem); } - if (replaceParameters) { + if (item.NameChanged) { + projectItem.Value = item.CurrentName; // set Value in case it was a hard-coded item in the .vstemplate file. + projectItem.SetAttributeValue("TargetFileName", item.OriginalName); + } + if (this.ReplaceParametersExtensions.Contains(Path.GetExtension(item.OriginalPath))) { projectItem.SetAttributeValue("ReplaceParameters", "true"); } } } - template.Save(sourceTemplateTaskItem.ItemSpec); + template.Save(this.DestinationTemplates[iTemplate].ItemSpec); + foreach (var pair in root.LeafDescendents) { + TaskItem item = new TaskItem(Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.OriginalPath)); + string apparentSource = Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.OriginalPath); + var sourcePathException = this.SourcePathExceptions.FirstOrDefault(ex => string.Equals(ex.ItemSpec, apparentSource)); + if (sourcePathException != null) { + item.SetMetadata("SourceFullPath", sourcePathException.GetMetadata("ActualSource")); + } else { + item.SetMetadata("SourceFullPath", Path.GetFullPath(apparentSource)); + } + item.SetMetadata("DestinationFullPath", Path.GetFullPath(Path.Combine(Path.GetDirectoryName(this.DestinationTemplates[iTemplate].ItemSpec), pair.CurrentPath))); + item.SetMetadata("RecursiveDir", Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec)); + item.SetMetadata("Transform", this.ReplaceParametersExtensions.Contains(Path.GetExtension(pair.OriginalName)) ? "true" : "false"); + projectItemsToCopy.Add(item); + } } + this.ProjectItems = projectItemsToCopy.ToArray(); + return !Log.HasLoggedErrors; } diff --git a/src/DotNetOpenAuth.BuildTasks/NativeMethods.cs b/src/DotNetOpenAuth.BuildTasks/NativeMethods.cs new file mode 100644 index 0000000..26de3a4 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/NativeMethods.cs @@ -0,0 +1,18 @@ +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Runtime.InteropServices; + + internal static class NativeMethods { + [DllImport("kernel32", SetLastError = true)] + private static extern bool CreateHardLink(string newFileName, string existingFileName, IntPtr securityAttributes); + + internal static void CreateHardLink(string existingFileName, string newFileName) { + if (!CreateHardLink(newFileName, existingFileName, IntPtr.Zero)) { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + } + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/PathSegment.cs b/src/DotNetOpenAuth.BuildTasks/PathSegment.cs new file mode 100644 index 0000000..56655ff --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/PathSegment.cs @@ -0,0 +1,321 @@ +//----------------------------------------------------------------------- +// <copyright file="PathSegment.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Diagnostics.Contracts; + using System.IO; + using System.Linq; + using System.Text; + + internal class PathSegment { + private const float ParentChildResizeThreshold = 0.30f; + private readonly PathSegment parent; + private readonly string originalName; + private string currentName; + private bool minimized; + private static readonly string[] ReservedFileNames = "CON PRN AUX CLOCK$ NUL COM0 COM1 COM2 COM3 COM4 COM5 COM6 COM7 COM8 COM9 LPT0 LPT1 LPT2 LPT3 LPT4 LPT5 LPT6 LPT7 LPT8 LPT9".Split(' '); + + internal PathSegment() { + this.currentName = string.Empty; + this.originalName = string.Empty; + this.minimized = true; + this.Children = new Collection<PathSegment>(); + } + + private PathSegment(string originalName, PathSegment parent) + : this() { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(originalName)); + Contract.Requires<ArgumentNullException>(parent != null); + this.currentName = this.originalName = originalName; + this.parent = parent; + this.minimized = false; + } + + internal string OriginalPath { + get { + if (this.parent != null) { + return Path.Combine(this.parent.OriginalPath, this.originalName); + } else { + return this.originalName; + } + } + } + + internal string CurrentPath { + get { + if (this.parent != null) { + return Path.Combine(this.parent.CurrentPath, this.currentName); + } else { + return this.currentName; + } + } + } + + internal string CurrentName { + get { return this.currentName; } + } + + internal string OriginalName { + get { return this.originalName; } + } + + private int SegmentCount { + get { + int parents = this.parent != null ? this.parent.SegmentCount : 0; + return parents + 1; + } + } + + internal int FullLength { + get { + if (this.parent != null) { + int parentLength = this.parent.FullLength; + if (parentLength > 0) { + parentLength++; // allow for an in between slash + } + return parentLength + this.currentName.Length; + } else { + return this.currentName.Length; + } + } + } + + internal bool NameChanged { + get { return !string.Equals(this.currentName, this.originalName, StringComparison.OrdinalIgnoreCase); } + } + + internal bool IsLeaf { + get { return this.Children.Count == 0; } + } + + internal IEnumerable<PathSegment> Descendents { + get { + IEnumerable<PathSegment> result = this.Children; + foreach (PathSegment child in this.Children) { + result = result.Concat(child.Descendents); + } + + return result; + } + } + + internal IEnumerable<PathSegment> Ancestors { + get { + PathSegment parent = this.parent; + while (parent != null) { + yield return parent; + parent = parent.parent; + } + } + } + + internal IEnumerable<PathSegment> SelfAndDescendents { + get { + yield return this; + foreach (var child in this.Descendents) { + yield return child; + } + } + } + + internal IEnumerable<PathSegment> SelfAndAncestors { + get { + yield return this; + foreach (var parent in this.Ancestors) { + yield return parent; + } + } + } + + internal IEnumerable<PathSegment> LeafChildren { + get { return this.Children.Where(child => child.IsLeaf); } + } + + internal IEnumerable<PathSegment> LeafDescendents { + get { return this.Descendents.Where(child => child.IsLeaf); } + } + + internal IEnumerable<PathSegment> Siblings { + get { return this.parent != null ? this.parent.Children : Enumerable.Empty<PathSegment>(); } + } + + internal Collection<PathSegment> Children { get; private set; } + + public override string ToString() { + string path; + if (this.NameChanged) { + path = "{" + this.originalName + " => " + this.currentName + "}"; + } else { + path = this.currentName; + } + + if (path.Length > 0 && !this.IsLeaf) { + path += "\\"; + } + + if (this.parent != null) { + path = parent.ToString() + path; + } + + return path; + } + + internal PathSegment Add(string originalPath) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(originalPath)); + Contract.Ensures(Contract.Result<PathSegment>() != null); + string[] segments = originalPath.Split(Path.DirectorySeparatorChar); + return this.Add(segments, 0); + } + + internal void Add(IEnumerable<string> originalPaths) { + foreach (string path in originalPaths) { + this.Add(path); + } + } + + internal int EnsureSelfAndChildrenNoLongerThan(int maxLength) { + Contract.Requires<ArgumentOutOfRangeException>(maxLength > 0, "A path can only have a positive length."); + Contract.Requires<ArgumentOutOfRangeException>(this.parent == null || maxLength > this.parent.FullLength + 1, "A child path cannot possibly be made shorter than its parent."); + Contract.Ensures(Contract.Result<int>() <= maxLength); + const int uniqueBase = 16; + + // Find the items that are too long, and always work on the longest one + var longPaths = this.SelfAndDescendents.Where(path => path.FullLength > maxLength).OrderByDescending(path => path.FullLength); + PathSegment longPath; + while ((longPath = longPaths.FirstOrDefault()) != null) { + // Keep working on this one until it's short enough. + do { + int tooLongBy = longPath.FullLength - maxLength; + var longSegments = longPath.SelfAndAncestors.Where(segment => !segment.minimized).OrderByDescending(segment => segment.CurrentName.Length); + PathSegment longestSegment = longSegments.FirstOrDefault(); + if (longestSegment == null) { + throw new InvalidOperationException("Unable to shrink path length sufficiently."); + } + PathSegment secondLongestSegment = longSegments.Skip(1).FirstOrDefault(); + int shortenByUpTo; + if (secondLongestSegment != null) { + shortenByUpTo = Math.Min(tooLongBy, Math.Max(1, longestSegment.CurrentName.Length - secondLongestSegment.CurrentName.Length)); + } else { + shortenByUpTo = tooLongBy; + } + int minimumGuaranteedUniqueLength = Math.Max(1, RoundUp(Math.Log(longestSegment.Siblings.Count(), uniqueBase))); + int allowableSegmentLength = Math.Max(minimumGuaranteedUniqueLength, longestSegment.CurrentName.Length - shortenByUpTo); + if (allowableSegmentLength >= longestSegment.CurrentName.Length) { + // We can't make this segment any shorter. + longestSegment.minimized = true; + } + longestSegment.currentName = longestSegment.CreateUniqueShortFileName(longestSegment.CurrentName, allowableSegmentLength); + } while (longPath.FullLength > maxLength); + } + + // Return the total length of self or longest child. + return this.SelfAndDescendents.Max(c => c.FullLength); + } + + internal PathSegment FindByOriginalPath(string originalPath) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(originalPath)); + string[] segments = originalPath.Split(Path.DirectorySeparatorChar); + return this.FindByOriginalPath(segments, 0); + } + + private string GetUniqueShortName(string preferredPrefix, string preferredSuffix, int allowableLength) { + Contract.Requires<ArgumentNullException>(preferredPrefix != null); + Contract.Requires<ArgumentNullException>(preferredSuffix != null); + Contract.Requires<ArgumentException>(allowableLength > 0); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); + Contract.Ensures(Contract.Result<string>().Length <= allowableLength); + string candidateName = string.Empty; + int i; + for (i = -1; candidateName.Length == 0 || ReservedFileNames.Contains(candidateName, StringComparer.OrdinalIgnoreCase) || this.Siblings.Any(child => string.Equals(child.CurrentName, candidateName, StringComparison.OrdinalIgnoreCase)); i++) { + string unique = i < 0 ? string.Empty : i.ToString("x"); + if (allowableLength < unique.Length) { + throw new InvalidOperationException("Unable to shorten path sufficiently to fit constraints."); + } + + candidateName = unique; + + // Suffix gets higher priority than the prefix, but only if the entire suffix can be appended. + if (candidateName.Length + preferredSuffix.Length <= allowableLength) { + candidateName += preferredSuffix; + } + + // Now prepend as much of the prefix as fits. + candidateName = preferredPrefix.Substring(0, Math.Min(allowableLength - candidateName.Length, preferredPrefix.Length)) + candidateName; + } + + return candidateName; + } + + private static int RoundUp(double value) { + int roundedValue = (int)value; + if (roundedValue < value) { + roundedValue++; + } + + return roundedValue; + } + + private string CreateUniqueShortFileName(string fileName, int targetLength) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(fileName)); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); + Contract.Ensures(Contract.Result<string>().Length <= targetLength); + + // The filename may already full within the target length. + if (fileName.Length <= targetLength) { + return fileName; + } + + string preferredPrefix = Path.GetFileNameWithoutExtension(fileName); + string preferredSuffix = Path.GetExtension(fileName); + + string shortenedFileName = GetUniqueShortName(preferredPrefix, preferredSuffix, targetLength); + return shortenedFileName; + } + + private void ShortenThis(int targetLength) { + this.currentName = CreateUniqueShortFileName(this.originalName, targetLength); + } + + private PathSegment Add(string[] segments, int segmentIndex) { + Contract.Requires<ArgumentNullException>(segments != null); + Contract.Requires<ArgumentOutOfRangeException>(segmentIndex < segments.Length); + Contract.Ensures(Contract.Result<PathSegment>() != null); + var match = this.Children.SingleOrDefault(child => String.Equals(child.originalName, segments[segmentIndex])); + if (match == null) { + match = new PathSegment(segments[segmentIndex], this); + this.Children.Add(match); + if (segments.Length == segmentIndex + 1) { + return match; + } + } + + return match.Add(segments, segmentIndex + 1); + } + + private PathSegment FindByOriginalPath(string[] segments, int segmentIndex) { + Contract.Requires<ArgumentNullException>(segments != null); + Contract.Requires<ArgumentOutOfRangeException>(segmentIndex < segments.Length); + if (string.Equals(this.originalName, segments[segmentIndex], StringComparison.OrdinalIgnoreCase)) { + if (segmentIndex == segments.Length - 1) { + return this; + } + + foreach (var child in this.Children) { + var match = child.FindByOriginalPath(segments, segmentIndex + 1); + if (match != null) { + return match; + } + } + } + + return null; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/Publicize.cs b/src/DotNetOpenAuth.BuildTasks/Publicize.cs new file mode 100644 index 0000000..f1781a7 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/Publicize.cs @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------- +// <copyright file="Publicize.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using Microsoft.Build.BuildEngine; + using Microsoft.Build.Utilities; + using Microsoft.Build.Framework; + + public class Publicize : ToolTask { + [Required] + public string MSBuildExtensionsPath { get; set; } + + [Required] + public ITaskItem Assembly { get; set; } + + public bool DelaySign { get; set; } + + public string KeyFile { get; set; } + + public bool SkipUnchangedFiles { get; set; } + + [Output] + public ITaskItem AccessorAssembly { get; set; } + + /// <summary> + /// Generates the full path to tool. + /// </summary> + /// <returns>An absolute path.</returns> + protected override string GenerateFullPathToTool() { + string toolPath = Path.Combine(this.MSBuildExtensionsPath, @"Microsoft\VisualStudio\v9.0\TeamTest\Publicize.exe"); + return toolPath; + } + + /// <summary> + /// Gets the name of the tool. + /// </summary> + /// <value>The name of the tool.</value> + protected override string ToolName { + get { return "Publicize.exe"; } + } + + /// <summary> + /// Validates the parameters. + /// </summary> + protected override bool ValidateParameters() { + if (!base.ValidateParameters()) { + return false; + } + + if (this.DelaySign && string.IsNullOrEmpty(this.KeyFile)) { + this.Log.LogError("DelaySign=true, but no KeyFile given."); + return false; + } + + return true; + } + + /// <summary> + /// Generates the command line commands. + /// </summary> + protected override string GenerateCommandLineCommands() { + CommandLineBuilder builder = new CommandLineBuilder(); + + if (this.DelaySign) { + builder.AppendSwitch("/delaysign"); + } + + builder.AppendSwitchIfNotNull("/keyfile:", this.KeyFile); + + builder.AppendFileNameIfNotNull(this.Assembly); + + return builder.ToString(); + } + + public override bool Execute() { + this.AccessorAssembly = new TaskItem(this.Assembly); + this.AccessorAssembly.ItemSpec = Path.Combine( + Path.GetDirectoryName(this.AccessorAssembly.ItemSpec), + Path.GetFileNameWithoutExtension(this.AccessorAssembly.ItemSpec) + "_Accessor") + Path.GetExtension(this.AccessorAssembly.ItemSpec); + + if (this.SkipUnchangedFiles && File.GetLastWriteTimeUtc(this.Assembly.ItemSpec) < File.GetLastWriteTimeUtc(this.AccessorAssembly.ItemSpec)) { + Log.LogMessage(MessageImportance.Low, "Skipping public accessor generation for {0} because {1} is up to date.", this.Assembly.ItemSpec, this.AccessorAssembly.ItemSpec); + return true; + } + + return base.Execute(); + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/Purge.cs b/src/DotNetOpenAuth.BuildTasks/Purge.cs index e19e485..cf1a214 100644 --- a/src/DotNetOpenAuth.BuildTasks/Purge.cs +++ b/src/DotNetOpenAuth.BuildTasks/Purge.cs @@ -7,12 +7,12 @@ namespace DotNetOpenAuth.BuildTasks { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using System.Text; - using Microsoft.Build.Utilities; - using Microsoft.Build.Framework; - using System.IO; using System.Text.RegularExpressions; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; /// <summary> /// Purges directory trees of all directories and files that are not on a whitelist. @@ -40,6 +40,7 @@ namespace DotNetOpenAuth.BuildTasks { /// <summary> /// Gets or sets the files that should be NOT be purged. /// </summary> + [Required] public ITaskItem[] IntendedFiles { get; set; } /// <summary> @@ -82,7 +83,7 @@ namespace DotNetOpenAuth.BuildTasks { } private static string NormalizePath(string path) { - return Regex.Replace(path, @"\\+", @"\"); + return Path.GetFullPath(Regex.Replace(path, @"\\+", @"\")); } } } diff --git a/src/DotNetOpenAuth.BuildTasks/Utilities.cs b/src/DotNetOpenAuth.BuildTasks/Utilities.cs new file mode 100644 index 0000000..80e1733 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/Utilities.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------- +// <copyright file="Utilities.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + + internal static class Utilities { + internal static string SuppressCharacters(string fileName, char[] suppress, char replacement) { + Contract.Requires<ArgumentNullException>(fileName != null); + Contract.Requires<ArgumentNullException>(suppress != null); + + if (fileName.IndexOfAny(suppress) < 0) { + return fileName; + } + + StringBuilder builder = new StringBuilder(fileName); + foreach (char ch in suppress) { + builder.Replace(ch, replacement); + } + + return builder.ToString(); + } + } +} diff --git a/src/DotNetOpenAuth.Test/AssemblyTesting.cs b/src/DotNetOpenAuth.Test/AssemblyTesting.cs index 7659a82..92b08ec 100644 --- a/src/DotNetOpenAuth.Test/AssemblyTesting.cs +++ b/src/DotNetOpenAuth.Test/AssemblyTesting.cs @@ -6,12 +6,12 @@ namespace DotNetOpenAuth.Test { using System.Diagnostics.Contracts; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [SetUpFixture] public class AssemblyTesting { - [AssemblyInitialize] - public static void AssemblyInitialize(TestContext tc) { + [SetUp] + public static void AssemblyInitialize() { // Make contract failures become test failures. Contract.ContractFailed += (sender, e) => { // For now, we have tests that verify that preconditions throw exceptions. diff --git a/src/DotNetOpenAuth.Test/Configuration/SectionTests.cs b/src/DotNetOpenAuth.Test/Configuration/SectionTests.cs index 73aad6d..e423053 100644 --- a/src/DotNetOpenAuth.Test/Configuration/SectionTests.cs +++ b/src/DotNetOpenAuth.Test/Configuration/SectionTests.cs @@ -9,11 +9,11 @@ namespace DotNetOpenAuth.Test.Configuration { using System.Linq; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class SectionTests { - [TestMethod] + [TestCase] public void UntrustedWebRequest() { var uwr = DotNetOpenAuthSection.Configuration.Messaging.UntrustedWebRequest; @@ -29,12 +29,12 @@ namespace DotNetOpenAuth.Test.Configuration { Assert.IsTrue(uwr.WhitelistHostsRegex.KeysAsStrings.Contains(".+trusted.+")); } - [TestMethod] + [TestCase] public void OpenIdMaxAuthenticationTime() { Assert.AreEqual(TimeSpan.Parse("00:08:17"), DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime); } - [TestMethod] + [TestCase] public void OpenIdRelyingParty() { var rp = DotNetOpenAuthSection.Configuration.OpenId.RelyingParty; Assert.IsNull(rp.ApplicationStore.CustomType); @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.Test.Configuration { Assert.IsFalse(rp.SecuritySettings.RequireSsl); } - [TestMethod] + [TestCase] public void OpenIdProvider() { var op = DotNetOpenAuthSection.Configuration.OpenId.Provider; Assert.IsNull(op.ApplicationStore.CustomType); diff --git a/src/DotNetOpenAuth.Test/CoordinatorBase.cs b/src/DotNetOpenAuth.Test/CoordinatorBase.cs index df331f3..f25964c 100644 --- a/src/DotNetOpenAuth.Test/CoordinatorBase.cs +++ b/src/DotNetOpenAuth.Test/CoordinatorBase.cs @@ -11,7 +11,7 @@ namespace DotNetOpenAuth.Test { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; internal abstract class CoordinatorBase<T1, T2> { private Action<T1> party1Action; @@ -84,7 +84,7 @@ namespace DotNetOpenAuth.Test { // Use the failing reason of a failing sub-thread as our reason, if anything failed. if (failingException != null) { - throw new AssertFailedException("Coordinator thread threw unhandled exception: " + failingException, failingException); + throw new AssertionException("Coordinator thread threw unhandled exception: " + failingException, failingException); } } } diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj index d4997ae..df29b1f 100644 --- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj +++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj @@ -1,7 +1,12 @@ -<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> + <ProjectRoot Condition="'$(ProjectRoot)' == ''">$(MSBuildProjectDirectory)\..\..\</ProjectRoot> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + </PropertyGroup> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" /> + <PropertyGroup> <ProductVersion>9.0.30729</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}</ProjectGuid> @@ -9,15 +14,31 @@ <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>DotNetOpenAuth.Test</RootNamespace> <AssemblyName>DotNetOpenAuth.Test</AssemblyName> - <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> - <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <FileUpgradeFlags> + </FileUpgradeFlags> + <OldToolsVersion>3.5</OldToolsVersion> + <UpgradeBackupLocation /> + <PublishUrl>publish\</PublishUrl> + <Install>true</Install> + <InstallFrom>Disk</InstallFrom> + <UpdateEnabled>false</UpdateEnabled> + <UpdateMode>Foreground</UpdateMode> + <UpdateInterval>7</UpdateInterval> + <UpdateIntervalUnits>Days</UpdateIntervalUnits> + <UpdatePeriodically>false</UpdatePeriodically> + <UpdateRequired>false</UpdateRequired> + <MapFileExtensions>true</MapFileExtensions> + <ApplicationRevision>0</ApplicationRevision> + <ApplicationVersion>1.0.0.%2a</ApplicationVersion> + <IsWebBootstrapper>false</IsWebBootstrapper> + <UseApplicationTrust>false</UseApplicationTrust> + <BootstrapperEnabled>true</BootstrapperEnabled> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> - <OutputPath>..\..\bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> @@ -46,11 +67,11 @@ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface> <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure> <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> - <OutputPath>..\..\bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> @@ -79,13 +100,10 @@ <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine> <CodeContractsRunInBackground>True</CodeContractsRunInBackground> <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies> - </PropertyGroup> - <PropertyGroup> - <SignAssembly>true</SignAssembly> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' "> <DebugSymbols>true</DebugSymbols> - <OutputPath>..\..\bin\CodeAnalysis\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <DebugType>full</DebugType> <PlatformTarget>AnyCPU</PlatformTarget> @@ -117,21 +135,13 @@ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure> <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires> <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\log4net.dll</HintPath> - </Reference> - <Reference Include="Microsoft.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\Microsoft.Contracts.dll</HintPath> - </Reference> - <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> - <Reference Include="Moq, Version=3.1.416.3, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\Moq.dll</HintPath> - </Reference> + <Reference Include="log4net" /> + <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" /> + <Reference Include="Moq" /> + <Reference Include="NUnit.Framework"/> <Reference Include="System" /> <Reference Include="System.configuration" /> <Reference Include="System.Core"> @@ -185,6 +195,7 @@ <Compile Include="Mocks\InMemoryTokenManager.cs" /> <Compile Include="Mocks\MockHttpRequest.cs" /> <Compile Include="Mocks\MockIdentifier.cs" /> + <Compile Include="Mocks\MockIdentifierDiscoveryService.cs" /> <Compile Include="Mocks\MockOpenIdExtension.cs" /> <Compile Include="Mocks\MockRealm.cs" /> <Compile Include="Mocks\MockTransformationBindingElement.cs" /> @@ -219,6 +230,8 @@ <Compile Include="Messaging\Bindings\StandardReplayProtectionBindingElementTests.cs" /> <Compile Include="OpenId\ChannelElements\SigningBindingElementTests.cs" /> <Compile Include="OpenId\DiffieHellmanTests.cs" /> + <Compile Include="OpenId\DiscoveryServices\UriDiscoveryServiceTests.cs" /> + <Compile Include="OpenId\DiscoveryServices\XriDiscoveryProxyServiceTests.cs" /> <Compile Include="OpenId\Extensions\AttributeExchange\FetchRequestTests.cs" /> <Compile Include="OpenId\Extensions\AttributeExchange\FetchResponseTests.cs" /> <Compile Include="OpenId\Extensions\AttributeExchange\AttributeExchangeRoundtripTests.cs" /> @@ -255,6 +268,7 @@ <Compile Include="OpenId\OpenIdCoordinator.cs" /> <Compile Include="OpenId\AssociationHandshakeTests.cs" /> <Compile Include="OpenId\OpenIdTestBase.cs" /> + <Compile Include="OpenId\OpenIdUtilitiesTests.cs" /> <Compile Include="OpenId\Provider\PerformanceTests.cs" /> <Compile Include="OpenId\ProviderEndpointDescriptionTests.cs" /> <Compile Include="OpenId\Provider\AnonymousRequestTests.cs" /> @@ -267,10 +281,11 @@ <Compile Include="OpenId\RelyingParty\NegativeAuthenticationResponseTests.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdTextBoxTests.cs" /> <Compile Include="OpenId\RelyingParty\PositiveAnonymousResponseTests.cs" /> + <Compile Include="OpenId\RelyingParty\PositiveAuthenticationResponseSnapshotTests.cs" /> <Compile Include="OpenId\RelyingParty\PositiveAuthenticationResponseTests.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyTests.cs" /> <Compile Include="OpenId\RelyingParty\RelyingPartySecuritySettingsTests.cs" /> - <Compile Include="OpenId\RelyingParty\ServiceEndpointTests.cs" /> + <Compile Include="OpenId\RelyingParty\IdentifierDiscoveryResultTests.cs" /> <Compile Include="OpenId\UriIdentifierTests.cs" /> <Compile Include="OpenId\XriIdentifierTests.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> @@ -287,15 +302,13 @@ <ProjectReference Include="..\DotNetOpenAuth\DotNetOpenAuth.csproj"> <Project>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</Project> <Name>DotNetOpenAuth</Name> + <Shadow>true</Shadow> </ProjectReference> </ItemGroup> <ItemGroup> <EmbeddedResource Include="Logging.config" /> </ItemGroup> <ItemGroup> - <Shadow Include="Test References\DotNetOpenAuth.accessor" /> - </ItemGroup> - <ItemGroup> <EmbeddedResource Include="OpenId\Discovery\htmldiscovery\html1020.html" /> <EmbeddedResource Include="OpenId\Discovery\htmldiscovery\html10both.html" /> <EmbeddedResource Include="OpenId\Discovery\htmldiscovery\html10del.html" /> @@ -325,8 +338,39 @@ <None Include="App.config" /> </ItemGroup> <ItemGroup> + <EmbeddedResource Include="OpenId\Discovery\xrdsdiscovery\xrds20dual.xml" /> + </ItemGroup> + <ItemGroup> <Folder Include="OpenId\UI\" /> </ItemGroup> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" /> -</Project> + <ItemGroup> + <BootstrapperPackage Include=".NETFramework,Version=v4.0"> + <Visible>False</Visible> + <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName> + <Install>true</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Client.3.5"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName> + <Install>false</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1</ProductName> + <Install>false</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1"> + <Visible>False</Visible> + <ProductName>Windows Installer 3.1</ProductName> + <Install>true</Install> + </BootstrapperPackage> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="OpenId\Discovery\htmldiscovery\html20provWithEmptyXrds.html" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="OpenId\Discovery\htmldiscovery\html20provWithBadXrds.html" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> +</Project>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.Test/Hosting/HostingTests.cs b/src/DotNetOpenAuth.Test/Hosting/HostingTests.cs index d7de7a1..b7e04f7 100644 --- a/src/DotNetOpenAuth.Test/Hosting/HostingTests.cs +++ b/src/DotNetOpenAuth.Test/Hosting/HostingTests.cs @@ -12,11 +12,11 @@ namespace DotNetOpenAuth.Test.Hosting { using System.Net; using System.Text; using DotNetOpenAuth.Test.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture, Category("HostASPNET")] public class HostingTests : TestBase { - [TestMethod] + [TestCase] public void AspHostBasicTest() { try { using (AspNetHost host = AspNetHost.CreateHost(TestWebDirectory)) { @@ -25,7 +25,7 @@ namespace DotNetOpenAuth.Test.Hosting { Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); using (StreamReader sr = new StreamReader(response.GetResponseStream())) { string content = sr.ReadToEnd(); - StringAssert.Contains(content, "Test home page"); + StringAssert.Contains("Test home page", content); } } } diff --git a/src/DotNetOpenAuth.Test/LocalizationTests.cs b/src/DotNetOpenAuth.Test/LocalizationTests.cs index 50e9a34..4920deb 100644 --- a/src/DotNetOpenAuth.Test/LocalizationTests.cs +++ b/src/DotNetOpenAuth.Test/LocalizationTests.cs @@ -8,19 +8,21 @@ namespace DotNetOpenAuth.Test { using System; using System.Globalization; using System.Threading; + using System.Web; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; /// <summary> /// Tests various localized resources work as expected. /// </summary> - [TestClass] - public class LocalizationTests { + [TestFixture] + public class LocalizationTests : TestBase { /// <summary> /// Tests that Serbian localized strings are correctly installed. /// </summary> - [TestMethod, ExpectedException(typeof(InvalidOperationException), "Ovaj metod zahteva tekući HttpContext. Kao alternativa, koristite preklopljeni metod koji dozvoljava da se prosledi informacija bez HttpContext-a.")] + [TestCase, ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Ovaj metod zahteva tekući HttpContext. Kao alternativa, koristite preklopljeni metod koji dozvoljava da se prosledi informacija bez HttpContext-a.")] public void Serbian() { + HttpContext.Current = null; // our testbase initializes this, but it must be null to throw Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("sr"); ErrorUtilities.VerifyHttpContext(); } diff --git a/src/DotNetOpenAuth.Test/Logging.config b/src/DotNetOpenAuth.Test/Logging.config index cd19de2..87da027 100644 --- a/src/DotNetOpenAuth.Test/Logging.config +++ b/src/DotNetOpenAuth.Test/Logging.config @@ -30,7 +30,4 @@ <logger name="DotNetOpenAuth.Test"> <level value="Debug" /> </logger> - <logger name="DotNetOpenAuth.OpenId.ChannelElements.SigningBindingElement"> - <level value="WARN" /> - </logger> </log4net> diff --git a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs index 3cc792b..9ba433d 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs @@ -9,11 +9,11 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class StandardExpirationBindingElementTests : MessagingTestBase { - [TestMethod] + [TestCase] public void SendSetsTimestamp() { TestExpiringMessage message = new TestExpiringMessage(MessageTransport.Indirect); message.Recipient = new Uri("http://localtest"); @@ -24,13 +24,13 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { Assert.IsTrue(DateTime.UtcNow - ((IExpiringProtocolMessage)message).UtcCreationDate < TimeSpan.FromSeconds(3), "The timestamp on the message was not set on send."); } - [TestMethod] + [TestCase] public void VerifyGoodTimestampIsAccepted() { this.Channel = CreateChannel(MessageProtections.Expiration); this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false); } - [TestMethod, ExpectedException(typeof(ExpiredMessageException))] + [TestCase, ExpectedException(typeof(ExpiredMessageException))] public void VerifyBadTimestampIsRejected() { this.Channel = CreateChannel(MessageProtections.Expiration); this.ParameterizedReceiveProtectedTest(DateTime.UtcNow - StandardExpirationBindingElement.MaximumMessageAge - TimeSpan.FromSeconds(1), false); diff --git a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs index 14651bc..d8698ce 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs @@ -15,16 +15,16 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class StandardReplayProtectionBindingElementTests : MessagingTestBase { private Protocol protocol; private StandardReplayProtectionBindingElement nonceElement; private IReplayProtectedProtocolMessage message; private INonceStore nonceStore; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { /// <summary> /// Verifies that the generated nonce includes random characters. /// </summary> - [TestMethod] + [TestCase] public void RandomCharactersTest() { Assert.IsNotNull(this.nonceElement.ProcessOutgoingMessage(this.message)); Assert.IsNotNull(this.message.Nonce, "No nonce was set on the message."); @@ -56,7 +56,7 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { /// <summary> /// Verifies that a message is received correctly. /// </summary> - [TestMethod] + [TestCase] public void ValidMessageReceivedTest() { this.message.Nonce = "a"; Assert.IsNotNull(this.nonceElement.ProcessIncomingMessage(this.message)); @@ -65,7 +65,7 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { /// <summary> /// Verifies that a message that doesn't have a string of random characters is received correctly. /// </summary> - [TestMethod] + [TestCase] public void ValidMessageNoNonceReceivedTest() { this.message.Nonce = string.Empty; this.nonceElement.AllowZeroLengthNonce = true; @@ -75,7 +75,7 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { /// <summary> /// Verifies that a message that doesn't have a string of random characters is received correctly. /// </summary> - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void InvalidMessageNoNonceReceivedTest() { this.message.Nonce = string.Empty; this.nonceElement.AllowZeroLengthNonce = false; @@ -85,7 +85,7 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { /// <summary> /// Verifies that a replayed message is rejected. /// </summary> - [TestMethod, ExpectedException(typeof(ReplayedMessageException))] + [TestCase, ExpectedException(typeof(ReplayedMessageException))] public void ReplayDetectionTest() { this.message.Nonce = "a"; Assert.IsNotNull(this.nonceElement.ProcessIncomingMessage(this.message)); diff --git a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs index 7846411..d22c2f7 100644 --- a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs @@ -13,23 +13,23 @@ namespace DotNetOpenAuth.Test.Messaging { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ChannelTests : MessagingTestBase { - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, 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] + [TestCase] public void ReadFromRequestQueryString() { this.ParameterizedReceiveTest("GET"); } - [TestMethod] + [TestCase] public void ReadFromRequestForm() { this.ParameterizedReceiveTest("POST"); } @@ -38,37 +38,37 @@ namespace DotNetOpenAuth.Test.Messaging { /// Verifies compliance to OpenID 2.0 section 5.1.1 by verifying the channel /// will reject messages that come with an unexpected HTTP verb. /// </summary> - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void ReadFromRequestDisallowedHttpMethod() { var fields = GetStandardTestFields(FieldFill.CompleteBeforeBindings); fields["GetOnly"] = "true"; this.Channel.ReadFromRequest(CreateHttpRequestInfo("POST", fields)); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SendNull() { this.Channel.PrepareResponse(null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void SendIndirectedUndirectedMessage() { IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect); this.Channel.PrepareResponse(message); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void SendDirectedNoRecipientMessage() { IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect); this.Channel.PrepareResponse(message); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void SendInvalidMessageTransport() { IProtocolMessage message = new TestDirectedMessage((MessageTransport)100); this.Channel.PrepareResponse(message); } - [TestMethod] + [TestCase] public void SendIndirectMessage301Get() { TestDirectedMessage message = new TestDirectedMessage(MessageTransport.Indirect); GetStandardTestMessage(FieldFill.CompleteBeforeBindings, message); @@ -77,29 +77,29 @@ namespace DotNetOpenAuth.Test.Messaging { OutgoingWebResponse response = this.Channel.PrepareResponse(message); Assert.AreEqual(HttpStatusCode.Redirect, response.Status); - StringAssert.StartsWith(response.Headers[HttpResponseHeader.Location], "http://provider/path"); + StringAssert.StartsWith("http://provider/path", response.Headers[HttpResponseHeader.Location]); foreach (var pair in expected) { string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key); string value = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Value); string substring = string.Format("{0}={1}", key, value); - StringAssert.Contains(response.Headers[HttpResponseHeader.Location], substring); + StringAssert.Contains(substring, response.Headers[HttpResponseHeader.Location]); } } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SendIndirectMessage301GetNullMessage() { TestBadChannel badChannel = new TestBadChannel(false); badChannel.Create301RedirectResponse(null, new Dictionary<string, string>()); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, 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))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SendIndirectMessage301GetNullFields() { TestBadChannel badChannel = new TestBadChannel(false); var message = new TestDirectedMessage(MessageTransport.Indirect); @@ -107,7 +107,7 @@ namespace DotNetOpenAuth.Test.Messaging { badChannel.Create301RedirectResponse(message, null); } - [TestMethod] + [TestCase] 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 @@ -122,29 +122,29 @@ namespace DotNetOpenAuth.Test.Messaging { 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."); + StringAssert.Contains("<form ", body); + StringAssert.Contains("action=\"http://provider/path\"", body); + StringAssert.Contains("method=\"post\"", body); + StringAssert.Contains("<input type=\"hidden\" name=\"age\" value=\"15\" />", body); + StringAssert.Contains("<input type=\"hidden\" name=\"Location\" value=\"http://host/path\" />", body); + StringAssert.Contains("<input type=\"hidden\" name=\"Name\" value=\"" + HttpUtility.HtmlEncode(message.Name) + "\" />", body); + StringAssert.Contains(".submit()", body, "There should be some javascript to automate form submission."); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SendIndirectMessageFormPostNullMessage() { TestBadChannel badChannel = new TestBadChannel(false); badChannel.CreateFormPostResponse(null, new Dictionary<string, string>()); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, 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))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SendIndirectMessageFormPostNullFields() { TestBadChannel badChannel = new TestBadChannel(false); var message = new TestDirectedMessage(MessageTransport.Indirect); @@ -159,7 +159,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// 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")] + [TestCase, ExpectedException(typeof(NotImplementedException))] public void SendDirectMessageResponse() { IProtocolMessage message = new TestDirectedMessage { Age = 15, @@ -169,25 +169,25 @@ namespace DotNetOpenAuth.Test.Messaging { this.Channel.PrepareResponse(message); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SendIndirectMessageNull() { TestBadChannel badChannel = new TestBadChannel(false); badChannel.PrepareIndirectResponse(null); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void ReceiveNull() { TestBadChannel badChannel = new TestBadChannel(false); badChannel.Receive(null, null); } - [TestMethod] + [TestCase] public void ReceiveUnrecognizedMessage() { TestBadChannel badChannel = new TestBadChannel(false); Assert.IsNull(badChannel.Receive(new Dictionary<string, string>(), null)); } - [TestMethod] + [TestCase] public void ReadFromRequestWithContext() { var fields = GetStandardTestFields(FieldFill.AllRequired); TestMessage expectedMessage = GetStandardTestMessage(FieldFill.AllRequired); @@ -195,24 +195,24 @@ namespace DotNetOpenAuth.Test.Messaging { HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter())); IProtocolMessage message = this.Channel.ReadFromRequest(); Assert.IsNotNull(message); - Assert.IsInstanceOfType(message, typeof(TestMessage)); + Assert.IsInstanceOf<TestMessage>(message); Assert.AreEqual(expectedMessage.Age, ((TestMessage)message).Age); } - [TestMethod, ExpectedException(typeof(InvalidOperationException))] + [TestCase, ExpectedException(typeof(InvalidOperationException))] public void ReadFromRequestNoContext() { HttpContext.Current = null; TestBadChannel badChannel = new TestBadChannel(false); badChannel.ReadFromRequest(); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void ReadFromRequestNull() { TestBadChannel badChannel = new TestBadChannel(false); badChannel.ReadFromRequest(null); } - [TestMethod] + [TestCase] public void SendReplayProtectedMessageSetsNonce() { TestReplayProtectedMessage message = new TestReplayProtectedMessage(MessageTransport.Indirect); message.Recipient = new Uri("http://localtest"); @@ -222,33 +222,33 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.IsNotNull(((IReplayProtectedProtocolMessage)message).Nonce); } - [TestMethod, ExpectedException(typeof(InvalidSignatureException))] + [TestCase, ExpectedException(typeof(InvalidSignatureException))] public void ReceivedInvalidSignature() { this.Channel = CreateChannel(MessageProtections.TamperProtection); this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, true); } - [TestMethod] + [TestCase] public void ReceivedReplayProtectedMessageJustOnce() { this.Channel = CreateChannel(MessageProtections.ReplayProtection); this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false); } - [TestMethod, ExpectedException(typeof(ReplayedMessageException))] + [TestCase, 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))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void MessageExpirationWithoutTamperResistance() { new TestChannel( new TestMessageFactory(), new StandardExpirationBindingElement()); } - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void TooManyBindingElementsProvidingSameProtection() { Channel channel = new TestChannel( new TestMessageFactory(), @@ -258,7 +258,7 @@ namespace DotNetOpenAuth.Test.Messaging { accessor.ProcessOutgoingMessage(new TestSignedDirectedMessage()); } - [TestMethod] + [TestCase] public void BindingElementsOrdering() { IChannelBindingElement transformA = new MockTransformationBindingElement("a"); IChannelBindingElement transformB = new MockTransformationBindingElement("b"); @@ -282,20 +282,20 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.AreSame(sign, channel.BindingElements[4]); } - [TestMethod, ExpectedException(typeof(UnprotectedMessageException))] + [TestCase, ExpectedException(typeof(UnprotectedMessageException))] public void InsufficientlyProtectedMessageSent() { var message = new TestSignedDirectedMessage(MessageTransport.Direct); message.Recipient = new Uri("http://localtest"); this.Channel.PrepareResponse(message); } - [TestMethod, ExpectedException(typeof(UnprotectedMessageException))] + [TestCase, ExpectedException(typeof(UnprotectedMessageException))] public void InsufficientlyProtectedMessageReceived() { this.Channel = CreateChannel(MessageProtections.None, MessageProtections.TamperProtection); this.ParameterizedReceiveProtectedTest(DateTime.Now, false); } - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, 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 db136f5..506a6b2 100644 --- a/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs +++ b/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs @@ -11,7 +11,7 @@ namespace DotNetOpenAuth.Test.Messaging { using System.Diagnostics.Contracts; using System.Linq; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; internal class CollectionAssert<T> { internal static void AreEquivalent(ICollection<T> expected, ICollection<T> actual) { diff --git a/src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs b/src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs index 55f4394..1c9f5a8 100644 --- a/src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs @@ -11,12 +11,12 @@ namespace DotNetOpenAuth.Test.Messaging { using System.Collections.ObjectModel; using System.Linq; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; /// <summary> /// Tests for cached enumeration. /// </summary> - [TestClass] + [TestFixture] public class EnumerableCacheTests { /// <summary> /// The number of times the generator method's implementation is started. @@ -29,20 +29,15 @@ namespace DotNetOpenAuth.Test.Messaging { private int generatorCompleted; /// <summary> - /// Gets or sets the test context. - /// </summary> - public TestContext TestContext { get; set; } - - /// <summary> /// Sets up a test. /// </summary> - [TestInitialize] + [SetUp] public void Setup() { this.generatorInvocations = 0; this.generatorCompleted = 0; } - [TestMethod] + [TestCase] public void EnumerableCache() { // Baseline var generator = this.NumberGenerator(); @@ -63,7 +58,7 @@ namespace DotNetOpenAuth.Test.Messaging { CollectionAssert.AreEqual(list1, list4); } - [TestMethod] + [TestCase] public void GeneratesOnlyRequiredElements() { var generator = this.NumberGenerator().CacheGeneratedResults(); Assert.AreEqual(0, this.generatorInvocations); @@ -72,28 +67,28 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.AreEqual(0, this.generatorCompleted, "Only taking part of the list should not have completed the generator."); } - [TestMethod] + [TestCase] public void PassThruDoubleCache() { var cache1 = this.NumberGenerator().CacheGeneratedResults(); var cache2 = cache1.CacheGeneratedResults(); Assert.AreSame(cache1, cache2, "Two caches were set up rather than just sharing the first one."); } - [TestMethod] + [TestCase] public void PassThruList() { var list = this.NumberGenerator().ToList(); var cache = list.CacheGeneratedResults(); Assert.AreSame(list, cache); } - [TestMethod] + [TestCase] public void PassThruArray() { var array = this.NumberGenerator().ToArray(); var cache = array.CacheGeneratedResults(); Assert.AreSame(array, cache); } - [TestMethod] + [TestCase] public void PassThruCollection() { var collection = new Collection<int>(); var cache = collection.CacheGeneratedResults(); @@ -103,7 +98,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Tests calling IEnumerator.Current before first call to MoveNext. /// </summary> - [TestMethod, ExpectedException(typeof(InvalidOperationException))] + [TestCase, ExpectedException(typeof(InvalidOperationException))] public void EnumerableCacheCurrentThrowsBefore() { var foo = this.NumberGenerator().CacheGeneratedResults().GetEnumerator().Current; } @@ -111,7 +106,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Tests calling IEnumerator.Current after MoveNext returns false. /// </summary> - [TestMethod, ExpectedException(typeof(InvalidOperationException))] + [TestCase, ExpectedException(typeof(InvalidOperationException))] public void EnumerableCacheCurrentThrowsAfter() { var enumerator = this.NumberGenerator().CacheGeneratedResults().GetEnumerator(); while (enumerator.MoveNext()) { diff --git a/src/DotNetOpenAuth.Test/Messaging/ErrorUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/ErrorUtilitiesTests.cs index 36b6ae7..4408708 100644 --- a/src/DotNetOpenAuth.Test/Messaging/ErrorUtilitiesTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/ErrorUtilitiesTests.cs @@ -7,31 +7,31 @@ namespace DotNetOpenAuth.Test.Messaging { using System; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ErrorUtilitiesTests { - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void VerifyArgumentNotNullThrows() { ErrorUtilities.VerifyArgumentNotNull(null, "someArg"); } - [TestMethod] + [TestCase] public void VerifyArgumentNotNullDoesNotThrow() { ErrorUtilities.VerifyArgumentNotNull("hi", "someArg"); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void VerifyNonZeroLengthOnNull() { ErrorUtilities.VerifyNonZeroLength(null, "someArg"); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void VerifyNonZeroLengthOnEmpty() { ErrorUtilities.VerifyNonZeroLength(string.Empty, "someArg"); } - [TestMethod] + [TestCase] public void VerifyNonZeroLengthOnNonEmpty() { ErrorUtilities.VerifyNonZeroLength("some Value", "someArg"); } diff --git a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs index fd77746..0085d59 100644 --- a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs @@ -9,17 +9,17 @@ namespace DotNetOpenAuth.Test.Messaging { using System.Collections.Specialized; using System.Web; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class HttpRequestInfoTests : TestBase { - [TestMethod] + [TestCase] public void CtorDefault() { HttpRequestInfo info = new HttpRequestInfo(); Assert.AreEqual("GET", info.HttpMethod); } - [TestMethod] + [TestCase] public void CtorRequest() { HttpRequest request = new HttpRequest("file", "http://someserver?a=b", "a=b"); ////request.Headers["headername"] = "headervalue"; // PlatformNotSupportedException prevents us mocking this up @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.Messaging { } // All these tests are ineffective because ServerVariables[] cannot be set. - ////[TestMethod] + ////[TestCase] ////public void CtorRequestWithDifferentPublicHttpHost() { //// HttpRequest request = new HttpRequest("file", "http://someserver?a=b", "a=b"); //// request.ServerVariables["HTTP_HOST"] = "publichost"; @@ -44,7 +44,7 @@ namespace DotNetOpenAuth.Test.Messaging { //// Assert.AreEqual(request.QueryString["a"], info.QueryString["a"]); ////} - ////[TestMethod] + ////[TestCase] ////public void CtorRequestWithDifferentPublicHttpsHost() { //// HttpRequest request = new HttpRequest("file", "https://someserver?a=b", "a=b"); //// request.ServerVariables["HTTP_HOST"] = "publichost"; @@ -55,7 +55,7 @@ namespace DotNetOpenAuth.Test.Messaging { //// Assert.AreEqual(request.QueryString["a"], info.QueryString["a"]); ////} - ////[TestMethod] + ////[TestCase] ////public void CtorRequestWithDifferentPublicHostNonstandardPort() { //// HttpRequest request = new HttpRequest("file", "http://someserver?a=b", "a=b"); //// request.ServerVariables["HTTP_HOST"] = "publichost:550"; @@ -66,7 +66,7 @@ namespace DotNetOpenAuth.Test.Messaging { //// Assert.AreEqual(request.QueryString["a"], info.QueryString["a"]); ////} - ////[TestMethod] + ////[TestCase] ////public void CtorRequestWithDifferentPublicIPv6Host() { //// HttpRequest request = new HttpRequest("file", "http://[fe80::587e:c6e5:d3aa:657a]:8089/v3.1/", ""); //// request.ServerVariables["HTTP_HOST"] = "[fe80::587e:c6e5:d3aa:657b]:8089"; @@ -80,7 +80,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// Checks that a property dependent on another null property /// doesn't generate a NullReferenceException. /// </summary> - [TestMethod] + [TestCase] public void QueryBeforeSettingUrl() { HttpRequestInfo info = new HttpRequestInfo(); Assert.IsNull(info.Query); @@ -89,7 +89,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies that looking up a querystring variable is gracefully handled without a query in the URL. /// </summary> - [TestMethod] + [TestCase] public void QueryStringLookupWithoutQuery() { HttpRequestInfo info = new HttpRequestInfo(); Assert.IsNull(info.QueryString["hi"]); @@ -98,7 +98,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies SSL forwarders are correctly handled when they supply X_FORWARDED_PROTO and HOST /// </summary> - [TestMethod] + [TestCase] public void GetPublicFacingUrlSSLForwarder1() { HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); var serverVariables = new NameValueCollection(); @@ -112,7 +112,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies SSL forwarders are correctly handled when they supply X_FORWARDED_PROTO and HOST:port /// </summary> - [TestMethod] + [TestCase] public void GetPublicFacingUrlSSLForwarder2() { HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); var serverVariables = new NameValueCollection(); @@ -126,7 +126,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies SSL forwarders are correctly handled when they supply just HOST /// </summary> - [TestMethod] + [TestCase] public void GetPublicFacingUrlSSLForwarder3() { HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); var serverVariables = new NameValueCollection(); @@ -139,7 +139,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies SSL forwarders are correctly handled when they supply just HOST:port /// </summary> - [TestMethod] + [TestCase] public void GetPublicFacingUrlSSLForwarder4() { HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); var serverVariables = new NameValueCollection(); diff --git a/src/DotNetOpenAuth.Test/Messaging/MessageSerializerTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessageSerializerTests.cs index 481a715..91cccf1 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessageSerializerTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessageSerializerTests.cs @@ -9,30 +9,30 @@ namespace DotNetOpenAuth.Test.Messaging { using System.Collections.Generic; using System.Xml; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; /// <summary> /// Tests for the <see cref="MessageSerializer"/> class. /// </summary> - [TestClass()] + [TestFixture()] public class MessageSerializerTests : MessagingTestBase { - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SerializeNull() { var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage)); serializer.Serialize(null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void GetInvalidMessageType() { MessageSerializer.Get(typeof(string)); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void GetNullType() { MessageSerializer.Get(null); } - [TestMethod()] + [TestCase()] public void SerializeTest() { var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage)); var message = GetStandardTestMessage(FieldFill.CompleteBeforeBindings); @@ -52,13 +52,13 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.IsFalse(actual.ContainsKey("EmptyMember")); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void DeserializeNull() { var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage)); serializer.Deserialize(null, null); } - [TestMethod] + [TestCase] public void DeserializeSimple() { var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage)); Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal); @@ -81,7 +81,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// The element sorting rules are first inheritance order, then alphabetical order. /// This test validates correct behavior on both. /// </remarks> - [TestMethod] + [TestCase] public void DeserializeVerifyElementOrdering() { var serializer = MessageSerializer.Get(typeof(Mocks.TestDerivedMessage)); Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal); @@ -105,7 +105,7 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.AreEqual("privateValue", actual.PrivatePropertyAccessor); } - [TestMethod] + [TestCase] public void DeserializeWithExtraFields() { var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage)); Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal); @@ -122,7 +122,7 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.IsNull(actual.EmptyMember); } - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void DeserializeInvalidMessage() { IProtocolMessage message = new Mocks.TestDirectedMessage(); var serializer = MessageSerializer.Get(message.GetType()); diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs index accb182..72f3359 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs @@ -13,7 +13,7 @@ namespace DotNetOpenAuth.Test { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; /// <summary> /// The base class that all messaging test classes inherit from. @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.Test { internal Channel Channel { get; set; } - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -156,7 +156,7 @@ namespace DotNetOpenAuth.Test { IDirectedProtocolMessage requestMessage = this.Channel.ReadFromRequest(CreateHttpRequestInfo(method, fields)); Assert.IsNotNull(requestMessage); - Assert.IsInstanceOfType(requestMessage, typeof(TestMessage)); + Assert.IsInstanceOf<TestMessage>(requestMessage); TestMessage actualMessage = (TestMessage)requestMessage; Assert.AreEqual(expectedMessage.Age, actualMessage.Age); Assert.AreEqual(expectedMessage.Name, actualMessage.Name); @@ -174,7 +174,7 @@ namespace DotNetOpenAuth.Test { } IProtocolMessage requestMessage = this.Channel.ReadFromRequest(CreateHttpRequestInfo("GET", fields)); Assert.IsNotNull(requestMessage); - Assert.IsInstanceOfType(requestMessage, typeof(TestSignedDirectedMessage)); + Assert.IsInstanceOf<TestSignedDirectedMessage>(requestMessage); TestSignedDirectedMessage actualMessage = (TestSignedDirectedMessage)requestMessage; Assert.AreEqual(expectedMessage.Age, actualMessage.Age); Assert.AreEqual(expectedMessage.Name, actualMessage.Name); diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs index 4fc89a7..2b0e8f9 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs @@ -15,11 +15,11 @@ namespace DotNetOpenAuth.Test.Messaging using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class MessagingUtilitiesTests : TestBase { - [TestMethod] + [TestCase] public void CreateQueryString() { var args = new Dictionary<string, string>(); args.Add("a", "b"); @@ -27,17 +27,17 @@ namespace DotNetOpenAuth.Test.Messaging Assert.AreEqual("a=b&c%2Fd=e%2Ff", MessagingUtilities.CreateQueryString(args)); } - [TestMethod] + [TestCase] public void CreateQueryStringEmptyCollection() { Assert.AreEqual(0, MessagingUtilities.CreateQueryString(new Dictionary<string, string>()).Length); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CreateQueryStringNullDictionary() { MessagingUtilities.CreateQueryString(null); } - [TestMethod] + [TestCase] public void AppendQueryArgs() { UriBuilder uri = new UriBuilder("http://baseline.org/page"); var args = new Dictionary<string, string>(); @@ -51,17 +51,17 @@ namespace DotNetOpenAuth.Test.Messaging Assert.AreEqual("http://baseline.org/page?a=b&c%2Fd=e%2Ff&g=h", uri.Uri.AbsoluteUri); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void AppendQueryArgsNullUriBuilder() { MessagingUtilities.AppendQueryArgs(null, new Dictionary<string, string>()); } - [TestMethod] + [TestCase] public void AppendQueryArgsNullDictionary() { MessagingUtilities.AppendQueryArgs(new UriBuilder(), null); } - [TestMethod] + [TestCase] public void ToDictionary() { NameValueCollection nvc = new NameValueCollection(); nvc["a"] = "b"; @@ -73,7 +73,7 @@ namespace DotNetOpenAuth.Test.Messaging Assert.AreEqual(nvc["c"], actual["c"]); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void ToDictionaryWithNullKey() { NameValueCollection nvc = new NameValueCollection(); nvc[null] = "a"; @@ -81,7 +81,7 @@ namespace DotNetOpenAuth.Test.Messaging nvc.ToDictionary(true); } - [TestMethod] + [TestCase] public void ToDictionaryWithSkippedNullKey() { NameValueCollection nvc = new NameValueCollection(); nvc[null] = "a"; @@ -91,27 +91,27 @@ namespace DotNetOpenAuth.Test.Messaging Assert.AreEqual(nvc["b"], dictionary["b"]); } - [TestMethod] + [TestCase] public void ToDictionaryNull() { Assert.IsNull(MessagingUtilities.ToDictionary(null)); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void ApplyHeadersToResponseNullAspNetResponse() { MessagingUtilities.ApplyHeadersToResponse(new WebHeaderCollection(), (HttpResponse)null); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void ApplyHeadersToResponseNullListenerResponse() { MessagingUtilities.ApplyHeadersToResponse(new WebHeaderCollection(), (HttpListenerResponse)null); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void ApplyHeadersToResponseNullHeaders() { MessagingUtilities.ApplyHeadersToResponse(null, new HttpResponse(new StringWriter())); } - [TestMethod] + [TestCase] public void ApplyHeadersToResponse() { var headers = new WebHeaderCollection(); headers[HttpResponseHeader.ContentType] = "application/binary"; @@ -128,7 +128,7 @@ namespace DotNetOpenAuth.Test.Messaging /// <remarks> /// The tests in this method come from http://wiki.oauth.net/TestCases /// </remarks> - [TestMethod] + [TestCase] public void EscapeUriDataStringRfc3986Tests() { Assert.AreEqual("abcABC123", MessagingUtilities.EscapeUriDataStringRfc3986("abcABC123")); Assert.AreEqual("-._~", MessagingUtilities.EscapeUriDataStringRfc3986("-._~")); @@ -145,7 +145,7 @@ namespace DotNetOpenAuth.Test.Messaging /// <summary> /// Verifies the overall format of the multipart POST is correct. /// </summary> - [TestMethod] + [TestCase] public void PostMultipart() { var httpHandler = new TestWebRequestHandler(); bool callbackTriggered = false; @@ -174,7 +174,7 @@ namespace DotNetOpenAuth.Test.Messaging /// <summary> /// Verifies proper behavior of GetHttpVerb /// </summary> - [TestMethod] + [TestCase] public void GetHttpVerbTest() { Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest)); Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest)); @@ -192,7 +192,7 @@ namespace DotNetOpenAuth.Test.Messaging /// <summary> /// Verifies proper behavior of GetHttpVerb on invalid input. /// </summary> - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void GetHttpVerbOutOfRangeTest() { MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.PostRequest); } @@ -200,7 +200,7 @@ namespace DotNetOpenAuth.Test.Messaging /// <summary> /// Verifies proper behavior of GetHttpDeliveryMethod /// </summary> - [TestMethod] + [TestCase] public void GetHttpDeliveryMethodTest() { Assert.AreEqual(HttpDeliveryMethods.GetRequest, MessagingUtilities.GetHttpDeliveryMethod("GET")); Assert.AreEqual(HttpDeliveryMethods.PostRequest, MessagingUtilities.GetHttpDeliveryMethod("POST")); @@ -212,7 +212,7 @@ namespace DotNetOpenAuth.Test.Messaging /// <summary> /// Verifies proper behavior of GetHttpDeliveryMethod for an unexpected input /// </summary> - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void GetHttpDeliveryMethodOutOfRangeTest() { MessagingUtilities.GetHttpDeliveryMethod("UNRECOGNIZED"); } diff --git a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs index 57614ba..08524b2 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs @@ -11,14 +11,14 @@ namespace DotNetOpenAuth.Test.Messaging { using System.IO; using System.Net; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class MultipartPostPartTests : TestBase { /// <summary> /// Verifies that the Length property matches the length actually serialized. /// </summary> - [TestMethod] + [TestCase] public void FormDataSerializeMatchesLength() { var part = MultipartPostPart.CreateFormPart("a", "b"); VerifyLength(part); @@ -27,7 +27,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies that the length property matches the length actually serialized. /// </summary> - [TestMethod] + [TestCase] public void FileSerializeMatchesLength() { using (TempFileCollection tfc = new TempFileCollection()) { string file = tfc.AddExtension(".txt"); @@ -40,7 +40,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies file multiparts identify themselves as files and not merely form-data. /// </summary> - [TestMethod] + [TestCase] public void FilePartAsFile() { var part = MultipartPostPart.CreateFormFilePart("somename", "somefile", "plain/text", new MemoryStream()); Assert.AreEqual("file", part.ContentDisposition); @@ -49,7 +49,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies MultiPartPost sends the right number of bytes. /// </summary> - [TestMethod] + [TestCase] public void MultiPartPostAscii() { using (TempFileCollection tfc = new TempFileCollection()) { string file = tfc.AddExtension("txt"); @@ -64,7 +64,7 @@ namespace DotNetOpenAuth.Test.Messaging { /// <summary> /// Verifies MultiPartPost sends the right number of bytes. /// </summary> - [TestMethod] + [TestCase] public void MultiPartPostMultiByteCharacters() { using (TempFileCollection tfc = new TempFileCollection()) { string file = tfc.AddExtension("txt"); @@ -94,9 +94,9 @@ namespace DotNetOpenAuth.Test.Messaging { bool posted = false; handler.Callback = req => { foreach (string header in req.Headers) { - TestContext.WriteLine("{0}: {1}", header, req.Headers[header]); + TestUtilities.TestLogger.InfoFormat("{0}: {1}", header, req.Headers[header]); } - TestContext.WriteLine(handler.RequestEntityAsString); + TestUtilities.TestLogger.InfoFormat(handler.RequestEntityAsString); Assert.AreEqual(req.ContentLength, handler.RequestEntityStream.Length); posted = true; return null; diff --git a/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs b/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs index 2923af4..10045de 100644 --- a/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs @@ -9,14 +9,14 @@ namespace DotNetOpenAuth.Test.Messaging { using System.Net.Mime; using System.Text; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class OutgoingWebResponseTests { /// <summary> /// Verifies that setting the Body property correctly converts to a byte stream. /// </summary> - [TestMethod] + [TestCase] public void SetBodyToByteStream() { var response = new OutgoingWebResponse(); string stringValue = "abc"; diff --git a/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs b/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs index 430b929..c9e3d24 100644 --- a/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs @@ -7,22 +7,22 @@ namespace DotNetOpenAuth.Test.Messaging { using System; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ProtocolExceptionTests : TestBase { - [TestMethod] + [TestCase] public void CtorDefault() { ProtocolException ex = new ProtocolException(); } - [TestMethod] + [TestCase] public void CtorWithTextMessage() { ProtocolException ex = new ProtocolException("message"); Assert.AreEqual("message", ex.Message); } - [TestMethod] + [TestCase] public void CtorWithTextMessageAndInnerException() { Exception innerException = new Exception(); ProtocolException ex = new ProtocolException("message", innerException); @@ -30,14 +30,14 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.AreSame(innerException, ex.InnerException); } - [TestMethod] + [TestCase] public void CtorWithProtocolMessage() { IProtocolMessage message = new Mocks.TestDirectedMessage(); ProtocolException ex = new ProtocolException("message", message); Assert.AreSame(message, ex.FaultedMessage); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorWithNullProtocolMessage() { new ProtocolException("message", (IProtocolMessage)null); } diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDescriptionTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDescriptionTests.cs index 76c454a..e57df65 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDescriptionTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDescriptionTests.cs @@ -8,26 +8,26 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class MessageDescriptionTests : MessagingTestBase { - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullType() { new MessageDescription(null, new Version(1, 0)); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullVersion() { new MessageDescription(typeof(Mocks.TestMessage), null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorNonMessageType() { new MessageDescription(typeof(string), new Version(1, 0)); } - [TestMethod] + [TestCase] public void MultiVersionedMessageTest() { var v10 = new MessageDescription(typeof(MultiVersionMessage), new Version(1, 0)); var v20 = new MessageDescription(typeof(MultiVersionMessage), new Version(2, 0)); diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs index b9e7436..9e3f4c5 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs @@ -12,20 +12,20 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { using System.Xml; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class MessageDictionaryTests : MessagingTestBase { private Mocks.TestMessage message; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); this.message = new Mocks.TestDirectedMessage(); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNull() { this.MessageDescriptions.GetAccessor(null); } @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IDictionary<System.String,System.String>.Values /// </summary> - [TestMethod] + [TestCase] public void Values() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); Collection<string> expected = new Collection<string> { @@ -59,7 +59,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IDictionary<System.String,System.String>.Keys /// </summary> - [TestMethod] + [TestCase] public void Keys() { // We expect that non-nullable value type fields will automatically have keys // in the dictionary for them. @@ -80,7 +80,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IDictionary<System.String,System.String>.Item /// </summary> - [TestMethod] + [TestCase] public void Item() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); @@ -103,7 +103,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.String<<.IsReadOnly /// </summary> - [TestMethod] + [TestCase] public void IsReadOnly() { ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); Assert.IsFalse(target.IsReadOnly); @@ -112,7 +112,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.String<<.Count /// </summary> - [TestMethod] + [TestCase] public void Count() { ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); IDictionary<string, string> targetDictionary = (IDictionary<string, string>)target; @@ -124,7 +124,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.String,System.String<<.GetEnumerator /// </summary> - [TestMethod] + [TestCase] public void GetEnumerator() { IEnumerable<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); IDictionary<string, string> targetDictionary = (IDictionary<string, string>)target; @@ -147,7 +147,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { Assert.IsTrue(keysLast == valuesLast && keysLast == actualLast); } - [TestMethod] + [TestCase] public void GetEnumeratorUntyped() { IEnumerable target = this.MessageDescriptions.GetAccessor(this.message); IDictionary<string, string> targetDictionary = (IDictionary<string, string>)target; @@ -174,7 +174,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IDictionary<System.String,System.String>.TryGetValue /// </summary> - [TestMethod] + [TestCase] public void TryGetValue() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); this.message.Name = "andrew"; @@ -194,7 +194,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IDictionary<System.String,System.String>.Remove /// </summary> - [TestMethod] + [TestCase] public void RemoveTest1() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); this.message.Name = "andrew"; @@ -211,7 +211,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IDictionary<System.String,System.String>.ContainsKey /// </summary> - [TestMethod] + [TestCase] public void ContainsKey() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); Assert.IsTrue(target.ContainsKey("age"), "Value type declared element should have a key."); @@ -225,7 +225,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.IDictionary<System.String,System.String>.Add /// </summary> - [TestMethod] + [TestCase] public void AddByKeyAndValue() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); target.Add("extra", "value"); @@ -234,7 +234,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { Assert.AreEqual("Andrew", this.message.Name); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void AddNullValue() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); target.Add("extra", null); @@ -243,35 +243,35 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.String<<.Add /// </summary> - [TestMethod] + [TestCase] public void AddByKeyValuePair() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); target.Add(new KeyValuePair<string, string>("extra", "value")); Assert.IsTrue(target.Contains(new KeyValuePair<string, string>("extra", "value"))); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void AddExtraFieldThatAlreadyExists() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); target.Add("extra", "value"); target.Add("extra", "value"); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void AddDeclaredValueThatAlreadyExists() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); target.Add("Name", "andrew"); target.Add("Name", "andrew"); } - [TestMethod] + [TestCase] public void DefaultReferenceTypeDeclaredPropertyHasNoKey() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); Assert.IsFalse(target.ContainsKey("Name"), "A null value should result in no key."); Assert.IsFalse(target.Keys.Contains("Name"), "A null value should result in no key."); } - [TestMethod] + [TestCase] public void RemoveStructDeclaredProperty() { IDictionary<string, string> target = this.MessageDescriptions.GetAccessor(this.message); this.message.Age = 5; @@ -284,7 +284,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.String<<.Remove /// </summary> - [TestMethod] + [TestCase] public void RemoveByKeyValuePair() { ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); this.message.Name = "Andrew"; @@ -297,7 +297,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.String<<.CopyTo /// </summary> - [TestMethod] + [TestCase] public void CopyTo() { ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target; @@ -314,7 +314,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.String<<.Contains /// </summary> - [TestMethod] + [TestCase] public void ContainsKeyValuePair() { ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target; @@ -330,7 +330,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// A test for System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.String<<.Clear /// </summary> - [TestMethod] + [TestCase] public void ClearValues() { MessageDictionary target = this.MessageDescriptions.GetAccessor(this.message); IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target; @@ -347,7 +347,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { /// <summary> /// Verifies that the Clear method throws the expected exception. /// </summary> - [TestMethod, ExpectedException(typeof(NotSupportedException))] + [TestCase, ExpectedException(typeof(NotSupportedException))] public void Clear() { MessageDictionary target = this.MessageDescriptions.GetAccessor(this.message); target.Clear(); diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs index 19e6a82..66237e7 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs @@ -12,42 +12,47 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class MessagePartTests : MessagingTestBase { - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void OptionalNonNullableStruct() { this.ParameterizedMessageTypeTest(typeof(MessageWithNonNullableOptionalStruct)); } - [TestMethod] + [TestCase] public void RequiredNonNullableStruct() { this.ParameterizedMessageTypeTest(typeof(MessageWithNonNullableRequiredStruct)); } - [TestMethod] + [TestCase] public void OptionalNullableStruct() { - this.ParameterizedMessageTypeTest(typeof(MessageWithNullableOptionalStruct)); + var message = new MessageWithNullableOptionalStruct(); + var part = this.ParameterizedMessageTypeTest(message.GetType()); + + Assert.IsNull(part.GetValue(message)); + part.SetValue(message, "3"); + Assert.AreEqual("3", part.GetValue(message)); } - [TestMethod] + [TestCase] public void RequiredNullableStruct() { this.ParameterizedMessageTypeTest(typeof(MessageWithNullableRequiredStruct)); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullMember() { new MessagePart(null, new MessagePartAttribute()); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullAttribute() { PropertyInfo field = typeof(MessageWithNullableOptionalStruct).GetProperty("OptionalInt", BindingFlags.NonPublic | BindingFlags.Instance); new MessagePart(field, null); } - [TestMethod] + [TestCase] public void SetValue() { var message = new MessageWithNonNullableRequiredStruct(); MessagePart part = this.ParameterizedMessageTypeTest(message.GetType()); @@ -55,7 +60,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { Assert.AreEqual(5, message.OptionalInt); } - [TestMethod] + [TestCase] public void GetValue() { var message = new MessageWithNonNullableRequiredStruct(); message.OptionalInt = 8; @@ -63,7 +68,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { Assert.AreEqual("8", part.GetValue(message)); } - [TestMethod] + [TestCase] public void Base64Member() { var message = new MessageWithBase64EncodedString(); message.LastName = "andrew"; @@ -73,7 +78,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { Assert.AreEqual("arnott", message.LastName); } - [TestMethod] + [TestCase] public void ConstantFieldMemberValidValues() { var message = new MessageWithConstantField(); MessagePart part = GetMessagePart(message.GetType(), "ConstantField"); @@ -82,20 +87,20 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { Assert.AreEqual("abc", part.GetValue(message)); } - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void ConstantFieldMemberInvalidValues() { var message = new MessageWithConstantField(); MessagePart part = GetMessagePart(message.GetType(), "ConstantField"); part.SetValue(message, "def"); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void NonFieldOrPropertyMember() { MemberInfo method = typeof(MessageWithNullableOptionalStruct).GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance); new MessagePart(method, new MessagePartAttribute()); } - [TestMethod] + [TestCase] public void RequiredMinAndMaxVersions() { Type messageType = typeof(MessageWithMinAndMaxVersionParts); FieldInfo newIn2Field = messageType.GetField("NewIn2", BindingFlags.Public | BindingFlags.Instance); diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs index c4a79b5..60c8bc3 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs @@ -7,18 +7,18 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { using System; using DotNetOpenAuth.Messaging.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ValueMappingTests { - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullToString() { - new ValueMapping(null, str => new object()); + new ValueMapping(null, null, str => new object()); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullToObject() { - new ValueMapping(obj => obj.ToString(), null); + new ValueMapping(obj => obj.ToString(), null, null); } } } diff --git a/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs b/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs index 89d165a..63be45a 100644 --- a/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs @@ -9,17 +9,17 @@ namespace DotNetOpenAuth.Test.Messaging { using System.IO; using System.Web; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ResponseTests : TestBase { - [TestMethod, ExpectedException(typeof(InvalidOperationException))] + [TestCase, ExpectedException(typeof(InvalidOperationException))] public void SendWithoutAspNetContext() { HttpContext.Current = null; new OutgoingWebResponse().Send(); } - [TestMethod] + [TestCase] public void Send() { StringWriter writer = new StringWriter(); HttpRequest httpRequest = new HttpRequest("file", "http://server", string.Empty); diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs index 16386de..0bb35bc 100644 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs +++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs @@ -14,7 +14,7 @@ namespace DotNetOpenAuth.Test.Mocks { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.Test.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; internal class CoordinatingChannel : Channel { /// <summary> diff --git a/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs b/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs index 0213a33..c18ea33 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs @@ -83,7 +83,7 @@ namespace DotNetOpenAuth.Test.Mocks { } } - internal void RegisterMockXrdsResponse(ServiceEndpoint endpoint) { + internal void RegisterMockXrdsResponse(IdentifierDiscoveryResult endpoint) { Contract.Requires<ArgumentNullException>(endpoint != null); string identityUri; @@ -92,10 +92,10 @@ namespace DotNetOpenAuth.Test.Mocks { } else { identityUri = endpoint.UserSuppliedIdentifier ?? endpoint.ClaimedIdentifier; } - this.RegisterMockXrdsResponse(new Uri(identityUri), new ServiceEndpoint[] { endpoint }); + this.RegisterMockXrdsResponse(new Uri(identityUri), new IdentifierDiscoveryResult[] { endpoint }); } - internal void RegisterMockXrdsResponse(Uri respondingUri, IEnumerable<ServiceEndpoint> endpoints) { + internal void RegisterMockXrdsResponse(Uri respondingUri, IEnumerable<IdentifierDiscoveryResult> endpoints) { Contract.Requires<ArgumentNullException>(endpoints != null); StringBuilder xrds = new StringBuilder(); @@ -130,12 +130,12 @@ namespace DotNetOpenAuth.Test.Mocks { this.RegisterMockResponse(respondingUri, ContentTypes.Xrds, xrds.ToString()); } - internal void RegisterMockXrdsResponse(UriIdentifier directedIdentityAssignedIdentifier, ServiceEndpoint providerEndpoint) { - ServiceEndpoint identityEndpoint = ServiceEndpoint.CreateForClaimedIdentifier( + internal void RegisterMockXrdsResponse(UriIdentifier directedIdentityAssignedIdentifier, IdentifierDiscoveryResult providerEndpoint) { + IdentifierDiscoveryResult identityEndpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier( directedIdentityAssignedIdentifier, directedIdentityAssignedIdentifier, - providerEndpoint.ProviderEndpoint, - providerEndpoint.ProviderDescription, + providerEndpoint.ProviderLocalIdentifier, + new ProviderEndpointDescription(providerEndpoint.ProviderEndpoint, providerEndpoint.Capabilities), 10, 10); this.RegisterMockXrdsResponse(identityEndpoint); diff --git a/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs b/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs index 346dde9..9f032b8 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs @@ -18,13 +18,13 @@ namespace DotNetOpenAuth.Test.Mocks { /// having a dependency on a hosted web site to actually perform discovery on. /// </summary> internal class MockIdentifier : Identifier { - private IEnumerable<ServiceEndpoint> endpoints; + private IEnumerable<IdentifierDiscoveryResult> endpoints; private MockHttpRequest mockHttpRequest; private Identifier wrappedIdentifier; - public MockIdentifier(Identifier wrappedIdentifier, MockHttpRequest mockHttpRequest, IEnumerable<ServiceEndpoint> endpoints) + public MockIdentifier(Identifier wrappedIdentifier, MockHttpRequest mockHttpRequest, IEnumerable<IdentifierDiscoveryResult> endpoints) : base(wrappedIdentifier.OriginalString, false) { Contract.Requires<ArgumentNullException>(wrappedIdentifier != null); Contract.Requires<ArgumentNullException>(mockHttpRequest != null); @@ -39,6 +39,10 @@ namespace DotNetOpenAuth.Test.Mocks { mockHttpRequest.RegisterMockXrdsResponse(new Uri(wrappedIdentifier.ToString()), endpoints); } + internal IEnumerable<IdentifierDiscoveryResult> DiscoveryEndpoints { + get { return this.endpoints; } + } + public override string ToString() { return this.wrappedIdentifier.ToString(); } @@ -51,10 +55,6 @@ namespace DotNetOpenAuth.Test.Mocks { return this.wrappedIdentifier.GetHashCode(); } - internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { - return this.endpoints; - } - internal override Identifier TrimFragment() { return this; } diff --git a/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs b/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs new file mode 100644 index 0000000..d74258d --- /dev/null +++ b/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------- +// <copyright file="MockIdentifierDiscoveryService.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 System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.RelyingParty; + + internal class MockIdentifierDiscoveryService : IIdentifierDiscoveryService { + /// <summary> + /// Initializes a new instance of the <see cref="MockIdentifierDiscoveryService"/> class. + /// </summary> + public MockIdentifierDiscoveryService() { + } + + #region IIdentifierDiscoveryService Members + + /// <summary> + /// Performs discovery on the specified identifier. + /// </summary> + /// <param name="identifier">The identifier to perform discovery on.</param> + /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> + /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> + /// <returns> + /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. + /// </returns> + public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { + var mockIdentifier = identifier as MockIdentifier; + if (mockIdentifier == null) { + abortDiscoveryChain = false; + return Enumerable.Empty<IdentifierDiscoveryResult>(); + } + + abortDiscoveryChain = true; + return mockIdentifier.DiscoveryEndpoints; + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs b/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs index 75281e2..96bd40d 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs @@ -7,7 +7,7 @@ namespace DotNetOpenAuth.Test.Mocks { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; internal class MockReplayProtectionBindingElement : IChannelBindingElement { private bool messageReceived; diff --git a/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs b/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs index e88fe52..1db9c68 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs @@ -10,7 +10,7 @@ namespace DotNetOpenAuth.Test.Mocks { using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; internal class MockTransformationBindingElement : IChannelBindingElement { private string transform; @@ -47,7 +47,7 @@ namespace DotNetOpenAuth.Test.Mocks { MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) { var testMessage = message as TestMessage; if (testMessage != null) { - StringAssert.StartsWith(testMessage.Name, this.transform); + StringAssert.StartsWith(this.transform, testMessage.Name); testMessage.Name = testMessage.Name.Substring(this.transform.Length); return MessageProtections.None; } diff --git a/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs b/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs index c009a83..5584a7b 100644 --- a/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs +++ b/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs @@ -12,11 +12,11 @@ namespace DotNetOpenAuth.Test.OAuth { using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AppendixScenarios : TestBase { - [TestMethod] + [TestCase] public void SpecAppendixAExample() { ServiceProviderDescription serviceDescription = new ServiceProviderDescription() { RequestTokenEndpoint = new MessageReceivingEndpoint("https://photos.example.net/request_token", HttpDeliveryMethods.PostRequest), diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs index fcdb5e8..a11c67d 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs @@ -8,11 +8,11 @@ namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class HmacSha1SigningBindingElementTests : MessagingTestBase { - [TestMethod] + [TestCase] public void SignatureTest() { UnauthorizedTokenRequest message = SigningBindingElementBaseTests.CreateTestRequestTokenMessage(this.MessageDescriptions, null); diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index b721fb7..c7b9f89 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -19,9 +19,9 @@ namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class OAuthChannelTests : TestBase { private OAuthChannel channel; private OAuthChannel_Accessor accessor; @@ -29,7 +29,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { private SigningBindingElementBase signingElement; private INonceStore nonceStore; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -41,32 +41,32 @@ namespace DotNetOpenAuth.Test.ChannelElements { this.channel.WebRequestHandler = this.webRequestHandler; } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullSigner() { new OAuthChannel(null, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory()); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullStore() { new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), null, new InMemoryTokenManager(), new TestMessageFactory()); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullTokenManager() { new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, null, new TestMessageFactory()); } - [TestMethod] + [TestCase] public void CtorSimpleConsumer() { new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager()); } - [TestMethod] + [TestCase] public void CtorSimpleServiceProvider() { new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager()); } - [TestMethod] + [TestCase] public void ReadFromRequestAuthorization() { this.ParameterizedReceiveTest(HttpDeliveryMethods.AuthorizationHeaderRequest); } @@ -75,7 +75,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { /// Verifies that the OAuth ReadFromRequest method gathers parameters /// from the Authorization header, the query string and the entity form data. /// </summary> - [TestMethod] + [TestCase] public void ReadFromRequestAuthorizationScattered() { // Start by creating a standard POST HTTP request. var fields = new Dictionary<string, string> { @@ -99,24 +99,24 @@ namespace DotNetOpenAuth.Test.ChannelElements { IDirectedProtocolMessage requestMessage = this.channel.ReadFromRequest(requestInfo); Assert.IsNotNull(requestMessage); - Assert.IsInstanceOfType(requestMessage, typeof(TestMessage)); + Assert.IsInstanceOf<TestMessage>(requestMessage); TestMessage testMessage = (TestMessage)requestMessage; Assert.AreEqual(15, testMessage.Age); Assert.AreEqual("Andrew", testMessage.Name); Assert.AreEqual("http://hostb/pathB", testMessage.Location.AbsoluteUri); } - [TestMethod] + [TestCase] public void ReadFromRequestForm() { this.ParameterizedReceiveTest(HttpDeliveryMethods.PostRequest); } - [TestMethod] + [TestCase] public void ReadFromRequestQueryString() { this.ParameterizedReceiveTest(HttpDeliveryMethods.GetRequest); } - [TestMethod] + [TestCase] public void SendDirectMessageResponse() { IProtocolMessage message = new TestDirectedMessage { Age = 15, @@ -135,7 +135,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.AreEqual("http://hostb/pathB", body["Location"]); } - [TestMethod] + [TestCase] public void ReadFromResponse() { var fields = new Dictionary<string, string> { { "age", "15" }, @@ -157,18 +157,18 @@ namespace DotNetOpenAuth.Test.ChannelElements { } } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void RequestNull() { this.channel.Request(null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void RequestNullRecipient() { IDirectedProtocolMessage message = new TestDirectedMessage(MessageTransport.Direct); this.channel.Request(message); } - [TestMethod, ExpectedException(typeof(NotSupportedException))] + [TestCase, ExpectedException(typeof(NotSupportedException))] public void RequestBadPreferredScheme() { TestDirectedMessage message = new TestDirectedMessage(MessageTransport.Direct); message.Recipient = new Uri("http://localtest"); @@ -176,7 +176,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { this.channel.Request(message); } - [TestMethod] + [TestCase] public void RequestUsingAuthorizationHeader() { this.ParameterizedRequestTest(HttpDeliveryMethods.AuthorizationHeaderRequest); } @@ -184,7 +184,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { /// <summary> /// Verifies that message parts can be distributed to the query, form, and Authorization header. /// </summary> - [TestMethod] + [TestCase] public void RequestUsingAuthorizationHeaderScattered() { TestDirectedMessage request = new TestDirectedMessage(MessageTransport.Direct) { Age = 15, @@ -217,17 +217,17 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.AreEqual("appearinform=formish", this.webRequestHandler.RequestEntityAsString); } - [TestMethod] + [TestCase] public void RequestUsingGet() { this.ParameterizedRequestTest(HttpDeliveryMethods.GetRequest); } - [TestMethod] + [TestCase] public void RequestUsingPost() { this.ParameterizedRequestTest(HttpDeliveryMethods.PostRequest); } - [TestMethod] + [TestCase] public void RequestUsingHead() { this.ParameterizedRequestTest(HttpDeliveryMethods.HeadRequest); } @@ -235,7 +235,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { /// <summary> /// Verifies that messages asking for special HTTP status codes get them. /// </summary> - [TestMethod] + [TestCase] public void SendDirectMessageResponseHonorsHttpStatusCodes() { IProtocolMessage message = MessagingTestBase.GetStandardTestMessage(MessagingTestBase.FieldFill.AllRequired); OutgoingWebResponse directResponse = this.accessor.PrepareDirectResponse(message); @@ -349,7 +349,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { IProtocolMessage response = this.channel.Request(request); Assert.IsNotNull(response); - Assert.IsInstanceOfType(response, typeof(TestMessage)); + Assert.IsInstanceOf<TestMessage>(response); TestMessage responseMessage = (TestMessage)response; Assert.AreEqual(request.Age, responseMessage.Age); Assert.AreEqual(request.Name, responseMessage.Name); @@ -365,7 +365,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { }; IProtocolMessage requestMessage = this.channel.ReadFromRequest(CreateHttpRequestInfo(scheme, fields)); Assert.IsNotNull(requestMessage); - Assert.IsInstanceOfType(requestMessage, typeof(TestMessage)); + Assert.IsInstanceOf<TestMessage>(requestMessage); TestMessage testMessage = (TestMessage)requestMessage; Assert.AreEqual(15, testMessage.Age); Assert.AreEqual("Andrew", testMessage.Name); diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs index 01d51a3..b81eefa 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs @@ -10,11 +10,11 @@ namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PlaintextSigningBindingElementTest { - [TestMethod] + [TestCase] public void HttpsSignatureGeneration() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); target.Channel = new TestChannel(); @@ -27,7 +27,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.AreEqual("cs&ts", message.Signature); } - [TestMethod] + [TestCase] public void HttpsSignatureVerification() { MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest); ITamperProtectionChannelBindingElement target = new PlaintextSigningBindingElement(); @@ -40,7 +40,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.IsNotNull(target.ProcessIncomingMessage(message)); } - [TestMethod] + [TestCase] public void HttpsSignatureVerificationNotApplicable() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); target.Channel = new TestChannel(); @@ -53,7 +53,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.AreEqual(MessageProtections.None, target.ProcessIncomingMessage(message), "PLAINTEXT binding element should opt-out where it doesn't understand."); } - [TestMethod] + [TestCase] public void HttpSignatureGeneration() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); target.Channel = new TestChannel(); @@ -68,7 +68,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.IsNull(message.Signature); } - [TestMethod] + [TestCase] public void HttpSignatureVerification() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); target.Channel = new TestChannel(); diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs index 6e566c8..c2ae65e 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs @@ -10,11 +10,11 @@ namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class SigningBindingElementBaseTests : MessagingTestBase { - [TestMethod] + [TestCase] public void BaseSignatureStringTest() { // Tests a message sent by HTTP GET, with no query string included in the endpoint. UnauthorizedTokenRequest message = CreateTestRequestTokenMessage( diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs index 40fc93e..715669a 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs @@ -10,13 +10,13 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using System.Linq; using System.Text; using DotNetOpenAuth.OAuth.ChannelElements; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class UriOrOobEncodingTests : TestBase { private UriOrOobEncoding encoding; - [TestInitialize] + [SetUp] public void Setup() { this.encoding = new UriOrOobEncoding(); } @@ -24,7 +24,7 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { /// <summary> /// Verifies null value encoding /// </summary> - [TestMethod] + [TestCase] public void NullValueEncoding() { Assert.AreEqual("oob", this.encoding.EncodedNullValue); } @@ -32,7 +32,7 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { /// <summary> /// Verifies decoding "oob" results in a null uri. /// </summary> - [TestMethod] + [TestCase] public void DecodeOobToNullUri() { Assert.IsNull(this.encoding.Decode("oob")); } @@ -40,7 +40,7 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { /// <summary> /// Verifies that decoding an empty string generates an exception. /// </summary> - [TestMethod, ExpectedException(typeof(UriFormatException))] + [TestCase, ExpectedException(typeof(UriFormatException))] public void DecodeEmptyStringFails() { this.encoding.Decode(string.Empty); } @@ -48,7 +48,7 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { /// <summary> /// Verifies proper decoding/encoding of a Uri /// </summary> - [TestMethod] + [TestCase] public void UriEncodeDecode() { Uri original = new Uri("http://somehost/p?q=a#frag"); string encodedValue = this.encoding.Encode(original); @@ -60,7 +60,7 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { /// <summary> /// Verifies failure to decode a relative Uri /// </summary> - [TestMethod, ExpectedException(typeof(UriFormatException))] + [TestCase, ExpectedException(typeof(UriFormatException))] public void RelativeUriDecodeFails() { this.encoding.Decode("../a/b"); } diff --git a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs index ce8070b..f866990 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs @@ -6,27 +6,27 @@ namespace DotNetOpenAuth.Test { using DotNetOpenAuth.OAuth; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ProtocolTests { - [TestMethod] + [TestCase] public void Default() { Assert.AreSame(Protocol.V10a, Protocol.Default); } - [TestMethod] + [TestCase] public void DataContractNamespace() { Assert.AreEqual("http://oauth.net/core/1.0/", Protocol.V10.DataContractNamespace); Assert.AreEqual("http://oauth.net/core/1.0/", Protocol.DataContractNamespaceV10); } - [TestMethod] + [TestCase] public void AuthorizationHeaderScheme() { Assert.AreEqual("OAuth", Protocol.AuthorizationHeaderScheme); } - [TestMethod] + [TestCase] public void ParameterPrefix() { Assert.AreEqual("oauth_", Protocol.ParameterPrefix); } diff --git a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs index 760a9e9..4ed1c74 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs @@ -8,17 +8,17 @@ namespace DotNetOpenAuth.Test { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; /// <summary> /// Tests for the <see cref="ServiceProviderEndpoints"/> class. /// </summary> - [TestClass] + [TestFixture] public class ServiceProviderDescriptionTests : TestBase { /// <summary> /// A test for UserAuthorizationUri /// </summary> - [TestMethod] + [TestCase] public void UserAuthorizationUriTest() { ServiceProviderDescription target = new ServiceProviderDescription(); MessageReceivingEndpoint expected = new MessageReceivingEndpoint("http://localhost/authorization", HttpDeliveryMethods.GetRequest); @@ -34,7 +34,7 @@ namespace DotNetOpenAuth.Test { /// <summary> /// A test for RequestTokenUri /// </summary> - [TestMethod] + [TestCase] public void RequestTokenUriTest() { var target = new ServiceProviderDescription(); MessageReceivingEndpoint expected = new MessageReceivingEndpoint("http://localhost/requesttoken", HttpDeliveryMethods.GetRequest); @@ -51,7 +51,7 @@ namespace DotNetOpenAuth.Test { /// Verifies that oauth parameters are not allowed in <see cref="ServiceProvider.RequestTokenUri"/>, /// per section OAuth 1.0 section 4.1. /// </summary> - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void RequestTokenUriWithOAuthParametersTest() { var target = new ServiceProviderDescription(); target.RequestTokenEndpoint = new MessageReceivingEndpoint("http://localhost/requesttoken?oauth_token=something", HttpDeliveryMethods.GetRequest); @@ -60,7 +60,7 @@ namespace DotNetOpenAuth.Test { /// <summary> /// A test for AccessTokenUri /// </summary> - [TestMethod] + [TestCase] public void AccessTokenUriTest() { var target = new ServiceProviderDescription(); MessageReceivingEndpoint expected = new MessageReceivingEndpoint("http://localhost/accesstoken", HttpDeliveryMethods.GetRequest); diff --git a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs index 2a443ce..ceb9a60 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs @@ -11,14 +11,14 @@ namespace DotNetOpenAuth.Test.OAuth { using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ServiceProviderTests : TestBase { /// <summary> /// Verifies the CreateVerificationCode method. /// </summary> - [TestMethod] + [TestCase] public void CreateVerificationCode() { this.TestCode(VerificationCodeFormat.Numeric, 3, MessagingUtilities.Digits); this.TestCode(VerificationCodeFormat.AlphaLower, 5, MessagingUtilities.LowercaseLetters); @@ -28,7 +28,7 @@ namespace DotNetOpenAuth.Test.OAuth { private void TestCode(VerificationCodeFormat format, int length, string allowableCharacters) { string code = ServiceProvider.CreateVerificationCode(format, length); - TestContext.WriteLine("{0} of length {2}: {1}", format, code, length); + TestUtilities.TestLogger.InfoFormat("{0} of length {2}: {1}", format, code, length); Assert.AreEqual(length, code.Length); foreach (char ch in code) { Assert.IsTrue(allowableCharacters.Contains(ch)); diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs index af3b1b1..8c2b5bd 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs @@ -12,21 +12,21 @@ namespace DotNetOpenAuth.Test.OpenId { using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AssociationHandshakeTests : OpenIdTestBase { - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } - [TestMethod] + [TestCase] public void AssociateUnencrypted() { this.ParameterizedAssociationTest(new Uri("https://host")); } - [TestMethod] + [TestCase] public void AssociateDiffieHellmanOverHttp() { this.ParameterizedAssociationTest(new Uri("http://host")); } @@ -38,7 +38,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// Some OPs out there flatly refuse to do this, and the spec doesn't forbid /// putting the two together, so we verify that DNOI can handle it. /// </remarks> - [TestMethod] + [TestCase] public void AssociateDiffieHellmanOverHttps() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -62,7 +62,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// Verifies that the RP and OP can renegotiate an association type if the RP's /// initial request for an association is for a type the OP doesn't support. /// </summary> - [TestMethod] + [TestCase] public void AssociateRenegotiateBitLength() { Protocol protocol = Protocol.V20; @@ -80,7 +80,7 @@ namespace DotNetOpenAuth.Test.OpenId { op.SecuritySettings.MaximumHashBitLength = 160; // Force OP to reject HMAC-SHA256 // Receive initial request for an HMAC-SHA256 association. - AutoResponsiveRequest req = (AutoResponsiveRequest) op.GetRequest(); + AutoResponsiveRequest req = (AutoResponsiveRequest)op.GetRequest(); AutoResponsiveRequest_Accessor reqAccessor = AutoResponsiveRequest_Accessor.AttachShadow(req); AssociateRequest associateRequest = (AssociateRequest)reqAccessor.RequestMessage; Assert.AreEqual(protocol.Args.SignatureAlgorithm.HMAC_SHA256, associateRequest.AssociationType); @@ -110,7 +110,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// <remarks> /// Verifies OP's compliance with OpenID 2.0 section 8.4.1. /// </remarks> - [TestMethod] + [TestCase] public void OPRejectsHttpNoEncryptionAssociateRequests() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -131,7 +131,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// Verifies that the OP rejects an associate request /// when the HMAC and DH bit lengths do not match. /// </summary> - [TestMethod] + [TestCase] public void OPRejectsMismatchingAssociationAndSessionTypes() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -154,7 +154,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// <summary> /// Verifies that the RP quietly rejects an OP that suggests an unknown association type. /// </summary> - [TestMethod] + [TestCase] public void RPRejectsUnrecognizedAssociationType() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -181,7 +181,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// <remarks> /// Verifies RP's compliance with OpenID 2.0 section 8.4.1. /// </remarks> - [TestMethod] + [TestCase] public void RPRejectsUnencryptedSuggestion() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -206,7 +206,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// Verifies that the RP rejects an associate renegotiate request /// when the HMAC and DH bit lengths do not match. /// </summary> - [TestMethod] + [TestCase] public void RPRejectsMismatchingAssociationAndSessionBitLengths() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -231,7 +231,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// Verifies that the RP cannot get caught in an infinite loop if a bad OP /// keeps sending it association retry messages. /// </summary> - [TestMethod] + [TestCase] public void RPOnlyRenegotiatesOnce() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -264,7 +264,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// <summary> /// Verifies security settings limit RP's acceptance of OP's counter-suggestion /// </summary> - [TestMethod] + [TestCase] public void AssociateRenegotiateLimitedByRPSecuritySettings() { Protocol protocol = Protocol.V20; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -284,7 +284,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// Verifies that the RP can recover from an invalid or non-existent /// response from the OP, for example in the HTTP timeout case. /// </summary> - [TestMethod] + [TestCase] public void AssociateQuietlyFailsAfterHttpError() { this.MockResponder.RegisterMockNotFound(OPUri); var rp = this.CreateRelyingParty(); @@ -318,9 +318,9 @@ namespace DotNetOpenAuth.Test.OpenId { private void ParameterizedAssociationTest( ProviderEndpointDescription opDescription, string expectedAssociationType) { - Protocol protocol = Protocol.Lookup(opDescription.ProtocolVersion); + Protocol protocol = Protocol.Lookup(Protocol.Lookup(opDescription.Version).ProtocolVersion); bool expectSuccess = expectedAssociationType != null; - bool expectDiffieHellman = !opDescription.Endpoint.IsTransportSecure(); + bool expectDiffieHellman = !opDescription.Uri.IsTransportSecure(); Association rpAssociation = null, opAssociation; AssociateSuccessfulResponse associateSuccessfulResponse = null; AssociateUnsuccessfulResponse associateUnsuccessfulResponse = null; @@ -337,7 +337,7 @@ namespace DotNetOpenAuth.Test.OpenId { op.SendResponse(req); }); coordinator.IncomingMessageFilter = message => { - Assert.AreSame(opDescription.ProtocolVersion, message.Version, "The message was recognized as version {0} but was expected to be {1}.", message.Version, opDescription.ProtocolVersion); + Assert.AreSame(opDescription.Version, message.Version, "The message was recognized as version {0} but was expected to be {1}.", message.Version, Protocol.Lookup(opDescription.Version).ProtocolVersion); var associateSuccess = message as AssociateSuccessfulResponse; var associateFailed = message as AssociateUnsuccessfulResponse; if (associateSuccess != null) { @@ -348,7 +348,7 @@ namespace DotNetOpenAuth.Test.OpenId { } }; coordinator.OutgoingMessageFilter = message => { - Assert.AreSame(opDescription.ProtocolVersion, message.Version, "The message was for version {0} but was expected to be for {1}.", message.Version, opDescription.ProtocolVersion); + Assert.AreEqual(opDescription.Version, message.Version, "The message was for version {0} but was expected to be for {1}.", message.Version, opDescription.Version); }; coordinator.Run(); @@ -356,7 +356,7 @@ namespace DotNetOpenAuth.Test.OpenId { if (expectSuccess) { Assert.IsNotNull(rpAssociation); - Assert.AreSame(rpAssociation, associationManagerAccessor.associationStore.GetAssociation(opDescription.Endpoint, rpAssociation.Handle)); + Assert.AreSame(rpAssociation, associationManagerAccessor.associationStore.GetAssociation(opDescription.Uri, rpAssociation.Handle)); opAssociation = coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, rpAssociation.Handle); Assert.IsNotNull(opAssociation, "The Provider should have stored the association."); @@ -367,15 +367,15 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.IsTrue(MessagingUtilities.AreEquivalent(opAssociation.SecretKey, rpAssociation.SecretKey)); if (expectDiffieHellman) { - Assert.IsInstanceOfType(associateSuccessfulResponse, typeof(AssociateDiffieHellmanResponse)); + Assert.IsInstanceOf<AssociateDiffieHellmanResponse>(associateSuccessfulResponse); var diffieHellmanResponse = (AssociateDiffieHellmanResponse)associateSuccessfulResponse; Assert.IsFalse(MessagingUtilities.AreEquivalent(diffieHellmanResponse.EncodedMacKey, rpAssociation.SecretKey), "Key should have been encrypted."); } else { - Assert.IsInstanceOfType(associateSuccessfulResponse, typeof(AssociateUnencryptedResponse)); + Assert.IsInstanceOf<AssociateUnencryptedResponse>(associateSuccessfulResponse); var unencryptedResponse = (AssociateUnencryptedResponse)associateSuccessfulResponse; } } else { - Assert.IsNull(associationManagerAccessor.associationStore.GetAssociation(opDescription.Endpoint, new RelyingPartySecuritySettings())); + Assert.IsNull(associationManagerAccessor.associationStore.GetAssociation(opDescription.Uri, new RelyingPartySecuritySettings())); Assert.IsNull(coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, new ProviderSecuritySettings())); } } diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationTests.cs index 6881255..a70189f 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AssociationTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AssociationTests.cs @@ -12,16 +12,16 @@ namespace DotNetOpenAuth.Test.OpenId { using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AssociationTests : OpenIdTestBase { private static readonly TimeSpan deltaDateTime = TimeSpan.FromSeconds(2); private static readonly HashAlgorithm sha1 = DiffieHellmanUtilities.Lookup(Protocol.Default, Protocol.Default.Args.SessionType.DH_SHA1); private byte[] sha1Secret; private byte[] sha1Secret2; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -35,7 +35,7 @@ namespace DotNetOpenAuth.Test.OpenId { this.sha1Secret2[1] = 0xcc; } - [TestMethod] + [TestCase] public void Properties() { string handle = "somehandle"; TimeSpan lifetime = TimeSpan.FromMinutes(2); @@ -49,7 +49,7 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.AreEqual(0, assoc.Issued.Millisecond, "No milliseconds because this can be cut off in conversions."); } - [TestMethod] + [TestCase] public void Sign() { Association assoc1 = HmacShaAssociation.Create(Protocol.Default, Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1, "h1", this.sha1Secret, TimeSpan.FromMinutes(2)); Association assoc2 = HmacShaAssociation.Create(Protocol.Default, Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1, "h2", this.sha1Secret2, TimeSpan.FromMinutes(2)); diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationsTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationsTests.cs index a0c4770..b3d7e4d 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AssociationsTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AssociationsTests.cs @@ -11,31 +11,31 @@ namespace DotNetOpenAuth.Test.OpenId { using System.Security.Cryptography; using System.Text; using DotNetOpenAuth.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AssociationsTests : OpenIdTestBase { private static readonly HashAlgorithm sha1 = DiffieHellmanUtilities.Lookup(Protocol.Default, Protocol.Default.Args.SessionType.DH_SHA1); private byte[] sha1Secret; private Associations assocs; - [TestInitialize] + [TestFixtureSetUp] public override void SetUp() { this.sha1Secret = new byte[sha1.HashSize / 8]; this.assocs = new Associations(); } - [TestMethod] + [TestCase] public void GetNonexistentHandle() { Assert.IsNull(this.assocs.Get("someinvalidhandle")); } - [TestMethod] + [TestCase] public void RemoveNonexistentHandle() { Assert.IsFalse(this.assocs.Remove("someinvalidhandle")); } - [TestMethod] + [TestCase] public void HandleLifecycle() { Association a = HmacShaAssociation.Create( Protocol.Default, @@ -50,7 +50,7 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.IsFalse(this.assocs.Remove(a.Handle)); } - [TestMethod] + [TestCase] public void Best() { Association a = HmacShaAssociation.Create( Protocol.Default, diff --git a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs index 7de2a8b..27db93e 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs @@ -14,16 +14,16 @@ namespace DotNetOpenAuth.Test.OpenId { using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AuthenticationTests : OpenIdTestBase { - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } - [TestMethod] + [TestCase] public void SharedAssociationPositive() { this.ParameterizedAuthenticationTest(true, true, false); } @@ -31,17 +31,17 @@ namespace DotNetOpenAuth.Test.OpenId { /// <summary> /// Verifies that a shared association protects against tampering. /// </summary> - [TestMethod] + [TestCase] public void SharedAssociationTampered() { this.ParameterizedAuthenticationTest(true, true, true); } - [TestMethod] + [TestCase] public void SharedAssociationNegative() { this.ParameterizedAuthenticationTest(true, false, false); } - [TestMethod] + [TestCase] public void PrivateAssociationPositive() { this.ParameterizedAuthenticationTest(false, true, false); } @@ -49,17 +49,17 @@ namespace DotNetOpenAuth.Test.OpenId { /// <summary> /// Verifies that a private association protects against tampering. /// </summary> - [TestMethod] + [TestCase] public void PrivateAssociationTampered() { this.ParameterizedAuthenticationTest(false, true, true); } - [TestMethod] + [TestCase] public void NoAssociationNegative() { this.ParameterizedAuthenticationTest(false, false, false); } - [TestMethod] + [TestCase] public void UnsolicitedAssertion() { this.MockResponder.RegisterMockRPDiscovery(); OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -77,7 +77,7 @@ namespace DotNetOpenAuth.Test.OpenId { coordinator.Run(); } - [TestMethod] + [TestCase] public void UnsolicitedAssertionRejected() { this.MockResponder.RegisterMockRPDiscovery(); OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -100,7 +100,7 @@ namespace DotNetOpenAuth.Test.OpenId { /// Verifies that delegating identifiers are rejected in unsolicited assertions /// when the appropriate security setting is set. /// </summary> - [TestMethod] + [TestCase] public void UnsolicitedDelegatingIdentifierRejection() { this.MockResponder.RegisterMockRPDiscovery(); OpenIdCoordinator coordinator = new OpenIdCoordinator( diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs index 29797dc..5edd51f 100644 --- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs @@ -18,15 +18,15 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; using DotNetOpenAuth.Test.OpenId.Extensions; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ExtensionsBindingElementTests : OpenIdTestBase { private StandardOpenIdExtensionFactory factory; private ExtensionsBindingElement rpElement; private IProtocolMessageWithExtensions request; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -37,7 +37,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { this.request = new SignedResponseRequest(Protocol.Default.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate); } - [TestMethod] + [TestCase] public void RoundTripFullStackTest() { IOpenIdMessageExtension request = new MockOpenIdExtension("requestPart", "requestData"); IOpenIdMessageExtension response = new MockOpenIdExtension("responsePart", "responseData"); @@ -47,12 +47,12 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { new IOpenIdMessageExtension[] { response }); } - [TestMethod] + [TestCase] public void ExtensionFactory() { Assert.AreSame(this.factory, this.rpElement.ExtensionFactory); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void PrepareMessageForSendingNull() { this.rpElement.ProcessOutgoingMessage(null); } @@ -60,13 +60,13 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// <summary> /// Verifies that false is returned when a non-extendable message is sent. /// </summary> - [TestMethod] + [TestCase] public void PrepareMessageForSendingNonExtendableMessage() { IProtocolMessage request = new AssociateDiffieHellmanRequest(Protocol.Default.Version, OpenIdTestBase.OPUri); Assert.IsNull(this.rpElement.ProcessOutgoingMessage(request)); } - [TestMethod] + [TestCase] public void PrepareMessageForSending() { this.request.Extensions.Add(new MockOpenIdExtension("part", "extra")); Assert.IsNotNull(this.rpElement.ProcessOutgoingMessage(this.request)); @@ -77,7 +77,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { Assert.AreEqual("extra", this.request.ExtraData["openid." + alias + ".data"]); } - [TestMethod] + [TestCase] public void PrepareMessageForReceiving() { this.request.ExtraData["openid.ns.mock"] = MockOpenIdExtension.MockTypeUri; this.request.ExtraData["openid.mock.Part"] = "part"; @@ -91,7 +91,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// <summary> /// Verifies that extension responses are included in the OP's signature. /// </summary> - [TestMethod] + [TestCase] public void ExtensionResponsesAreSigned() { Protocol protocol = Protocol.Default; var op = this.CreateProvider(); @@ -113,7 +113,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// <summary> /// Verifies that unsigned extension responses (where any or all fields are unsigned) are ignored. /// </summary> - [TestMethod] + [TestCase] public void ExtensionsAreIdentifiedAsSignedOrUnsigned() { Protocol protocol = Protocol.Default; OpenIdCoordinator coordinator = new OpenIdCoordinator( @@ -144,7 +144,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// OpenID Authentication 2.0 section 12 states that /// "A namespace MUST NOT be assigned more than one alias in the same message". /// </remarks> - [TestMethod] + [TestCase] public void TwoExtensionsSameTypeUri() { IOpenIdMessageExtension request1 = new MockOpenIdExtension("requestPart1", "requestData1"); IOpenIdMessageExtension request2 = new MockOpenIdExtension("requestPart2", "requestData2"); @@ -154,8 +154,8 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { new IOpenIdMessageExtension[] { request1, request2 }, new IOpenIdMessageExtension[0]); Assert.Fail("Expected ProtocolException not thrown."); - } catch (AssertFailedException ex) { - Assert.IsInstanceOfType(ex.InnerException, typeof(ProtocolException)); + } catch (AssertionException ex) { + Assert.IsInstanceOf<ProtocolException>(ex.InnerException); } } diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/KeyValueFormEncodingTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/KeyValueFormEncodingTests.cs index 92170c4..75ac503 100644 --- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/KeyValueFormEncodingTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/KeyValueFormEncodingTests.cs @@ -14,9 +14,9 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.ChannelElements; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class KeyValueFormEncodingTests : TestBase { private Dictionary<string, string> sampleData = new Dictionary<string, string> { { "key1", "value1" }, @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { Both = 0x3, } - [TestMethod] + [TestCase] public void BasicEncodingTest() { byte[] kvfBytes = KeyValueFormEncoding.GetBytes(this.sampleData); string responseString = Encoding.UTF8.GetString(kvfBytes); @@ -69,7 +69,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { } } - [TestMethod] + [TestCase] public void EncodeDecode() { this.KVDictTest(UTF8Encoding.UTF8.GetBytes(string.Empty), new Dictionary<string, string>(), TestMode.Both); @@ -117,35 +117,35 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { this.KVDictTest(UTF8Encoding.UTF8.GetBytes("east:west\nnorth:south"), d10, TestMode.Decoder); } - [TestMethod, ExpectedException(typeof(FormatException))] + [TestCase, ExpectedException(typeof(FormatException))] public void NoValue() { this.Illegal("x\n", KeyValueFormConformanceLevel.OpenId11); } - [TestMethod, ExpectedException(typeof(FormatException))] + [TestCase, ExpectedException(typeof(FormatException))] public void NoValueLoose() { Dictionary<string, string> d = new Dictionary<string, string>(); this.KVDictTest(Encoding.UTF8.GetBytes("x\n"), d, TestMode.Decoder); } - [TestMethod, ExpectedException(typeof(FormatException))] + [TestCase, ExpectedException(typeof(FormatException))] public void EmptyLine() { this.Illegal("x:b\n\n", KeyValueFormConformanceLevel.OpenId20); } - [TestMethod] + [TestCase] public void EmptyLineLoose() { Dictionary<string, string> d = new Dictionary<string, string>(); d.Add("x", "b"); this.KVDictTest(Encoding.UTF8.GetBytes("x:b\n\n"), d, TestMode.Decoder); } - [TestMethod, ExpectedException(typeof(FormatException))] + [TestCase, ExpectedException(typeof(FormatException))] public void LastLineNotTerminated() { this.Illegal("x:y\na:b", KeyValueFormConformanceLevel.OpenId11); } - [TestMethod] + [TestCase] public void LastLineNotTerminatedLoose() { Dictionary<string, string> d = new Dictionary<string, string>(); d.Add("x", "y"); diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs index 385cd19..97a40e8 100644 --- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs @@ -18,16 +18,16 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] 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] + [SetUp] public void Setup() { this.webHandler = new Mocks.TestWebRequestHandler(); this.channel = new OpenIdChannel(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(maximumMessageAge), new RelyingPartySecuritySettings()); @@ -35,7 +35,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { this.channel.WebRequestHandler = this.webHandler; } - [TestMethod] + [TestCase] 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 @@ -53,7 +53,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// <summary> /// Verifies that the channel sends direct message requests as HTTP POST requests. /// </summary> - [TestMethod] + [TestCase] public void DirectRequestsUsePost() { IDirectedProtocolMessage requestMessage = new Mocks.TestDirectedMessage(MessageTransport.Direct) { Recipient = new Uri("http://host"), @@ -61,7 +61,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { }; HttpWebRequest httpRequest = this.accessor.CreateHttpRequest(requestMessage); Assert.AreEqual("POST", httpRequest.Method); - StringAssert.Contains(this.webHandler.RequestEntityAsString, "Name=Andrew"); + StringAssert.Contains("Name=Andrew", this.webHandler.RequestEntityAsString); } /// <summary> @@ -73,7 +73,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// class is verified elsewhere. We're only checking that the KVF class is being used by the /// <see cref="OpenIdChannel.SendDirectMessageResponse"/> method. /// </remarks> - [TestMethod] + [TestCase] public void DirectResponsesSentUsingKeyValueForm() { IProtocolMessage message = MessagingTestBase.GetStandardTestMessage(MessagingTestBase.FieldFill.AllRequired); MessageDictionary messageFields = this.MessageDescriptions.GetAccessor(message); @@ -90,7 +90,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// <summary> /// Verifies that direct message responses are read in using the Key Value Form decoder. /// </summary> - [TestMethod] + [TestCase] public void DirectResponsesReceivedAsKeyValueForm() { var fields = new Dictionary<string, string> { { "var1", "value1" }, @@ -105,7 +105,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// <summary> /// Verifies that messages asking for special HTTP status codes get them. /// </summary> - [TestMethod] + [TestCase] public void SendDirectMessageResponseHonorsHttpStatusCodes() { IProtocolMessage message = MessagingTestBase.GetStandardTestMessage(MessagingTestBase.FieldFill.AllRequired); OutgoingWebResponse directResponse = this.accessor.PrepareDirectResponse(message); diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/SigningBindingElementTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/SigningBindingElementTests.cs index 3ab6559..6160680 100644 --- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/SigningBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/SigningBindingElementTests.cs @@ -12,16 +12,16 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class SigningBindingElementTests : OpenIdTestBase { /// <summary> /// Verifies that the signatures generated match Known Good signatures. /// </summary> - [TestMethod] + [TestCase] public void SignaturesMatchKnownGood() { - Protocol protocol = Protocol.Default; + Protocol protocol = Protocol.V20; var settings = new ProviderSecuritySettings(); var store = new AssociationMemoryStore<AssociationRelyingPartyType>(); byte[] associationSecret = Convert.FromBase64String("rsSwv1zPWfjPRQU80hciu8FPDC+GONAMJQ/AvSo1a2M="); @@ -42,7 +42,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { /// <summary> /// Verifies that all parameters in ExtraData in signed responses are signed. /// </summary> - [TestMethod] + [TestCase] public void SignedResponsesIncludeExtraDataInSignature() { Protocol protocol = Protocol.Default; SigningBindingElement sbe = new SigningBindingElement(new AssociationMemoryStore<AssociationRelyingPartyType>(), new ProviderSecuritySettings()); diff --git a/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs index 426e19a..9e89b03 100644 --- a/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs @@ -9,12 +9,12 @@ namespace DotNetOpenAuth.Test.OpenId { using System.IO; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; using Org.Mentalis.Security.Cryptography; - [TestClass] + [TestFixture] public class DiffieHellmanTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void Test() { string s1 = Test1(); string s2 = Test1(); @@ -22,7 +22,7 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.AreNotEqual(s1, s2, "Secret keys should NOT be the same."); } - [TestMethod, Timeout(15000)] + [TestCase, Timeout(15000), Category("Slow"), Category("Performance")] public void TestPublic() { TextReader reader = new StringReader(OpenIdTestBase.LoadEmbeddedFile("dhpriv.txt")); @@ -30,7 +30,7 @@ namespace DotNetOpenAuth.Test.OpenId { string line; int lineNumber = 0; while ((line = reader.ReadLine()) != null) { - TestContext.WriteLine("\tLine {0}", ++lineNumber); + TestUtilities.TestLogger.InfoFormat("\tLine {0}", ++lineNumber); string[] parts = line.Trim().Split(' '); byte[] x = Convert.FromBase64String(parts[0]); DiffieHellmanManaged dh = new DiffieHellmanManaged(AssociateDiffieHellmanRequest.DefaultMod, AssociateDiffieHellmanRequest.DefaultGen, x); diff --git a/src/DotNetOpenAuth.Test/OpenId/Discovery/htmldiscovery/html20provWithBadXrds.html b/src/DotNetOpenAuth.Test/OpenId/Discovery/htmldiscovery/html20provWithBadXrds.html new file mode 100644 index 0000000..e695116 --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/Discovery/htmldiscovery/html20provWithBadXrds.html @@ -0,0 +1,10 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Untitled Page</title> + <meta http-equiv="X-XRDS-Location" content="http://localhost/xrds-notfound.xml"/> + <link rel="openid2.provider" href="http://a/b" /> +</head> +<body> +</body> +</html> diff --git a/src/DotNetOpenAuth.Test/OpenId/Discovery/htmldiscovery/html20provWithEmptyXrds.html b/src/DotNetOpenAuth.Test/OpenId/Discovery/htmldiscovery/html20provWithEmptyXrds.html new file mode 100644 index 0000000..97ad7dc --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/Discovery/htmldiscovery/html20provWithEmptyXrds.html @@ -0,0 +1,10 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Untitled Page</title> + <meta http-equiv="X-XRDS-Location" content="http://localhost/xrds-irrelevant.xml"/> + <link rel="openid2.provider" href="http://a/b" /> +</head> +<body> +</body> +</html> diff --git a/src/DotNetOpenAuth.Test/OpenId/Discovery/xrdsdiscovery/xrds20dual.xml b/src/DotNetOpenAuth.Test/OpenId/Discovery/xrdsdiscovery/xrds20dual.xml new file mode 100644 index 0000000..9e6a66b --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/Discovery/xrdsdiscovery/xrds20dual.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xrds:XRDS + xmlns:xrds="xri://$xrds" + xmlns:openid="http://openid.net/xmlns/1.0" + xmlns="xri://$xrd*($v*2.0)"> + <XRD> + <Service priority="10"> + <Type>http://specs.openid.net/auth/2.0/signon</Type> + <Type>http://specs.openid.net/auth/2.0/server</Type> + <URI>http://a/b</URI> + </Service> + </XRD> +</xrds:XRDS> diff --git a/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs new file mode 100644 index 0000000..1050b4b --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs @@ -0,0 +1,303 @@ +//----------------------------------------------------------------------- +// <copyright file="UriDiscoveryServiceTests.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.OpenId.DiscoveryServices { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Text; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + using DotNetOpenAuth.OpenId.RelyingParty; + using NUnit.Framework; + + [TestFixture] + public class UriDiscoveryServiceTests : OpenIdTestBase { + [TestCase] + public void DiscoveryWithRedirects() { + Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, false); + + // Add a couple of chained redirect pages that lead to the claimedId. + Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); + Uri insecureMidpointUri = new Uri("http://localhost/insecureStop"); + this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri); + this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString())); + + // don't require secure SSL discovery for this test. + Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, false); + Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase] + public void DiscoverRequireSslWithSecureRedirects() { + Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true); + + // Add a couple of chained redirect pages that lead to the claimedId. + // All redirects should be secure. + Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); + Uri secureMidpointUri = new Uri("https://localhost/secureStop"); + this.MockResponder.RegisterMockRedirect(userSuppliedUri, secureMidpointUri); + this.MockResponder.RegisterMockRedirect(secureMidpointUri, new Uri(claimedId.ToString())); + + Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true); + Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase, ExpectedException(typeof(ProtocolException))] + public void DiscoverRequireSslWithInsecureRedirect() { + Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true); + + // Add a couple of chained redirect pages that lead to the claimedId. + // Include an insecure HTTP jump in those redirects to verify that + // the ultimate endpoint is never found as a result of high security profile. + Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); + Uri insecureMidpointUri = new Uri("http://localhost/insecureStop"); + this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri); + this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString())); + + Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true); + this.Discover(userSuppliedIdentifier); + } + + [TestCase] + public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() { + var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); + Uri secureClaimedUri = new Uri("https://localhost/secureId"); + + string html = string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>", insecureXrdsSource); + this.MockResponder.RegisterMockResponse(secureClaimedUri, "text/html", html); + + Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true); + Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase] + public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() { + var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); + + string html = "<html><head></head><body></body></html>"; + WebHeaderCollection headers = new WebHeaderCollection { + { "X-XRDS-Location", insecureXrdsSource } + }; + this.MockResponder.RegisterMockResponse(VanityUriSsl, VanityUriSsl, "text/html", headers, html); + + Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true); + Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase] + public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() { + var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); + string html = string.Format( + @" + <html><head> + <meta http-equiv='X-XRDS-Location' content='{0}'/> <!-- this one will be insecure and ignored --> + <link rel='openid2.provider' href='{1}' /> + <link rel='openid2.local_id' href='{2}' /> + </head><body></body></html>", + HttpUtility.HtmlEncode(insecureXrdsSource), + HttpUtility.HtmlEncode(OPUriSsl.AbsoluteUri), + HttpUtility.HtmlEncode(OPLocalIdentifiersSsl[1].AbsoluteUri)); + this.MockResponder.RegisterMockResponse(VanityUriSsl, "text/html", html); + + Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true); + + // We verify that the XRDS was ignored and the LINK tags were used + // because the XRDS OP-LocalIdentifier uses different local identifiers. + Assert.AreEqual(OPLocalIdentifiersSsl[1].AbsoluteUri, this.Discover(userSuppliedIdentifier).Single().ProviderLocalIdentifier.ToString()); + } + + [TestCase] + public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() { + var insecureEndpoint = GetServiceEndpoint(0, ProtocolVersion.V20, 10, false); + var secureEndpoint = GetServiceEndpoint(1, ProtocolVersion.V20, 20, true); + UriIdentifier secureClaimedId = new UriIdentifier(VanityUriSsl, true); + this.MockResponder.RegisterMockXrdsResponse(secureClaimedId, new IdentifierDiscoveryResult[] { insecureEndpoint, secureEndpoint }); + Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, this.Discover(secureClaimedId).Single().ProviderLocalIdentifier); + } + + [TestCase] + public void XrdsDirectDiscovery_10() { + this.FailDiscoverXrds("xrds-irrelevant"); + this.DiscoverXrds("xrds10", ProtocolVersion.V10, null, "http://a/b"); + this.DiscoverXrds("xrds11", ProtocolVersion.V11, null, "http://a/b"); + this.DiscoverXrds("xrds1020", ProtocolVersion.V10, null, "http://a/b"); + } + + [TestCase] + public void XrdsDirectDiscovery_20() { + this.DiscoverXrds("xrds20", ProtocolVersion.V20, null, "http://a/b"); + this.DiscoverXrds("xrds2010a", ProtocolVersion.V20, null, "http://a/b"); + this.DiscoverXrds("xrds2010b", ProtocolVersion.V20, null, "http://a/b"); + } + + [TestCase] + public void HtmlDiscover_11() { + this.DiscoverHtml("html10prov", ProtocolVersion.V11, null, "http://a/b"); + this.DiscoverHtml("html10both", ProtocolVersion.V11, "http://c/d", "http://a/b"); + this.FailDiscoverHtml("html10del"); + + // Verify that HTML discovery generates the 1.x endpoints when appropriate + this.DiscoverHtml("html2010", ProtocolVersion.V11, "http://g/h", "http://e/f"); + this.DiscoverHtml("html1020", ProtocolVersion.V11, "http://g/h", "http://e/f"); + this.DiscoverHtml("html2010combinedA", ProtocolVersion.V11, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedB", ProtocolVersion.V11, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedC", ProtocolVersion.V11, "http://c/d", "http://a/b"); + } + + [TestCase] + public void HtmlDiscover_20() { + this.DiscoverHtml("html20prov", ProtocolVersion.V20, null, "http://a/b"); + this.DiscoverHtml("html20both", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.FailDiscoverHtml("html20del"); + this.DiscoverHtml("html2010", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html1020", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedA", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedB", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedC", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.FailDiscoverHtml("html20relative"); + } + + [TestCase] + public void XrdsDiscoveryFromHead() { + this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml")); + this.DiscoverXrds("XrdsReferencedInHead.html", ProtocolVersion.V10, null, "http://a/b"); + } + + [TestCase] + public void XrdsDiscoveryFromHttpHeader() { + WebHeaderCollection headers = new WebHeaderCollection(); + headers.Add("X-XRDS-Location", new Uri("http://localhost/xrds1020.xml").AbsoluteUri); + this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml")); + this.DiscoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion.V10, null, "http://a/b", headers); + } + + /// <summary> + /// Verifies HTML discovery proceeds if an XRDS document is referenced that doesn't contain OpenID endpoints. + /// </summary> + [TestCase] + public void HtmlDiscoveryProceedsIfXrdsIsEmpty() { + this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds-irrelevant.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds-irrelevant.xml")); + this.DiscoverHtml("html20provWithEmptyXrds", ProtocolVersion.V20, null, "http://a/b"); + } + + /// <summary> + /// Verifies HTML discovery proceeds if the XRDS that is referenced cannot be found. + /// </summary> + [TestCase] + public void HtmlDiscoveryProceedsIfXrdsIsBadOrMissing() { + this.DiscoverHtml("html20provWithBadXrds", ProtocolVersion.V20, null, "http://a/b"); + } + + /// <summary> + /// Verifies that a dual identifier yields only one service endpoint by default. + /// </summary> + [TestCase] + public void DualIdentifierOffByDefault() { + this.MockResponder.RegisterMockResponse(VanityUri, "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds20dual.xml")); + var results = this.Discover(VanityUri).ToList(); + Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == r.Protocol.ClaimedIdentifierForOPIdentifier), "OP Identifier missing from discovery results."); + Assert.AreEqual(1, results.Count, "Unexpected additional services discovered."); + } + + /// <summary> + /// Verifies that a dual identifier yields two service endpoints when that feature is turned on. + /// </summary> + [TestCase] + public void DualIdentifier() { + this.MockResponder.RegisterMockResponse(VanityUri, "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds20dual.xml")); + var rp = this.CreateRelyingParty(true); + rp.Channel.WebRequestHandler = this.RequestHandler; + rp.SecuritySettings.AllowDualPurposeIdentifiers = true; + var results = rp.Discover(VanityUri).ToList(); + Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == r.Protocol.ClaimedIdentifierForOPIdentifier), "OP Identifier missing from discovery results."); + Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == VanityUri), "Claimed identifier missing from discovery results."); + Assert.AreEqual(2, results.Count, "Unexpected additional services discovered."); + } + + private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect) { + this.Discover(url, version, expectedLocalId, providerEndpoint, expectSreg, useRedirect, null); + } + + private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect, WebHeaderCollection headers) { + Protocol protocol = Protocol.Lookup(version); + Uri baseUrl = new Uri("http://localhost/"); + UriIdentifier claimedId = new Uri(baseUrl, url); + UriIdentifier userSuppliedIdentifier = new Uri(baseUrl, "Discovery/htmldiscovery/redirect.aspx?target=" + url); + if (expectedLocalId == null) { + expectedLocalId = claimedId; + } + Identifier idToDiscover = useRedirect ? userSuppliedIdentifier : claimedId; + + string contentType; + if (url.EndsWith("html")) { + contentType = "text/html"; + } else if (url.EndsWith("xml")) { + contentType = "application/xrds+xml"; + } else { + throw new InvalidOperationException(); + } + this.MockResponder.RegisterMockResponse(new Uri(idToDiscover), claimedId, contentType, headers ?? new WebHeaderCollection(), LoadEmbeddedFile(url)); + + IdentifierDiscoveryResult expected = IdentifierDiscoveryResult.CreateForClaimedIdentifier( + claimedId, + expectedLocalId, + new ProviderEndpointDescription(new Uri(providerEndpoint), new string[] { protocol.ClaimedIdentifierServiceTypeURI }), // services aren't checked by Equals + null, + null); + + IdentifierDiscoveryResult se = this.Discover(idToDiscover).FirstOrDefault(ep => ep.Equals(expected)); + Assert.IsNotNull(se, url + " failed to be discovered."); + + // Do extra checking of service type URIs, which aren't included in + // the ServiceEndpoint.Equals method. + Assert.AreEqual(expectSreg ? 2 : 1, se.Capabilities.Count); + Assert.IsTrue(se.Capabilities.Contains(protocol.ClaimedIdentifierServiceTypeURI)); + Assert.AreEqual(expectSreg, se.IsExtensionSupported<ClaimsRequest>()); + } + + private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) { + this.DiscoverXrds(page, version, expectedLocalId, providerEndpoint, null); + } + + private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, WebHeaderCollection headers) { + if (!page.Contains(".")) { + page += ".xml"; + } + this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, false, headers); + this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, true, headers); + } + + private void DiscoverHtml(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool useRedirect) { + this.Discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, providerEndpoint, false, useRedirect); + } + + private void DiscoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) { + string page = scenario + ".html"; + this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, false); + this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, true); + } + + private void FailDiscover(string url) { + UriIdentifier userSuppliedId = new Uri(new Uri("http://localhost"), url); + + this.MockResponder.RegisterMockResponse(new Uri(userSuppliedId), userSuppliedId, "text/html", LoadEmbeddedFile(url)); + + Assert.AreEqual(0, this.Discover(userSuppliedId).Count()); // ... but that no endpoint info is discoverable + } + + private void FailDiscoverHtml(string scenario) { + this.FailDiscover("/Discovery/htmldiscovery/" + scenario + ".html"); + } + + private void FailDiscoverXrds(string scenario) { + this.FailDiscover("/Discovery/xrdsdiscovery/" + scenario + ".xml"); + } + } +} diff --git a/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/XriDiscoveryProxyServiceTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/XriDiscoveryProxyServiceTests.cs new file mode 100644 index 0000000..5d914f5 --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/XriDiscoveryProxyServiceTests.cs @@ -0,0 +1,394 @@ +//----------------------------------------------------------------------- +// <copyright file="XriDiscoveryProxyServiceTests.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.OpenId.DiscoveryServices { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.RelyingParty; + using NUnit.Framework; + + [TestFixture] + public class XriDiscoveryProxyServiceTests : OpenIdTestBase { + [TestCase] + public void Discover() { + string xrds = @"<?xml version='1.0' encoding='UTF-8'?> +<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'> + <Query>*Arnott</Query> + <Status ceid='off' cid='verified' code='100'/> + <Expires>2008-07-14T02:03:24.000Z</Expires> + <ProviderID>xri://=</ProviderID> + <LocalID>!9b72.7dd1.50a9.5ccd</LocalID> + <CanonicalID>=!9B72.7DD1.50A9.5CCD</CanonicalID> + + <Service priority='10'> + <ProviderID>xri://!!1008</ProviderID> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='default' select='false'/> + <Path select='true'>(+contact)</Path> + <Path match='null' select='false'/> + <URI append='qxri' priority='1'>http://1id.com/contact/</URI> + + </Service> + <Service priority='10'> + <ProviderID>xri://!!1008</ProviderID> + <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> + <Type match='null' select='false'/> + <URI append='qxri' priority='1'>http://1id.com/</URI> + </Service> + + <Service priority='10'> + <ProviderID>xri://!!1008</ProviderID> + <Type select='true'>http://openid.net/signon/1.0</Type> + <URI append='none' priority='10'>http://1id.com/sso</URI> + </Service> +</XRD>"; + Dictionary<string, string> mocks = new Dictionary<string, string> { + { "https://xri.net/=Arnott?_xrd_r=application/xrd%2Bxml;sep=false", xrds }, + { "https://xri.net/=!9B72.7DD1.50A9.5CCD?_xrd_r=application/xrd%2Bxml;sep=false", xrds }, + }; + this.MockResponder.RegisterMockXrdsResponses(mocks); + + string expectedCanonicalId = "=!9B72.7DD1.50A9.5CCD"; + IdentifierDiscoveryResult se = this.VerifyCanonicalId("=Arnott", expectedCanonicalId); + Assert.AreEqual(Protocol.V10, Protocol.Lookup(se.Version)); + Assert.AreEqual("http://1id.com/sso", se.ProviderEndpoint.ToString()); + Assert.AreEqual(se.ClaimedIdentifier, se.ProviderLocalIdentifier); + Assert.AreEqual("=Arnott", se.FriendlyIdentifierForDisplay); + } + + [TestCase] + public void DiscoverCommunityInameCanonicalIDs() { + string llliResponse = @"<?xml version='1.0' encoding='UTF-8'?> +<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'> + <Query>*llli</Query> + <Status ceid='off' cid='verified' code='100'/> + <Expires>2008-07-14T02:21:06.000Z</Expires> + <ProviderID>xri://@</ProviderID> + <LocalID>!72cd.a072.157e.a9c6</LocalID> + <CanonicalID>@!72CD.A072.157E.A9C6</CanonicalID> + <Service priority='10'> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>http://openid.net/signon/1.0</Type> + <URI append='none' priority='1'>https://login.llli.org/server/</URI> + </Service> + <Service priority='1'> + <ProviderID>xri://!!1003!103</ProviderID> + <Type match='null' select='false'/> + <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> + <Path match='default'/> + <Path>(+index)</Path> + <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> + </Service> + <Service priority='10'> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://$res*auth*($v*2.0)</Type> + <MediaType>application/xrds+xml;trust=none</MediaType> + <URI priority='10'>http://resolve.ezibroker.net/resolve/@llli/</URI> + </Service> + <Service priority='10'> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='null'/> + <Path select='true'>(+contact)</Path> + <Path match='null'/> + <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> + </Service> +</XRD> +"; + string llliAreaResponse = @"<?xml version='1.0' encoding='UTF-8'?> +<XRD xmlns='xri://$xrd*($v*2.0)'> + <Query>*area</Query> + <Status cid='verified' code='100'>SUCCESS</Status> + <ServerStatus code='100'>SUCCESS</ServerStatus> + <Expires>2008-07-15T01:21:07.000Z</Expires> + <ProviderID>xri://!!1003</ProviderID> + <LocalID>0000.0000.3B9A.CA0C</LocalID> + <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C</CanonicalID> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>http://openid.net/signon/1.0</Type> + <URI append='none' priority='1'>https://login.llli.org/server/</URI> + </Service> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='null'/> + <Path select='true'>(+contact)</Path> + <Path match='null'/> + <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> + </Service> + <Service priority='1'> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> + <Type match='null' select='false'/> + <Path>(+index)</Path> + <Path match='default'/> + <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> + </Service> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://$res*auth*($v*2.0)</Type> + <MediaType>application/xrds+xml;trust=none</MediaType> + <URI>http://resolve.ezibroker.net/resolve/@llli*area/</URI> + </Service> +</XRD>"; + string llliAreaCanadaUnattachedResponse = @"<?xml version='1.0' encoding='UTF-8'?> +<XRD xmlns='xri://$xrd*($v*2.0)'> + <Query>*canada.unattached</Query> + <Status cid='verified' code='100'>SUCCESS</Status> + <ServerStatus code='100'>SUCCESS</ServerStatus> + <Expires>2008-07-15T01:21:08.000Z</Expires> + <ProviderID>xri://!!1003</ProviderID> + <LocalID>0000.0000.3B9A.CA41</LocalID> + <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41</CanonicalID> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>http://openid.net/signon/1.0</Type> + <URI append='none' priority='1'>https://login.llli.org/server/</URI> + </Service> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='null'/> + <Path select='true'>(+contact)</Path> + <Path match='null'/> + <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> + </Service> + <Service priority='1'> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> + <Type match='null' select='false'/> + <Path>(+index)</Path> + <Path match='default'/> + <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> + </Service> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://$res*auth*($v*2.0)</Type> + <MediaType>application/xrds+xml;trust=none</MediaType> + <URI>http://resolve.ezibroker.net/resolve/@llli*area*canada.unattached/</URI> + </Service> +</XRD>"; + string llliAreaCanadaUnattachedAdaResponse = @"<?xml version='1.0' encoding='UTF-8'?> +<XRD xmlns='xri://$xrd*($v*2.0)'> + <Query>*ada</Query> + <Status cid='verified' code='100'>SUCCESS</Status> + <ServerStatus code='100'>SUCCESS</ServerStatus> + <Expires>2008-07-15T01:21:10.000Z</Expires> + <ProviderID>xri://!!1003</ProviderID> + <LocalID>0000.0000.3B9A.CA01</LocalID> + <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01</CanonicalID> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>http://openid.net/signon/1.0</Type> + <URI append='none' priority='1'>https://login.llli.org/server/</URI> + </Service> + <Service> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='null'/> + <Path select='true'>(+contact)</Path> + <Path match='null'/> + <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> + </Service> + <Service priority='1'> + <ProviderID>xri://!!1003!103</ProviderID> + <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> + <Type match='null' select='false'/> + <Path>(+index)</Path> + <Path match='default'/> + <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> + </Service> +</XRD>"; + string webResponse = @"<?xml version='1.0' encoding='UTF-8'?> +<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'> + <Query>*Web</Query> + <Status ceid='off' cid='verified' code='100'/> + <Expires>2008-07-14T02:21:12.000Z</Expires> + <ProviderID>xri://=</ProviderID> + <LocalID>!91f2.8153.f600.ae24</LocalID> + <CanonicalID>=!91F2.8153.F600.AE24</CanonicalID> + <Service priority='10'> + <Type select='true'>xri://+i-service*(+locator)*($v*1.0)</Type> + <Path select='true'>(+locator)</Path> + <MediaType match='default' select='false'/> + <URI append='qxri'>http://locator.fullxri.com/locator/</URI> + </Service> + <Service priority='10'> + <ProviderID>xri://=web</ProviderID> + <Type select='true'>xri://$res*auth*($v*2.0)</Type> + <Type select='true'>xri://$res*auth*($v*2.0)</Type> + <MediaType select='true'>application/xrds+xml</MediaType> + <URI append='qxri' priority='1'>https://resolve.freexri.com/ns/=web/</URI> + <URI append='qxri' priority='2'>http://resolve.freexri.com/ns/=web/</URI> + </Service> + <Service priority='10'> + <Type select='true'>http://openid.net/signon/1.0</Type> + <Type select='true'>http://specs.openid.net/auth/2.0/signon</Type> + <Path select='true'>(+login)</Path> + <Path match='default' select='false'/> + <MediaType match='default' select='false'/> + <URI append='none' priority='2'>http://authn.fullxri.com/authentication/</URI> + <URI append='none' priority='1'>https://authn.fullxri.com/authentication/</URI> + </Service> + <Service priority='10'> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='null' select='false'/> + <Path select='true'>(+contact)</Path> + <Path match='null' select='false'/> + <MediaType match='default' select='false'/> + <URI append='qxri'>http://contact.fullxri.com/contact/</URI> + </Service> + <KeyInfo xmlns='http://www.w3.org/2000/09/xmldsig#'> + <X509Data> + <X509Certificate> +MIIExzCCA6+gAwIBAgIJAM+MlFr0Sth6MA0GCSqGSIb3DQEBBQUAMIGdMR8wHQYD +VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE +CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs +YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs +YWluLmVkdTAeFw0wNjA4MTcxOTU5NTNaFw0xMTA4MTYxOTU5NTNaMIGdMR8wHQYD +VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE +CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs +YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs +YWluLmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL6uFqas4dK6 +A2wTZL0viRQNJrPyFnFBDSZGib/2ijhgzed/vvmZIBM9sFpwahcuR5hvyKUe37/c +/RSZXoNDi/eiNOx4qb0l9UB6bd8qvc4V1PnLE7L+ZYcmwrvTKm4x8qXMgEv1wca2 +FPsreHNPdLiTUZ8v0tDTWi3Mgi7y47VTzJaTkcfmO1nL6xAtln5sLdH0PbMM3LAp +T1d3nwI3VdbhqqZ+6+OKEuC8gk5iH4lfrbr6C9bYS6vzIKrotHpZ3N2aIC3NMjJD +PMw/mfCuADfRNlHXgZW+0zyUkwGTMDea8qgsoAMWJGdeTIw8I1I3RhnbgLzdsNQl +b/1ZXx1uJRUCAwEAAaOCAQYwggECMB0GA1UdDgQWBBQe+xSjYTrlfraJARjMxscb +j36jvDCB0gYDVR0jBIHKMIHHgBQe+xSjYTrlfraJARjMxscbj36jvKGBo6SBoDCB +nTEfMB0GA1UEAxMWU3VwZXJ2aWxsYWluOiBUaGUgUm9vdDELMAkGA1UEBhMCVVMx +ETAPBgNVBAgTCE5ldyBZb3JrMQ8wDQYDVQQHEwZHb3RoYW0xIDAeBgNVBAoTF1N1 +cGVydmlsbGFpbiBVbml2ZXJzaXR5MScwJQYJKoZIhvcNAQkBFhhwZW5ndWluQHN1 +cGVydmlsbGFpbi5lZHWCCQDPjJRa9ErYejAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBBQUAA4IBAQC4SPBDGYAxfbXd8N5OvG0drM7a5hjXfcCZpiILlPSRpxp79yh7 +I5vVWxBxUfolwbei7PTBVy7CE27SUbSICeqWjcDCfjNjiZk6mLS80rm/TdLrHSyM ++Ujlw9MGcBGaLI+sdziDUMtTQDpeAyQTaGVbh1mx5874Hlo1VXqGYNo0RwR+iLfs +x48VuO6GbWVyxtktkE2ypz1KLWiyI056YynydRvuBCBHeRqGUixPlH9CrmeSCP2S +sfbiKnMOGXjIYbvbsTAMdW2iqg6IWa/fgxhvZoAXChM9bkhisJQc0qD0J5TJQwgr +uEyb50RJ7DWmXctSC0b3eymZ2lSXxAWNOsNy + </X509Certificate> + </X509Data> + </KeyInfo> +</XRD>"; + this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> { + { "https://xri.net/@llli?_xrd_r=application/xrd%2Bxml;sep=false", llliResponse }, + { "https://xri.net/@llli*area?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaResponse }, + { "https://xri.net/@llli*area*canada.unattached?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedResponse }, + { "https://xri.net/@llli*area*canada.unattached*ada?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedAdaResponse }, + { "https://xri.net/=Web?_xrd_r=application/xrd%2Bxml;sep=false", webResponse }, + }); + this.VerifyCanonicalId("@llli", "@!72CD.A072.157E.A9C6"); + this.VerifyCanonicalId("@llli*area", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C"); + this.VerifyCanonicalId("@llli*area*canada.unattached", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41"); + this.VerifyCanonicalId("@llli*area*canada.unattached*ada", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01"); + this.VerifyCanonicalId("=Web", "=!91F2.8153.F600.AE24"); + } + + [TestCase] + public void DiscoveryCommunityInameDelegateWithoutCanonicalID() { + this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> { + { "https://xri.net/=Web*andrew.arnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?> +<XRD xmlns='xri://$xrd*($v*2.0)'> + <Query>*andrew.arnott</Query> + <Status cid='absent' code='100'>Success</Status> + <ServerStatus code='100'>Success</ServerStatus> + <Expires>2008-07-14T03:30:59.722Z</Expires> + <ProviderID>=!91F2.8153.F600.AE24</ProviderID> + <Service> + <Type select='true'>http://openid.net/signon/1.0</Type> + <Path select='true'>(+login)</Path> + <Path match='default'/> + <MediaType match='default'/> + <URI append='none' priority='2'>http://www.myopenid.com/server</URI> + <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate> + </Service> + <Service> + <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='null'/> + <Path select='true'>(+contact)</Path> + <Path match='null'/> + <MediaType match='default'/> + <URI append='qxri'>http://contact.freexri.com/contact/</URI> + </Service> + <Service> + <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> + <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> + <Path select='true'>(+index)</Path> + <Path match='default'/> + <MediaType match='default'/> + <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI> + </Service> + <Service> + <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> + <Type select='true'>http://openid.net/signon/1.0</Type> + <Path select='true'>(+login)</Path> + <Path match='default'/> + <MediaType match='default'/> + <URI append='none' priority='2'>http://authn.freexri.com/authentication/</URI> + <URI append='none' priority='1'>https://authn.freexri.com/authentication/</URI> + </Service> + <ServedBy>OpenXRI</ServedBy> +</XRD>" }, + { "https://xri.net/@id*andrewarnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?> +<XRD xmlns='xri://$xrd*($v*2.0)'> + <Query>*andrewarnott</Query> + <Status cid='absent' code='100'>Success</Status> + <ServerStatus code='100'>Success</ServerStatus> + <Expires>2008-07-14T03:31:00.466Z</Expires> + <ProviderID>@!B1E8.C27B.E41C.25C3</ProviderID> + <Service> + <Type select='true'>http://openid.net/signon/1.0</Type> + <Path select='true'>(+login)</Path> + <Path match='default'/> + <MediaType match='default'/> + <URI append='none' priority='2'>http://www.myopenid.com/server</URI> + <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate> + </Service> + <Service> + <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> + <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> + <Type match='null'/> + <Path select='true'>(+contact)</Path> + <Path match='null'/> + <MediaType match='default'/> + <URI append='qxri'>http://contact.freexri.com/contact/</URI> + </Service> + <Service> + <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> + <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> + <Path select='true'>(+index)</Path> + <Path match='default'/> + <MediaType match='default'/> + <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI> + </Service> + <ServedBy>OpenXRI</ServedBy> +</XRD>" }, + }); + // Consistent with spec section 7.3.2.3, we do not permit + // delegation on XRI discovery when there is no CanonicalID present. + this.VerifyCanonicalId("=Web*andrew.arnott", null); + this.VerifyCanonicalId("@id*andrewarnott", null); + } + + private IdentifierDiscoveryResult VerifyCanonicalId(Identifier iname, string expectedClaimedIdentifier) { + var se = this.Discover(iname).FirstOrDefault(); + if (expectedClaimedIdentifier != null) { + Assert.IsNotNull(se); + Assert.AreEqual(expectedClaimedIdentifier, se.ClaimedIdentifier.ToString(), "i-name {0} discovery resulted in unexpected CanonicalId", iname); + Assert.IsTrue(se.Capabilities.Count > 0); + } else { + Assert.IsNull(se); + } + return se; + } + } +} diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs index fa05e94..3433cfa 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs @@ -7,16 +7,16 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AttributeExchangeRoundtripTests : OpenIdTestBase { private const string NicknameTypeUri = WellKnownAttributes.Name.Alias; private const string EmailTypeUri = WellKnownAttributes.Contact.Email; private const string IncrementingAttribute = "http://incatt"; private int incrementingAttributeValue = 1; - [TestMethod] + [TestCase] public void Fetch() { var request = new FetchRequest(); request.Attributes.Add(new AttributeRequest(NicknameTypeUri)); @@ -29,7 +29,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { ExtensionTestUtilities.Roundtrip(Protocol.Default, new[] { request }, new[] { response }); } - [TestMethod] + [TestCase] public void Store() { var request = new StoreRequest(); var newAttribute = new AttributeValues( diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs index 48b5727..5cc8ec1 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs @@ -8,11 +8,11 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using System; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.Test.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AttributeRequestTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void CtorDefault() { AttributeRequest req = new AttributeRequest(); Assert.AreEqual(1, req.Count); @@ -20,27 +20,27 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.IsFalse(req.IsRequired); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorEmptyTypeUri() { new AttributeRequest(string.Empty); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorNullTypeUri() { new AttributeRequest(null); } - [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] + [TestCase, ExpectedException(typeof(ArgumentOutOfRangeException))] public void CtorCountZero() { new AttributeRequest(WellKnownAttributes.Contact.Email, false, 0); } - [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] + [TestCase, ExpectedException(typeof(ArgumentOutOfRangeException))] public void CtorCountNegative() { new AttributeRequest(WellKnownAttributes.Contact.Email, false, -1); } - [TestMethod] + [TestCase] public void CtorFull() { var req = new AttributeRequest(WellKnownAttributes.Contact.Email, true, 5); Assert.AreEqual(WellKnownAttributes.Contact.Email, req.TypeUri); @@ -48,19 +48,19 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual(5, req.Count); } - [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] + [TestCase, ExpectedException(typeof(ArgumentOutOfRangeException))] public void SetCountZero() { var req = new AttributeRequest(); req.Count = 0; } - [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))] + [TestCase, ExpectedException(typeof(ArgumentOutOfRangeException))] public void SetCountNegative() { var req = new AttributeRequest(); req.Count = -1; } - [TestMethod] + [TestCase] public void EqualityTests() { var req1 = new AttributeRequest(); var req2 = new AttributeRequest(); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeValuesTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeValuesTests.cs index 1f7e17c..6c28461 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeValuesTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeValuesTests.cs @@ -10,11 +10,11 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { using System.Linq; using System.Text; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AttributeValuesTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void Ctor() { var att = new AttributeValues(); Assert.IsNull(att.TypeUri); @@ -37,7 +37,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { /// <summary> /// Verifies the Equals method. /// </summary> - [TestMethod] + [TestCase] public void EqualityTests() { var att1 = new AttributeValues(); var att2 = new AttributeValues(); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchRequestTests.cs index 43eba3f..2388798 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchRequestTests.cs @@ -9,35 +9,35 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using System.IO; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.Test.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class FetchRequestTests : OpenIdTestBase { - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void AddAttributeRequestNull() { new FetchRequest().Attributes.Add(null); } - [TestMethod] + [TestCase] public void AddAttributeRequest() { var req = new FetchRequest(); req.Attributes.Add(new AttributeRequest() { TypeUri = "http://someUri" }); } - [TestMethod] + [TestCase] public void AddAttributeRequestStrangeUri() { var req = new FetchRequest(); req.Attributes.Add(new AttributeRequest() { TypeUri = "=someUri*who*knows*but*this*is*legal" }); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void AddAttributeRequestAgain() { var req = new FetchRequest(); req.Attributes.Add(new AttributeRequest() { TypeUri = "http://UriTwice" }); req.Attributes.Add(new AttributeRequest() { TypeUri = "http://UriTwice" }); } - [TestMethod] + [TestCase] public void RespondSimpleValue() { var req = new AttributeRequest(); req.TypeUri = "http://someType"; @@ -47,7 +47,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual("value", resp.Values[0]); } - [TestMethod] + [TestCase] public void RespondTwoValues() { var req = new AttributeRequest(); req.TypeUri = "http://someType"; @@ -59,7 +59,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual("value2", resp.Values[1]); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void RespondTooManyValues() { var req = new AttributeRequest(); req.TypeUri = "http://someType"; @@ -67,7 +67,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { req.Respond("value1", "value2"); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void RespondNull() { var req = new AttributeRequest(); req.TypeUri = "http://someType"; @@ -75,7 +75,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { req.Respond(null); } - [TestMethod] + [TestCase] public void EqualityTests() { var req1 = new FetchRequest(); var req2 = new FetchRequest(); @@ -100,7 +100,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that the class is serializable. /// </summary> - [TestMethod] + [TestCase] public void Serializable() { var fetch = new FetchRequest(); fetch.Attributes.AddRequired("http://someAttribute"); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs index d7082c3..c545a46 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/FetchResponseTests.cs @@ -9,37 +9,37 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using System.IO; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.Test.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class FetchResponseTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void AddAttribute() { var response = new FetchResponse(); response.Attributes.Add(new AttributeValues("http://someattribute", "Value1")); } - [TestMethod] + [TestCase] public void AddTwoAttributes() { var response = new FetchResponse(); response.Attributes.Add(new AttributeValues("http://someattribute", "Value1")); response.Attributes.Add(new AttributeValues("http://someOtherAttribute", "Value2")); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void AddAttributeTwice() { var response = new FetchResponse(); response.Attributes.Add(new AttributeValues("http://someattribute", "Value1")); response.Attributes.Add(new AttributeValues("http://someattribute", "Value1")); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void AddAttributeNull() { var response = new FetchResponse(); response.Attributes.Add(null); } - [TestMethod] + [TestCase] public void GetAttributeValue() { var response = new FetchResponse(); @@ -55,7 +55,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual("a", response.GetAttributeValue("http://someattribute3")); } - [TestMethod] + [TestCase] public void EqualityTests() { var response1 = new FetchResponse(); var response2 = new FetchResponse(); @@ -80,7 +80,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that the class is serializable. /// </summary> - [TestMethod] + [TestCase] public void Serializable() { var fetch = new FetchResponse(); fetch.Attributes.Add("http://someAttribute", "val1", "val2"); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreRequestTests.cs index b11c469..fc486aa 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreRequestTests.cs @@ -12,14 +12,14 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class StoreRequestTests { /// <summary> /// Verifies the constructor behavior. /// </summary> - [TestMethod] + [TestCase] public void Ctor() { var req = new StoreRequest(); Assert.IsFalse(req.Attributes.Any()); @@ -28,7 +28,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { /// <summary> /// Verifies the AddAttribute method. /// </summary> - [TestMethod] + [TestCase] public void AddAttributeByValue() { var req = new StoreRequest(); AttributeValues value = new AttributeValues(); @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { /// <summary> /// Verifies the AddAttribute method. /// </summary> - [TestMethod] + [TestCase] public void AddAttributeByPrimitives() { var req = new StoreRequest(); req.Attributes.Add("http://att1", "value1", "value2"); @@ -51,7 +51,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { /// <summary> /// Verifies the Equals method. /// </summary> - [TestMethod] + [TestCase] public void EqualityTests() { var req1 = new StoreRequest(); var req2 = new StoreRequest(); @@ -71,7 +71,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { /// <summary> /// Verifies that the class is serializable. /// </summary> - [TestMethod] + [TestCase] public void Serializable() { var store = new StoreRequest(); store.Attributes.Add("http://someAttribute", "val1", "val2"); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreResponseTests.cs index 4e432e1..564ef7f 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/StoreResponseTests.cs @@ -7,14 +7,14 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { using System.IO; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class StoreResponseTests { /// <summary> /// Verifies the constructor's behavior. /// </summary> - [TestMethod] + [TestCase] public void Ctor() { var response = new StoreResponse(); Assert.IsTrue(response.Succeeded, "The default status should be Succeeded."); @@ -28,7 +28,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { /// <summary> /// Verifies the Equals method. /// </summary> - [TestMethod] + [TestCase] public void EqualityTests() { var response1 = new StoreResponse(); var response2 = new StoreResponse(); @@ -51,7 +51,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.AttributeExchange { /// <summary> /// Verifies that the class is serializable. /// </summary> - [TestMethod] + [TestCase] public void Serializable() { var store = new StoreResponse(); store.Succeeded = false; diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs index 1fb3160..1b1dd49 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs @@ -14,14 +14,14 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ExtensionsInteropHelperOPTests : OpenIdTestBase { private AuthenticationRequest request; private IList<IExtensionMessage> extensions; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -36,7 +36,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies no extensions appear as no extensions /// </summary> - [TestMethod] + [TestCase] public void NoRequestedExtensions() { var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.request); Assert.IsNull(sreg); @@ -51,7 +51,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreSame(sregResponse, extensions.Single()); } - [TestMethod] + [TestCase] public void NegativeResponse() { this.request.IsAuthenticated = false; ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request); @@ -60,7 +60,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies sreg coming in is seen as sreg. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsAsSregWithSreg() { var sregInjected = new ClaimsRequest(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns) { Nickname = DemandLevel.Request, @@ -81,7 +81,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies AX coming in looks like sreg. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsAsSregWithAX() { this.ParameterizedAXTest(AXAttributeFormats.AXSchemaOrg); } @@ -89,7 +89,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies AX coming in looks like sreg. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsAsSregWithAXSchemaOpenIdNet() { this.ParameterizedAXTest(AXAttributeFormats.SchemaOpenIdNet); } @@ -97,7 +97,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies sreg and AX in one request has a preserved sreg request. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsAsSregWithBothSregAndAX() { var sregInjected = new ClaimsRequest(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns) { Nickname = DemandLevel.Request, diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs index 7edec09..44a629a 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs @@ -5,20 +5,21 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test.OpenId.Extensions { + using System.Collections.ObjectModel; using System.Linq; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ExtensionsInteropHelperRPRequestTests : OpenIdTestBase { private AuthenticationRequest authReq; private ClaimsRequest sreg; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -41,7 +42,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that without an Sreg extension to copy from, no AX extension request is added. /// </summary> - [TestMethod] + [TestCase] public void SpreadSregToAXNoExtensions() { ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.AXSchemaOrg); Assert.AreEqual(0, this.authReq.AppliedExtensions.Count()); @@ -50,7 +51,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that Sreg requests are correctly copied to axschema.org AX requests. /// </summary> - [TestMethod] + [TestCase] public void SpreadSregToAXBasic() { this.authReq.AddExtension(this.sreg); ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.AXSchemaOrg); @@ -69,7 +70,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that sreg can spread to multiple AX schemas. /// </summary> - [TestMethod] + [TestCase] public void SpreadSregToAxMultipleSchemas() { this.authReq.AddExtension(this.sreg); ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.AXSchemaOrg | AXAttributeFormats.SchemaOpenIdNet); @@ -82,7 +83,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies no spread if the OP advertises sreg support. /// </summary> - [TestMethod] + [TestCase] public void SpreadSregToAxNoOpIfOPSupportsSreg() { this.authReq.AddExtension(this.sreg); this.InjectAdvertisedTypeUri(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns); @@ -93,7 +94,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies a targeted AX request if the OP advertises a recognized type URI format. /// </summary> - [TestMethod] + [TestCase] public void SpreadSregToAxTargetedAtOPFormat() { this.authReq.AddExtension(this.sreg); this.InjectAdvertisedTypeUri(WellKnownAttributes.Name.FullName); @@ -106,7 +107,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that TransformAXFormat correctly translates AX schema Type URIs. /// </summary> - [TestMethod] + [TestCase] public void TransformAXFormatTest() { Assert.AreEqual(WellKnownAttributes.Name.Alias, ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.AXSchemaOrg)); Assert.AreEqual("http://schema.openid.net/namePerson/friendly", ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.SchemaOpenIdNet)); @@ -118,11 +119,9 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// </summary> /// <param name="typeUri">The type URI.</param> private void InjectAdvertisedTypeUri(string typeUri) { - var serviceEndpoint = ServiceEndpoint_Accessor.AttachShadow(((ServiceEndpoint)this.authReq.Provider)); - serviceEndpoint.ProviderDescription = ProviderEndpointDescription_Accessor.AttachShadow( - new ProviderEndpointDescription( - serviceEndpoint.ProviderDescription.Endpoint, - serviceEndpoint.ProviderDescription.Capabilities.Concat(new[] { typeUri }))); + var serviceEndpoint = (IdentifierDiscoveryResult)this.authReq.Provider; + serviceEndpoint.SetCapabilitiesForTestHook( + new ReadOnlyCollection<string>(serviceEndpoint.Capabilities.Concat(new[] { typeUri }).ToList())); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs index 655e616..7b528d0 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs @@ -13,14 +13,14 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ExtensionsInteropHelperRPResponseTests : OpenIdTestBase { private IAuthenticationResponse response; private IList<IExtensionMessage> extensions; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -32,7 +32,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that with no extensions present, UnifyExtensionsAsSreg returns an empty ClaimsResponse. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsAsSregNoExtensions() { var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.response, true); Assert.IsNotNull(sreg); @@ -42,7 +42,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies that with sreg and AX extensions present, the sreg extension is returned. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsAsSregWithSreg() { var sregInjected = new ClaimsResponse { Nickname = "andy", @@ -59,7 +59,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies UnifyExtensionsAsSreg correctly converts AX to sreg. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsAsSregFromAXSchemaOrg() { var axInjected = new FetchResponse(); axInjected.Attributes.Add(WellKnownAttributes.Name.Alias, "nate"); @@ -71,7 +71,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// <summary> /// Verifies UnifyExtensionsAsSreg correctly converts AX in a non-standard format to sreg. /// </summary> - [TestMethod] + [TestCase] public void UnifyExtensionsasSregFromSchemaOpenIdNet() { var axInjected = new FetchResponse(); axInjected.Attributes.Add(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.SchemaOpenIdNet), "nate"); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PapeRoundTripTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PapeRoundTripTests.cs index 75737ab..6f35f85 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PapeRoundTripTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PapeRoundTripTests.cs @@ -9,18 +9,18 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; using DotNetOpenAuth.Test.OpenId.Extensions; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PapeRoundTripTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void Trivial() { var request = new PolicyRequest(); var response = new PolicyResponse(); ExtensionTestUtilities.Roundtrip(Protocol.Default, new[] { request }, new[] { response }); } - [TestMethod] + [TestCase] public void Full() { var request = new PolicyRequest(); request.MaximumAuthenticationAge = TimeSpan.FromMinutes(10); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequestTests.cs index 303c747..d75eccc 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequestTests.cs @@ -13,11 +13,11 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PolicyRequestTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void Ctor() { PolicyRequest req = new PolicyRequest(); Assert.IsNull(req.MaximumAuthenticationAge); @@ -25,7 +25,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(0, req.PreferredPolicies.Count); } - [TestMethod] + [TestCase] public void MaximumAuthenticationAgeTest() { PolicyRequest req = new PolicyRequest(); req.MaximumAuthenticationAge = TimeSpan.FromHours(1); @@ -35,7 +35,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.IsNull(req.MaximumAuthenticationAge); } - [TestMethod] + [TestCase] public void AddPolicies() { PolicyRequest resp = new PolicyRequest(); resp.PreferredPolicies.Add(AuthenticationPolicies.MultiFactor); @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(AuthenticationPolicies.PhishingResistant, resp.PreferredPolicies[1]); } - [TestMethod] + [TestCase] public void AddPolicyMultipleTimes() { // Although this isn't really the desired behavior (we'd prefer to see an // exception thrown), since we're using a List<string> internally we can't @@ -57,7 +57,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(2, resp.PreferredPolicies.Count); } - [TestMethod] + [TestCase] public void AddAuthLevelTypes() { PolicyRequest req = new PolicyRequest(); req.PreferredAuthLevelTypes.Add(Constants.AssuranceLevels.NistTypeUri); @@ -65,7 +65,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.IsTrue(req.PreferredAuthLevelTypes.Contains(Constants.AssuranceLevels.NistTypeUri)); } - [TestMethod] + [TestCase] public void EqualsTest() { PolicyRequest req = new PolicyRequest(); PolicyRequest req2 = new PolicyRequest(); @@ -104,7 +104,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(req, req2); } - [TestMethod] + [TestCase] public void Serialize() { PolicyRequest req = new PolicyRequest(); IMessageWithEvents reqEvents = req; diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponseTests.cs index e0faaac..7491e21 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponseTests.cs @@ -12,15 +12,15 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PolicyResponseTests : OpenIdTestBase { private static readonly DateTime someLocalTime = new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Local); private static readonly DateTime someUtcTime = new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Utc); private static readonly DateTime someUnspecifiedTime = new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Unspecified); - [TestMethod] + [TestCase] public void Ctor() { PolicyResponse resp = new PolicyResponse(); Assert.IsNotNull(resp.ActualPolicies); @@ -29,7 +29,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.IsNull(resp.NistAssuranceLevel); } - [TestMethod] + [TestCase] public void AddPolicies() { PolicyResponse resp = new PolicyResponse(); resp.ActualPolicies.Add(AuthenticationPolicies.MultiFactor); @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(AuthenticationPolicies.PhishingResistant, resp.ActualPolicies[1]); } - [TestMethod] + [TestCase] public void AddPolicyMultipleTimes() { // Although this isn't really the desired behavior (we'd prefer to see an // exception thrown), since we're using a List<string> internally we can't @@ -51,7 +51,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(2, resp.ActualPolicies.Count); } - [TestMethod] + [TestCase] public void AuthenticationTimeUtcConvertsToUtc() { PolicyResponse resp = new PolicyResponse(); resp.AuthenticationTimeUtc = someLocalTime; @@ -60,20 +60,20 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(someLocalTime.ToUniversalTime(), resp.AuthenticationTimeUtc.Value); } - [TestMethod] + [TestCase] public void AuthenticationTimeUtcSetUtc() { PolicyResponse resp = new PolicyResponse(); resp.AuthenticationTimeUtc = someUtcTime; Assert.AreEqual(someUtcTime, resp.AuthenticationTimeUtc); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void AuthenticationTimeUtcSetUnspecified() { PolicyResponse resp = new PolicyResponse(); resp.AuthenticationTimeUtc = someUnspecifiedTime; } - [TestMethod] + [TestCase] public void AuthenticationTimeUtcSetNull() { PolicyResponse resp = new PolicyResponse(); resp.AuthenticationTimeUtc = null; @@ -84,7 +84,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.IsNull(resp.AuthenticationTimeUtc); } - [TestMethod] + [TestCase] public void NistAssuranceLevelSetVarious() { PolicyResponse resp = new PolicyResponse(); resp.NistAssuranceLevel = NistAssuranceLevel.Level1; @@ -95,7 +95,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(NistAssuranceLevel.InsufficientForLevel1, resp.NistAssuranceLevel); } - [TestMethod] + [TestCase] public void AssuranceLevels() { PolicyResponse resp = new PolicyResponse(); Assert.AreEqual(0, resp.AssuranceLevels.Count); @@ -108,7 +108,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.IsNull(resp.NistAssuranceLevel); } - [TestMethod] + [TestCase] public void EqualsTest() { PolicyResponse resp = new PolicyResponse(); PolicyResponse resp2 = new PolicyResponse(); @@ -164,7 +164,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.ProviderAuthenticationPolicy { Assert.AreEqual(resp, resp2); } - [TestMethod] + [TestCase] public void Serialize() { PolicyResponse resp = new PolicyResponse(); IMessageWithEvents respEvents = resp; diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs index 3af54d3..f7a948e 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsRequestTests.cs @@ -8,11 +8,11 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ClaimsRequestTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void CreateResponse() { // some unofficial type URIs... this.ParameterizedTypeUriPreservedTest("http://openid.net/sreg/1.0"); @@ -21,7 +21,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { this.ParameterizedTypeUriPreservedTest("http://openid.net/extensions/sreg/1.1"); } - [TestMethod] + [TestCase] public void RequiredOptionalLists() { ClaimsRequest req = new ClaimsRequest(); MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(req); @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual("nickname,postcode", dictionary["required"]); } - [TestMethod] + [TestCase] public void EqualityTests() { ClaimsRequest req1 = new ClaimsRequest(); ClaimsRequest req2 = new ClaimsRequest(); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs index 6dbfa4f..0bdc36e 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs @@ -12,18 +12,18 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ClaimsResponseTests { - [TestMethod] + [TestCase] public void EmptyMailAddress() { ClaimsResponse response = new ClaimsResponse(Constants.sreg_ns); response.Email = string.Empty; Assert.IsNull(response.MailAddress); } - [TestMethod, Ignore] // serialization no longer supported + [TestCase, Ignore("serialization no longer supported")] public void BinarySerialization() { ClaimsResponse fields = this.GetFilledData(); MemoryStream ms = new MemoryStream(); @@ -35,7 +35,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual(fields, fields2); } - [TestMethod, Ignore] // serialization no longer supported + [TestCase, Ignore("serialization no longer supported")] public void XmlSerialization() { ClaimsResponse fields = this.GetFilledData(); MemoryStream ms = new MemoryStream(); @@ -47,7 +47,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual(fields, fields2); } - [TestMethod] + [TestCase] public void EqualityTest() { ClaimsResponse fields1 = this.GetFilledData(); @@ -95,7 +95,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreNotEqual(fields1, fields2); } - [TestMethod] + [TestCase] public void Birthdates() { var response = new ClaimsResponse(); // Verify that they both start out as null @@ -124,7 +124,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.IsFalse(response.BirthDate.HasValue); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void InvalidRawBirthdate() { var response = new ClaimsResponse(); response.BirthDateRaw = "2008"; diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs index 7a60a32..389fa5d 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs @@ -8,19 +8,36 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.UI { using System.Globalization; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Extensions.UI; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class UIRequestTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void Defaults() { - UIRequest request = new UIRequest(); + var request = new UIRequest(); Assert.AreEqual("popup", request.Mode); Assert.AreEqual(1, request.LanguagePreference.Length); Assert.AreEqual(CultureInfo.CurrentUICulture, request.LanguagePreference[0]); + Assert.IsFalse(request.Icon.HasValue); + } + + [TestCase] + public void IconEncodingDecoding() + { + var request = new UIRequest(); + MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(request); + Assert.IsFalse(dictionary.ContainsKey("icon")); + + Assert.IsFalse(request.Icon.HasValue); + dictionary["icon"] = "true"; + Assert.IsTrue(request.Icon.Value); + + dictionary.ClearValues(); + request.Icon = true; + Assert.AreEqual("true", dictionary["icon"]); } - [TestMethod] + [TestCase] public void LanguagePreferenceEncodingDecoding() { var request = new UIRequest(); MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(request); @@ -42,7 +59,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.UI { Assert.AreEqual(new CultureInfo("es-ES"), request.LanguagePreference[1]); } - [TestMethod] + [TestCase] public void ModeEncoding() { var request = new UIRequest(); MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(request); diff --git a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs index cc02265..01e2fdc 100644 --- a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs @@ -9,23 +9,23 @@ namespace DotNetOpenAuth.Test.OpenId { using System.Collections.Generic; using System.Linq; using DotNetOpenAuth.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class IdentifierTests { private string uri = "http://www.yahoo.com/"; private string uriNoScheme = "www.yahoo.com"; private string uriHttps = "https://www.yahoo.com/"; private string xri = "=arnott*andrew"; - [TestMethod] + [TestCase] public void TryParseNoThrow() { Identifier id; Assert.IsFalse(Identifier.TryParse(null, out id)); Assert.IsFalse(Identifier.TryParse(string.Empty, out id)); } - [TestMethod] + [TestCase] public void TryParse() { Identifier id; Assert.IsTrue(Identifier.TryParse("http://host/path", out id)); @@ -34,16 +34,16 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.AreEqual("=arnott", id.ToString()); } - [TestMethod] + [TestCase] public void Parse() { - Assert.IsInstanceOfType(Identifier.Parse(this.uri), typeof(UriIdentifier)); - Assert.IsInstanceOfType(Identifier.Parse(this.xri), typeof(XriIdentifier)); + Assert.IsInstanceOf<UriIdentifier>(Identifier.Parse(this.uri)); + Assert.IsInstanceOf<XriIdentifier>(Identifier.Parse(this.xri)); } /// <summary> /// Tests conformance with 2.0 spec section 7.2#2 /// </summary> - [TestMethod] + [TestCase] public void ParseEndUserSuppliedXriIdentifer() { List<char> symbols = new List<char>(XriIdentifier.GlobalContextSymbols); symbols.Add('('); @@ -52,35 +52,35 @@ namespace DotNetOpenAuth.Test.OpenId { prefixes.AddRange(symbols.Select(s => "xri://" + s.ToString())); foreach (string prefix in prefixes) { var id = Identifier.Parse(prefix + "andrew"); - Assert.IsInstanceOfType(id, typeof(XriIdentifier)); + Assert.IsInstanceOf<XriIdentifier>(id); } } /// <summary> /// Verifies conformance with 2.0 spec section 7.2#3 /// </summary> - [TestMethod] + [TestCase] public void ParseEndUserSuppliedUriIdentifier() { // verify a fully-qualified Uri var id = Identifier.Parse(this.uri); - Assert.IsInstanceOfType(id, typeof(UriIdentifier)); + Assert.IsInstanceOf<UriIdentifier>(id); Assert.AreEqual(this.uri, ((UriIdentifier)id).Uri.AbsoluteUri); // verify an HTTPS Uri id = Identifier.Parse(this.uriHttps); - Assert.IsInstanceOfType(id, typeof(UriIdentifier)); + Assert.IsInstanceOf<UriIdentifier>(id); Assert.AreEqual(this.uriHttps, ((UriIdentifier)id).Uri.AbsoluteUri); // verify that if the scheme is missing it is added automatically id = Identifier.Parse(this.uriNoScheme); - Assert.IsInstanceOfType(id, typeof(UriIdentifier)); + Assert.IsInstanceOf<UriIdentifier>(id); Assert.AreEqual(this.uri, ((UriIdentifier)id).Uri.AbsoluteUri); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void ParseNull() { Identifier.Parse(null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void ParseEmpty() { Identifier.Parse(string.Empty); } diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateDiffieHellmanRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateDiffieHellmanRequestTests.cs index a8648ac..b091062 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateDiffieHellmanRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateDiffieHellmanRequestTests.cs @@ -8,24 +8,24 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using System; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AssociateDiffieHellmanRequestTests { private static readonly Uri Recipient = new Uri("http://host"); private AssociateDiffieHellmanRequest request; - [TestInitialize] + [SetUp] public void Setup() { this.request = new AssociateDiffieHellmanRequest(Protocol.V20.Version, Recipient); } - [TestMethod] + [TestCase] public void Ctor() { Assert.AreEqual(Recipient, this.request.Recipient); } - [TestMethod] + [TestCase] public void Mode() { Assert.AreEqual("associate", this.request.Mode); } diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateRequestTests.cs index 0957118..f3c18d9 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateRequestTests.cs @@ -11,31 +11,31 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AssociateRequestTests : OpenIdTestBase { private readonly Protocol protocol = Protocol.V20; private Uri secureRecipient = new Uri("https://hi"); private Uri insecureRecipient = new Uri("http://hi"); private AssociateRequest request; - [TestInitialize] + [SetUp] public void Setup() { this.request = new AssociateUnencryptedRequest(this.protocol.Version, this.secureRecipient); } - [TestMethod] + [TestCase] public void ConstructorTest() { Assert.AreEqual(this.secureRecipient, this.request.Recipient); } - [TestMethod] + [TestCase] public void Mode() { Assert.AreEqual(this.protocol.Args.Mode.associate, this.request.Mode); } - [TestMethod] + [TestCase] public void MessagePartsTest() { this.request.AssociationType = this.protocol.Args.SignatureAlgorithm.HMAC_SHA1; this.request.SessionType = this.protocol.Args.SessionType.NoEncryption; @@ -51,26 +51,26 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.AreEqual(this.protocol.Args.SessionType.NoEncryption, dict[this.protocol.openid.session_type]); } - [TestMethod] + [TestCase] public void ValidMessageTest() { this.request = new AssociateUnencryptedRequest(Protocol.V20.Version, this.secureRecipient); this.request.AssociationType = this.protocol.Args.SignatureAlgorithm.HMAC_SHA1; this.request.EnsureValidMessage(); } - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void InvalidMessageTest() { this.request = new AssociateUnencryptedRequest(Protocol.V20.Version, this.insecureRecipient); this.request.AssociationType = this.protocol.Args.SignatureAlgorithm.HMAC_SHA1; this.request.EnsureValidMessage(); // no-encryption only allowed for secure channels. } - [TestMethod] + [TestCase] public void RequiredProtection() { Assert.AreEqual(MessageProtections.None, this.request.RequiredProtection); } - [TestMethod] + [TestCase] public void Transport() { Assert.AreEqual(MessageTransport.Direct, this.request.Transport); } @@ -78,7 +78,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { /// <summary> /// Verifies security settings limit RP's initial associate request /// </summary> - [TestMethod] + [TestCase] public void AssociateRequestDeterminedBySecuritySettings() { Protocol protocol = Protocol.V20; SecuritySettings securitySettings = new RelyingPartySecuritySettings(); diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnencryptedResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnencryptedResponseTests.cs index 1f5e87c..37be1cb 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnencryptedResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnencryptedResponseTests.cs @@ -9,20 +9,20 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AssociateUnencryptedResponseTests : OpenIdTestBase { private AssociateUnencryptedResponse response; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); var request = new AssociateUnencryptedRequest(Protocol.V20.Version, new Uri("http://host")); this.response = new AssociateUnencryptedResponse(request.Version, request); } - [TestMethod] + [TestCase] public void ParameterNames() { this.response.AssociationHandle = "HANDLE"; this.response.AssociationType = "HMAC-SHA1"; @@ -38,12 +38,12 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.AreEqual("50", fields["expires_in"]); } - [TestMethod] + [TestCase] public void RequiredProtection() { Assert.AreEqual(MessageProtections.None, this.response.RequiredProtection); } - [TestMethod] + [TestCase] public void Transport() { Assert.AreEqual(MessageTransport.Direct, this.response.Transport); } diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnsuccessfulResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnsuccessfulResponseTests.cs index a6a691d..9b76473 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnsuccessfulResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/AssociateUnsuccessfulResponseTests.cs @@ -9,20 +9,20 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AssociateUnsuccessfulResponseTests : OpenIdTestBase { private AssociateUnsuccessfulResponse response; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); var request = new AssociateUnencryptedRequest(Protocol.V20.Version, new Uri("http://host")); this.response = new AssociateUnsuccessfulResponse(request.Version, request); } - [TestMethod] + [TestCase] public void ParameterNames() { this.response.ErrorMessage = "Some Error"; this.response.AssociationType = "HMAC-SHA1"; diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs index 96a2e23..d2d2cc4 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs @@ -6,16 +6,37 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Messages; + using NUnit.Framework; - [TestClass] + [TestFixture] public class CheckAuthenticationRequestTests : OpenIdTestBase { - [TestInitialize] - public override void SetUp() { - base.SetUp(); + /// <summary> + /// Verifies that the check_auth request is sent preserving EXACTLY (non-normalized) + /// what is in the positive assertion. + /// </summary> + /// <remarks> + /// This is very important because any normalization + /// (like changing https://host:443/ to https://host/) in the message will invalidate the signature + /// and cause the authentication to inappropriately fail. + /// Designed to verify fix to Trac #198. + /// </remarks> + [TestCase] + public void ExactPositiveAssertionPreservation() { + var rp = CreateRelyingParty(true); + + // Initialize the positive assertion response with some data that is NOT in normalized form. + var positiveAssertion = new PositiveAssertionResponse(Protocol.Default.Version, RPUri) + { + ClaimedIdentifier = "https://HOST:443/a", + ProviderEndpoint = new Uri("https://anotherHOST:443/b"), + }; + + var checkAuth = new CheckAuthenticationRequest(positiveAssertion, rp.Channel); + var actual = rp.Channel.MessageDescriptions.GetAccessor(checkAuth); + Assert.AreEqual("https://HOST:443/a", actual["openid.claimed_id"]); + Assert.AreEqual("https://anotherHOST:443/b", actual["openid.op_endpoint"]); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationResponseTests.cs index 1e773bd..cbe6638 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationResponseTests.cs @@ -12,16 +12,16 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class CheckAuthenticationResponseTests : OpenIdTestBase { - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } - [TestMethod] + [TestCase] public void IsValid() { Protocol protocol = Protocol.Default; var request = new CheckAuthenticationRequest(protocol.Version, OPUri); diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs index 2c9ea12..e33a191 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs @@ -12,11 +12,11 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class CheckIdRequestTests : OpenIdTestBase { - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/DirectErrorResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/DirectErrorResponseTests.cs index 02fa7df..039748f 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/DirectErrorResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/DirectErrorResponseTests.cs @@ -10,13 +10,13 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class DirectErrorResponseTests : OpenIdTestBase { private DirectErrorResponse response; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -24,7 +24,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { this.response = new DirectErrorResponse(request.Version, request); } - [TestMethod] + [TestCase] public void ParameterNames() { this.response.ErrorMessage = "Some Error"; this.response.Contact = "Andrew Arnott"; @@ -42,7 +42,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { /// Verifies that error messages are created as HTTP 400 errors, /// per OpenID 2.0 section 5.1.2.2. /// </summary> - [TestMethod] + [TestCase] public void ErrorMessagesAsHttp400() { var httpStatusMessage = (IHttpDirectResponse)this.response; Assert.AreEqual(HttpStatusCode.BadRequest, httpStatusMessage.HttpStatusCode); diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectErrorResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectErrorResponseTests.cs index fdb08eb..c9fb535 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectErrorResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectErrorResponseTests.cs @@ -10,25 +10,25 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class IndirectErrorResponseTests : OpenIdTestBase { private IndirectErrorResponse response; - [TestInitialize] + [SetUp] public void Setup() { CheckIdRequest request = new CheckIdRequest(Protocol.V20.Version, OPUri, AuthenticationRequestMode.Immediate); request.ReturnTo = RPUri; this.response = new IndirectErrorResponse(request); } - [TestMethod] + [TestCase] public void Ctor() { Assert.AreEqual(RPUri, this.response.Recipient); } - [TestMethod] + [TestCase] public void ParameterNames() { this.response.ErrorMessage = "Some Error"; this.response.Contact = "Andrew Arnott"; diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs index 8b0937a..ad6b15d 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs @@ -16,9 +16,9 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class IndirectSignedResponseTests : OpenIdTestBase { private const string CreationDateString = "2005-05-15T17:11:51Z"; private readonly DateTime creationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); @@ -27,7 +27,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { private IndirectSignedResponse unsolicited; private Protocol protocol; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { this.unsolicited = new IndirectSignedResponse(this.protocol.Version, RPUri); } - [TestMethod] + [TestCase] public void CtorFromRequest() { Assert.AreEqual(this.protocol.Args.Mode.id_res, this.response.Mode); Assert.AreEqual(this.request.Version, this.response.Version); @@ -48,7 +48,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.IsTrue(DateTime.UtcNow - ((ITamperResistantOpenIdMessage)this.response).UtcCreationDate < TimeSpan.FromSeconds(5)); } - [TestMethod] + [TestCase] public void CtorUnsolicited() { Assert.AreEqual(this.protocol.Args.Mode.id_res, this.unsolicited.Mode); Assert.AreEqual(this.protocol.Version, this.unsolicited.Version); @@ -60,7 +60,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.AreEqual(OPUri, this.unsolicited.ProviderEndpoint); } - [TestMethod] + [TestCase] public void ResponseNonceSetter() { const string HybridValue = CreationDateString + "UNIQUE"; var responseAccessor = IndirectSignedResponse_Accessor.AttachShadow(this.response); @@ -74,7 +74,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.IsNull(responseReplay.Nonce); } - [TestMethod] + [TestCase] public void ResponseNonceGetter() { var responseAccessor = IndirectSignedResponse_Accessor.AttachShadow(this.response); IReplayProtectedProtocolMessage responseReplay = this.response; @@ -86,7 +86,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.AreEqual(this.creationDate, responseReplay.UtcCreationDate); } - [TestMethod] + [TestCase] public void UtcCreationDateConvertsToUniversal() { IReplayProtectedProtocolMessage responseReplay = this.response; DateTime local = DateTime.Parse("1982-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal); @@ -109,7 +109,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.AreEqual(this.creationDate.Hour, utcCreationDate.Hour, "The hour should match since both times are UTC time."); } - [TestMethod] + [TestCase] public void ReturnToDoesNotMatchRecipient() { // Make sure its valid first, so we know that when it's invalid // it is due to our tampering. @@ -127,24 +127,24 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { } } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void GetReturnToArgumentNullKey() { this.response.GetReturnToArgument(null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void GetReturnToArgumentEmptyKey() { this.response.GetReturnToArgument(string.Empty); } - [TestMethod] + [TestCase] public void GetReturnToArgumentDoesNotReturnExtraArgs() { this.response.ExtraData["a"] = "b"; Assert.IsNull(this.response.GetReturnToArgument("a")); Assert.AreEqual(0, this.response.GetReturnToParameterNames().Count()); } - [TestMethod] + [TestCase] public void GetReturnToArgumentAndNames() { UriBuilder returnToBuilder = new UriBuilder(this.response.ReturnTo); returnToBuilder.AppendQueryArgs(new Dictionary<string, string> { { "a", "b" } }); diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/NegativeAssertionResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/NegativeAssertionResponseTests.cs index 7876732..c35780d 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/NegativeAssertionResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/NegativeAssertionResponseTests.cs @@ -13,16 +13,16 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class NegativeAssertionResponseTests : OpenIdTestBase { - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } - [TestMethod] + [TestCase] public void Mode() { var setupRequestV1 = new CheckIdRequest(Protocol.V10.Version, OPUri, AuthenticationRequestMode.Setup); setupRequestV1.ReturnTo = RPUri; @@ -45,14 +45,14 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.IsFalse(new NegativeAssertionResponse(setupRequestV2).Immediate); } - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void UserSetupUrlRequiredInV1Immediate() { var immediateRequestV1 = new CheckIdRequest(Protocol.V10.Version, OPUri, AuthenticationRequestMode.Immediate); immediateRequestV1.ReturnTo = RPUri; new NegativeAssertionResponse(immediateRequestV1).EnsureValidMessage(); } - [TestMethod] + [TestCase] public void UserSetupUrlSetForV1Immediate() { var immediateRequestV1 = new CheckIdRequest(Protocol.V10.Version, OPUri, AuthenticationRequestMode.Immediate); immediateRequestV1.ReturnTo = RPUri; @@ -61,7 +61,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { response.EnsureValidMessage(); } - [TestMethod] + [TestCase] public void UserSetupUrlNotRequiredInV1SetupOrV2() { var setupRequestV1 = new CheckIdRequest(Protocol.V10.Version, OPUri, AuthenticationRequestMode.Setup); setupRequestV1.ReturnTo = RPUri; diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs index faa6a91..90d24b1 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs @@ -15,9 +15,9 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PositiveAssertionResponseTests : OpenIdTestBase { private const string CreationDateString = "2005-05-15T17:11:51Z"; private readonly DateTime creationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); @@ -26,7 +26,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { private PositiveAssertionResponse unsolicited; private Protocol protocol; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -38,7 +38,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { this.unsolicited = new PositiveAssertionResponse(this.protocol.Version, RPUri); } - [TestMethod] + [TestCase] public void CtorFromRequest() { Assert.AreEqual(this.protocol.Args.Mode.id_res, this.response.Mode); Assert.AreEqual(this.request.Version, this.response.Version); @@ -46,7 +46,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { Assert.AreEqual(OPUri, this.response.ProviderEndpoint); } - [TestMethod] + [TestCase] public void CtorUnsolicited() { Assert.AreEqual(this.protocol.Args.Mode.id_res, this.unsolicited.Mode); Assert.AreEqual(this.protocol.Version, this.unsolicited.Version); @@ -60,7 +60,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { /// <summary> /// Verifies that local_id and claimed_id can either be null or specified. /// </summary> - [TestMethod] + [TestCase] public void ClaimedIdAndLocalIdSpecifiedIsValid() { this.response.LocalIdentifier = "http://local"; this.response.ClaimedIdentifier = "http://claimedid"; diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/SignedResponseRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/SignedResponseRequestTests.cs index 0177685..bc95f8b 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Messages/SignedResponseRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Messages/SignedResponseRequestTests.cs @@ -10,9 +10,9 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class SignedResponseRequestTests : OpenIdTestBase { private Uri providerEndpoint; private SignedResponseRequest immediatev1; @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { private SignedResponseRequest immediatev2; private SignedResponseRequest setupv2; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -55,7 +55,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { /// This test does not test all the realm-return_to matching rules as that is done in the Realm tests. /// This test merely checks that the compatibility match occurs at all. /// </remarks> - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void RealmReturnToMismatchV2() { this.setupv2.Realm = "http://somehost/"; this.setupv2.ReturnTo = new Uri("http://someotherhost/"); @@ -66,7 +66,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages { /// Verifies that the validation check throws if the return_to and the realm /// values are not compatible. /// </summary> - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void RealmReturnToMismatchV1() { this.setupv1.Realm = "http://somehost/"; this.setupv1.ReturnTo = new Uri("http://someotherhost/"); diff --git a/src/DotNetOpenAuth.Test/OpenId/NonIdentityTests.cs b/src/DotNetOpenAuth.Test/OpenId/NonIdentityTests.cs index 49bb32c..17ab090 100644 --- a/src/DotNetOpenAuth.Test/OpenId/NonIdentityTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/NonIdentityTests.cs @@ -9,11 +9,11 @@ namespace DotNetOpenAuth.Test.OpenId { using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class NonIdentityTests : OpenIdTestBase { - [TestMethod] + [TestCase] public void ExtensionOnlyChannelLevel() { Protocol protocol = Protocol.V20; AuthenticationRequestMode mode = AuthenticationRequestMode.Setup; @@ -25,12 +25,12 @@ namespace DotNetOpenAuth.Test.OpenId { }, op => { var request = op.Channel.ReadFromRequest<SignedResponseRequest>(); - Assert.IsNotInstanceOfType(request, typeof(CheckIdRequest)); + Assert.IsNotInstanceOf<CheckIdRequest>(request); }); coordinator.Run(); } - [TestMethod] + [TestCase] public void ExtensionOnlyFacadeLevel() { Protocol protocol = Protocol.V20; var coordinator = new OpenIdCoordinator( @@ -48,7 +48,7 @@ namespace DotNetOpenAuth.Test.OpenId { var request = (IAnonymousRequest)op.GetRequest(); request.IsApproved = true; - Assert.IsNotInstanceOfType(request, typeof(CheckIdRequest)); + Assert.IsNotInstanceOf<CheckIdRequest>(request); op.SendResponse(request); }); coordinator.Run(); diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs index 0f9d472..d4884e8 100644 --- a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs +++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs @@ -57,10 +57,12 @@ namespace DotNetOpenAuth.Test.OpenId { private void EnsurePartiesAreInitialized() { if (this.RelyingParty == null) { this.RelyingParty = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore()); + this.RelyingParty.DiscoveryServices.Add(new MockIdentifierDiscoveryService()); } if (this.Provider == null) { this.Provider = new OpenIdProvider(new StandardProviderApplicationStore()); + this.Provider.DiscoveryServices.Add(new MockIdentifierDiscoveryService()); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs index 5034b7e..c7df99a 100644 --- a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs +++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.Test.OpenId { using System; + using System.Collections.Generic; using System.IO; using System.Reflection; using DotNetOpenAuth.Configuration; @@ -15,7 +16,7 @@ namespace DotNetOpenAuth.Test.OpenId { using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; public class OpenIdTestBase : TestBase { internal IDirectWebRequestHandler RequestHandler; @@ -61,7 +62,7 @@ namespace DotNetOpenAuth.Test.OpenId { protected ProviderSecuritySettings ProviderSecuritySettings { get; private set; } - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -74,7 +75,7 @@ namespace DotNetOpenAuth.Test.OpenId { Identifier.EqualityOnStrings = true; } - [TestCleanup] + [TearDown] public override void Cleanup() { base.Cleanup(); @@ -116,17 +117,17 @@ namespace DotNetOpenAuth.Test.OpenId { } } - internal static ServiceEndpoint GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl) { + internal static IdentifierDiscoveryResult GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl) { return GetServiceEndpoint(user, providerVersion, servicePriority, useSsl, false); } - internal static ServiceEndpoint GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl, bool delegating) { + internal static IdentifierDiscoveryResult GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl, bool delegating) { var providerEndpoint = new ProviderEndpointDescription( useSsl ? OpenIdTestBase.OPUriSsl : OpenIdTestBase.OPUri, new string[] { Protocol.Lookup(providerVersion).ClaimedIdentifierServiceTypeURI }); var local_id = useSsl ? OPLocalIdentifiersSsl[user] : OPLocalIdentifiers[user]; var claimed_id = delegating ? (useSsl ? VanityUriSsl : VanityUri) : local_id; - return ServiceEndpoint.CreateForClaimedIdentifier( + return IdentifierDiscoveryResult.CreateForClaimedIdentifier( claimed_id, claimed_id, local_id, @@ -176,6 +177,12 @@ namespace DotNetOpenAuth.Test.OpenId { } } + internal IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier) { + var rp = this.CreateRelyingParty(true); + rp.Channel.WebRequestHandler = this.RequestHandler; + return rp.Discover(identifier); + } + protected Realm GetMockRealm(bool useSsl) { var rpDescription = new RelyingPartyEndpointDescription(useSsl ? RPUriSsl : RPUri, new string[] { Protocol.V20.RPReturnToTypeURI }); return new MockRealm(useSsl ? RPRealmUriSsl : RPRealmUri, rpDescription); @@ -190,9 +197,21 @@ namespace DotNetOpenAuth.Test.OpenId { } protected Identifier GetMockIdentifier(ProtocolVersion providerVersion, bool useSsl, bool delegating) { - ServiceEndpoint se = GetServiceEndpoint(0, providerVersion, 10, useSsl, delegating); + var se = GetServiceEndpoint(0, providerVersion, 10, useSsl, delegating); UriIdentifier identityUri = (UriIdentifier)se.ClaimedIdentifier; - return new MockIdentifier(identityUri, this.MockResponder, new ServiceEndpoint[] { se }); + return new MockIdentifier(identityUri, this.MockResponder, new IdentifierDiscoveryResult[] { se }); + } + + protected Identifier GetMockDualIdentifier() { + Protocol protocol = Protocol.Default; + var opDesc = new ProviderEndpointDescription(OPUri, protocol.Version); + var dualResults = new IdentifierDiscoveryResult[] { + IdentifierDiscoveryResult.CreateForClaimedIdentifier(VanityUri.AbsoluteUri, OPLocalIdentifiers[0], opDesc, 10, 10), + IdentifierDiscoveryResult.CreateForProviderIdentifier(protocol.ClaimedIdentifierForOPIdentifier, opDesc, 20, 20), + }; + + Identifier dualId = new MockIdentifier(VanityUri, this.MockResponder, dualResults); + return dualId; } /// <summary> @@ -211,6 +230,7 @@ namespace DotNetOpenAuth.Test.OpenId { protected OpenIdRelyingParty CreateRelyingParty(bool stateless) { var rp = new OpenIdRelyingParty(stateless ? null : new StandardRelyingPartyApplicationStore()); rp.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; + rp.DiscoveryServices.Add(new MockIdentifierDiscoveryService()); return rp; } @@ -221,6 +241,7 @@ namespace DotNetOpenAuth.Test.OpenId { protected OpenIdProvider CreateProvider() { var op = new OpenIdProvider(new StandardProviderApplicationStore()); op.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; + op.DiscoveryServices.Add(new MockIdentifierDiscoveryService()); return op; } } diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdUtilitiesTests.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdUtilitiesTests.cs new file mode 100644 index 0000000..fefb852 --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdUtilitiesTests.cs @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------- +// <copyright file="OpenIdUtilitiesTests.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.OpenId { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + using DotNetOpenAuth.OpenId.Messages; + using NUnit.Framework; + + [TestFixture] + public class OpenIdUtilitiesTests : OpenIdTestBase { + } +} diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs index 14fef91..1df4c9e 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs @@ -9,14 +9,14 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AnonymousRequestTests : OpenIdTestBase { /// <summary> /// Verifies that IsApproved controls which response message is returned. /// </summary> - [TestMethod] + [TestCase] public void IsApprovedDeterminesReturnedMessage() { var op = CreateProvider(); Protocol protocol = Protocol.V20; @@ -27,11 +27,11 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { Assert.IsFalse(anonReq.IsApproved.HasValue); anonReq.IsApproved = false; - Assert.IsInstanceOfType(anonReq.Response, typeof(NegativeAssertionResponse)); + Assert.IsInstanceOf<NegativeAssertionResponse>(anonReq.Response); anonReq.IsApproved = true; - Assert.IsInstanceOfType(anonReq.Response, typeof(IndirectSignedResponse)); - Assert.IsNotInstanceOfType(anonReq.Response, typeof(PositiveAssertionResponse)); + Assert.IsInstanceOf<IndirectSignedResponse>(anonReq.Response); + Assert.IsNotInstanceOf<PositiveAssertionResponse>(anonReq.Response); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs index accbd97..dc5b3e3 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs @@ -10,14 +10,14 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AuthenticationRequestTest : OpenIdTestBase { /// <summary> /// Verifies the user_setup_url is set properly for immediate negative responses. /// </summary> - [TestMethod] + [TestCase] public void UserSetupUrl() { // Construct a V1 immediate request Protocol protocol = Protocol.V11; diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/HostProcessedRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/HostProcessedRequestTests.cs index d308271..9bb8095 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/HostProcessedRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/HostProcessedRequestTests.cs @@ -9,16 +9,16 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class HostProcessedRequestTests : OpenIdTestBase { private Protocol protocol; private OpenIdProvider provider; private CheckIdRequest checkIdRequest; private AuthenticationRequest request; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -30,12 +30,12 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { this.request = new AuthenticationRequest(this.provider, this.checkIdRequest); } - [TestMethod] + [TestCase] public void IsReturnUrlDiscoverableNoResponse() { Assert.AreEqual(RelyingPartyDiscoveryResult.NoServiceDocument, this.request.IsReturnUrlDiscoverable(this.provider)); } - [TestMethod] + [TestCase] public void IsReturnUrlDiscoverableValidResponse() { this.MockResponder.RegisterMockRPDiscovery(); this.request = new AuthenticationRequest(this.provider, this.checkIdRequest); @@ -46,7 +46,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// Verifies that when discovery would be performed over standard HTTP and RequireSsl /// is set, that discovery fails. /// </summary> - [TestMethod] + [TestCase] public void IsReturnUrlDiscoverableNotSsl() { this.provider.SecuritySettings.RequireSsl = true; this.MockResponder.RegisterMockRPDiscovery(); @@ -56,7 +56,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// <summary> /// Verifies that when discovery would be performed over HTTPS that discovery succeeds. /// </summary> - [TestMethod] + [TestCase] public void IsReturnUrlDiscoverableRequireSsl() { this.MockResponder.RegisterMockRPDiscovery(); this.checkIdRequest.Realm = RPRealmUriSsl; @@ -73,7 +73,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { Assert.AreEqual(RelyingPartyDiscoveryResult.Success, this.request.IsReturnUrlDiscoverable(this.provider)); } - [TestMethod] + [TestCase] public void IsReturnUrlDiscoverableValidButNoMatch() { this.MockResponder.RegisterMockRPDiscovery(); this.provider.SecuritySettings.RequireSsl = false; // reset for another failure test case diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs index 8528aa7..75d871c 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs @@ -15,13 +15,13 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Hosting; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class OpenIdProviderTests : OpenIdTestBase { private OpenIdProvider provider; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -31,7 +31,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// <summary> /// Verifies that the constructor throws an exception if the app store is null. /// </summary> - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNull() { new OpenIdProvider(null); } @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// <summary> /// Verifies that the SecuritySettings property throws when set to null. /// </summary> - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SecuritySettingsSetNull() { this.provider.SecuritySettings = null; } @@ -47,25 +47,25 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// <summary> /// Verifies the SecuritySettings property can be set to a new instance. /// </summary> - [TestMethod] + [TestCase] public void SecuritySettings() { var newSettings = new ProviderSecuritySettings(); this.provider.SecuritySettings = newSettings; Assert.AreSame(newSettings, this.provider.SecuritySettings); } - [TestMethod] + [TestCase] public void ExtensionFactories() { var factories = this.provider.ExtensionFactories; Assert.IsNotNull(factories); Assert.AreEqual(1, factories.Count); - Assert.IsInstanceOfType(factories[0], typeof(StandardOpenIdExtensionFactory)); + Assert.IsInstanceOf<StandardOpenIdExtensionFactory>(factories[0]); } /// <summary> /// Verifies the Channel property. /// </summary> - [TestMethod] + [TestCase] public void ChannelGetter() { Assert.IsNotNull(this.provider.Channel); } @@ -73,7 +73,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// <summary> /// Verifies the GetRequest method throws outside an HttpContext. /// </summary> - [TestMethod, ExpectedException(typeof(InvalidOperationException))] + [TestCase, ExpectedException(typeof(InvalidOperationException))] public void GetRequestNoContext() { HttpContext.Current = null; this.provider.GetRequest(); @@ -82,7 +82,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// <summary> /// Verifies GetRequest throws on null input. /// </summary> - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void GetRequestNull() { this.provider.GetRequest(null); } @@ -90,7 +90,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { /// <summary> /// Verifies that GetRequest correctly returns the right messages. /// </summary> - [TestMethod] + [TestCase] public void GetRequest() { HttpRequestInfo httpInfo = new HttpRequestInfo(); httpInfo.UrlBeforeRewriting = new Uri("http://someUri"); @@ -104,13 +104,13 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { }, op => { IRequest request = op.GetRequest(); - Assert.IsInstanceOfType(request, typeof(AutoResponsiveRequest)); + Assert.IsInstanceOf<AutoResponsiveRequest>(request); op.SendResponse(request); }); coordinator.Run(); } - [TestMethod] + [TestCase] public void BadRequestsGenerateValidErrorResponses() { var coordinator = new OpenIdCoordinator( rp => { @@ -127,7 +127,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { coordinator.Run(); } - [TestMethod] + [TestCase, Category("HostASPNET")] public void BadRequestsGenerateValidErrorResponsesHosted() { try { using (AspNetHost host = AspNetHost.CreateHost(TestWebDirectory)) { diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs index 9f4727d..7984b58 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs @@ -18,28 +18,28 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture, Category("Performance")] public class PerformanceTests : OpenIdTestBase { private const string SharedAssociationHandle = "handle"; private static readonly TimeSpan TestRunTime = TimeSpan.FromSeconds(3); private OpenIdProvider provider; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); SuspendLogging(); this.provider = CreateProvider(); } - [TestCleanup] + [TearDown] public override void Cleanup() { ResumeLogging(); base.Cleanup(); } - [TestMethod] + [TestCase] public void AssociateDH() { var associateRequest = this.CreateAssociateRequest(OPUri); Stopwatch timer = new Stopwatch(); @@ -48,15 +48,15 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) { IRequest request = this.provider.GetRequest(associateRequest); var response = this.provider.PrepareResponse(request); - Assert.IsInstanceOfType(response.OriginalMessage, typeof(AssociateSuccessfulResponse)); + Assert.IsInstanceOf<AssociateSuccessfulResponse>(response.OriginalMessage); } timer.Stop(); double executionsPerSecond = GetExecutionsPerSecond(iterations, timer); - TestContext.WriteLine("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond); + TestUtilities.TestLogger.InfoFormat("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond); Assert.IsTrue(executionsPerSecond >= 2, "Too slow ({0} >= 2 executions per second required.)", executionsPerSecond); } - [TestMethod] + [TestCase] public void AssociateClearText() { var associateRequest = this.CreateAssociateRequest(OPUriSsl); // SSL will cause a plaintext association Stopwatch timer = new Stopwatch(); @@ -65,29 +65,29 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) { IRequest request = this.provider.GetRequest(associateRequest); var response = this.provider.PrepareResponse(request); - Assert.IsInstanceOfType(response.OriginalMessage, typeof(AssociateSuccessfulResponse)); + Assert.IsInstanceOf<AssociateSuccessfulResponse>(response.OriginalMessage); } timer.Stop(); double executionsPerSecond = GetExecutionsPerSecond(iterations, timer); - TestContext.WriteLine("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond); + TestUtilities.TestLogger.InfoFormat("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond); Assert.IsTrue(executionsPerSecond > 1000, "Too slow ({0} > 1000 executions per second required.)", executionsPerSecond); } - [TestMethod] + [TestCase] public void CheckIdSharedHmacSha1Association() { Protocol protocol = Protocol.Default; string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA1; double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType); - TestContext.WriteLine("{0} executions per second.", executionsPerSecond); + TestUtilities.TestLogger.InfoFormat("{0} executions per second.", executionsPerSecond); Assert.IsTrue(executionsPerSecond > 500, "Too slow ({0} > 500 executions per second required.)", executionsPerSecond); } - [TestMethod] + [TestCase] public void CheckIdSharedHmacSha256Association() { Protocol protocol = Protocol.Default; string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA256; double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType); - TestContext.WriteLine("{0} executions per second.", executionsPerSecond); + TestUtilities.TestLogger.InfoFormat("{0} executions per second.", executionsPerSecond); Assert.IsTrue(executionsPerSecond > 400, "Too slow ({0} > 400 executions per second required.)", executionsPerSecond); } @@ -110,11 +110,11 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { var request = (IAuthenticationRequest)this.provider.GetRequest(checkidRequest); request.IsAuthenticated = true; var response = this.provider.PrepareResponse(request); - Assert.IsInstanceOfType(response.OriginalMessage, typeof(PositiveAssertionResponse)); + Assert.IsInstanceOf<PositiveAssertionResponse>(response.OriginalMessage); } timer.Stop(); double executionsPerSecond = GetExecutionsPerSecond(iterations, timer); - TestContext.WriteLine("Responded to {0} checkid messages in {1}; or {2} authentications per second.", iterations, timer.Elapsed, executionsPerSecond); + TestUtilities.TestLogger.InfoFormat("Responded to {0} checkid messages in {1}; or {2} authentications per second.", iterations, timer.Elapsed, executionsPerSecond); return executionsPerSecond; } diff --git a/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs b/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs index 089265f..65185fc 100644 --- a/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs @@ -6,57 +6,23 @@ namespace DotNetOpenAuth.Test.OpenId { using System; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class ProviderEndpointDescriptionTests : OpenIdTestBase { - private ProviderEndpointDescription se; - - private string[] v20TypeUris = { Protocol.V20.ClaimedIdentifierServiceTypeURI }; - - [TestInitialize] - public override void SetUp() { - base.SetUp(); - - this.se = new ProviderEndpointDescription(OPUri, Protocol.V20.Version); - } - - [TestMethod, ExpectedException(typeof(ArgumentNullException))] - public void IsExtensionSupportedNullType() { - this.se.IsExtensionSupported((Type)null); - } - - [TestMethod, ExpectedException(typeof(ArgumentException))] - public void IsExtensionSupportedNullString() { - this.se.IsExtensionSupported((string)null); - } - - [TestMethod, ExpectedException(typeof(ArgumentException))] - public void IsExtensionSupportedEmptyString() { - this.se.IsExtensionSupported(string.Empty); + [TestCase] + public void NonNullCapabilities() { + var epd = new ProviderEndpointDescription(OPUri, Protocol.Default.Version); + Assert.IsNotNull(epd.Capabilities); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] - public void IsExtensionSupportedNullExtension() { - this.se.IsExtensionSupported((IOpenIdMessageExtension)null); - } - - [TestMethod] - public void IsExtensionSupported() { - this.se = new ProviderEndpointDescription(OPUri, this.v20TypeUris); - Assert.IsFalse(this.se.IsExtensionSupported<ClaimsRequest>()); - Assert.IsFalse(this.se.IsExtensionSupported(new ClaimsRequest())); - Assert.IsFalse(this.se.IsExtensionSupported("http://someextension/typeuri")); - - this.se = new ProviderEndpointDescription( - OPUri, - new[] { Protocol.V20.ClaimedIdentifierServiceTypeURI, "http://someextension", Constants.sreg_ns }); - Assert.IsTrue(this.se.IsExtensionSupported<ClaimsRequest>()); - Assert.IsTrue(this.se.IsExtensionSupported(new ClaimsRequest())); - Assert.IsTrue(this.se.IsExtensionSupported("http://someextension")); + [TestCase, ExpectedException(typeof(ProtocolException))] + public void ProtocolDetectionWithoutClues() { + new ProviderEndpointDescription(OPUri, new[] { Protocol.V20.HtmlDiscoveryLocalIdKey }); // random type URI irrelevant to detection } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs b/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs index e062e12..fceb15a 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs @@ -7,11 +7,11 @@ namespace DotNetOpenAuth.Test { using System; using DotNetOpenAuth.OpenId; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class RealmTests { - [TestMethod] + [TestCase] public void ValidRealmsTest() { // Just create these. If any are determined to be invalid, // an exception should be thrown that would fail this test. @@ -26,67 +26,67 @@ namespace DotNetOpenAuth.Test { new Realm("http://*.guest.myopenid.com/"); } - [TestMethod] + [TestCase] [ExpectedException(typeof(ArgumentNullException))] public void InvalidRealmNullString() { new Realm((string)null); } - [TestMethod] + [TestCase] [ExpectedException(typeof(ArgumentNullException))] public void InvalidRealmNullUri() { new Realm((Uri)null); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmEmpty() { new Realm(string.Empty); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmBadProtocol() { new Realm("asdf://www.microsoft.com/"); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmNoScheme() { new Realm("www.guy.com"); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmBadWildcard1() { new Realm("http://*www.my.com"); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmBadWildcard2() { new Realm("http://www.*.com"); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmBadWildcard3() { new Realm("http://www.my.*/"); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmTwoWildcards1() { new Realm("http://**.my.com"); } - [TestMethod] + [TestCase] [ExpectedException(typeof(UriFormatException))] public void InvalidRealmTwoWildcards2() { new Realm("http://*.*.my.com"); } - [TestMethod] + [TestCase] public void IsSaneTest() { Assert.IsTrue(new Realm("http://www.myopenid.com").IsSane); Assert.IsTrue(new Realm("http://myopenid.com").IsSane); @@ -98,7 +98,7 @@ namespace DotNetOpenAuth.Test { Assert.IsFalse(new Realm("http://*.co.uk").IsSane); } - [TestMethod] + [TestCase] public void IsUrlWithinRealmTests() { /* * The openid.return_to URL MUST descend from the openid.trust_root, or the @@ -166,7 +166,7 @@ namespace DotNetOpenAuth.Test { Assert.IsFalse(new Realm("http://www.my.com/abc").Contains("http://www.my.com/ABC")); } - [TestMethod] + [TestCase] public void ImplicitConversionFromStringTests() { Realm realm = "http://host"; Assert.AreEqual("host", realm.Host); @@ -174,7 +174,7 @@ namespace DotNetOpenAuth.Test { Assert.IsNull(realm); } - [TestMethod] + [TestCase] public void ImplicitConversionToStringTests() { Realm realm = new Realm("http://host/"); string realmString = realm; @@ -184,7 +184,7 @@ namespace DotNetOpenAuth.Test { Assert.IsNull(realmString); } - [TestMethod] + [TestCase] public void ImplicitConverstionFromUriTests() { Uri uri = new Uri("http://host"); Realm realm = uri; @@ -194,7 +194,7 @@ namespace DotNetOpenAuth.Test { Assert.IsNull(realm); } - [TestMethod] + [TestCase] public void EqualsTest() { Realm testRealm1a = new Realm("http://www.yahoo.com"); Realm testRealm1b = new Realm("http://www.yahoo.com"); diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs index 0ddc76b..001f3fa 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs @@ -16,9 +16,10 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using DotNetOpenAuth.Test.Mocks; + using NUnit.Framework; - [TestClass] + [TestFixture] public class AuthenticationRequestTests : OpenIdTestBase { private readonly Realm realm = new Realm("http://localhost/rp.aspx"); private readonly Identifier claimedId = "http://claimedId"; @@ -26,7 +27,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { private readonly Protocol protocol = Protocol.Default; private Uri returnTo; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); this.returnTo = new Uri("http://localhost/rp.aspx"); @@ -35,9 +36,9 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies IsDirectedIdentity returns true when appropriate. /// </summary> - [TestMethod] + [TestCase] public void IsDirectedIdentity() { - IAuthenticationRequest_Accessor iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); + var iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); Assert.IsFalse(iauthRequest.IsDirectedIdentity); iauthRequest = this.CreateAuthenticationRequest(IdentifierSelect, IdentifierSelect); @@ -47,9 +48,9 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies ClaimedIdentifier behavior. /// </summary> - [TestMethod] + [TestCase] public void ClaimedIdentifier() { - IAuthenticationRequest_Accessor iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.delegatedLocalId); + var iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.delegatedLocalId); Assert.AreEqual(this.claimedId, iauthRequest.ClaimedIdentifier); iauthRequest = this.CreateAuthenticationRequest(IdentifierSelect, IdentifierSelect); @@ -59,16 +60,16 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies ProviderVersion behavior. /// </summary> - [TestMethod] + [TestCase] public void ProviderVersion() { var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); - Assert.AreEqual(this.protocol.Version, authRequest.endpoint.Protocol.Version); + Assert.AreEqual(this.protocol.Version, authRequest.DiscoveryResult.Version); } /// <summary> /// Verifies RedirectingResponse. /// </summary> - [TestMethod] + [TestCase] public void CreateRequestMessage() { OpenIdCoordinator coordinator = new OpenIdCoordinator( rp => { @@ -106,7 +107,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies that delegating authentication requests are filtered out when configured to do so. /// </summary> - [TestMethod] + [TestCase] public void CreateFiltersDelegatingIdentifiers() { Identifier id = GetMockIdentifier(ProtocolVersion.V20, false, true); var rp = CreateRelyingParty(); @@ -122,9 +123,9 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies the Provider property returns non-null. /// </summary> - [TestMethod] + [TestCase] public void Provider() { - IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); + var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); Assert.IsNotNull(authRequest.Provider); Assert.AreEqual(OPUri, authRequest.Provider.Uri); Assert.AreEqual(this.protocol.Version, authRequest.Provider.Version); @@ -133,9 +134,9 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies that AddCallbackArguments adds query arguments to the return_to URL of the message. /// </summary> - [TestMethod] + [TestCase] public void AddCallbackArgument() { - IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); + var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); Assert.AreEqual(this.returnTo, authRequest.ReturnToUrl); authRequest.AddCallbackArguments("p1", "v1"); var req = (SignedResponseRequest)authRequest.RedirectingResponse.OriginalMessage; @@ -147,12 +148,12 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// Verifies that AddCallbackArguments replaces pre-existing parameter values /// rather than appending them. /// </summary> - [TestMethod] + [TestCase] public void AddCallbackArgumentClearsPreviousArgument() { UriBuilder returnToWithArgs = new UriBuilder(this.returnTo); returnToWithArgs.AppendQueryArgs(new Dictionary<string, string> { { "p1", "v1" } }); this.returnTo = returnToWithArgs.Uri; - IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); + var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); authRequest.AddCallbackArguments("p1", "v2"); var req = (SignedResponseRequest)authRequest.RedirectingResponse.OriginalMessage; NameValueCollection query = HttpUtility.ParseQueryString(req.ReturnTo.Query); @@ -162,32 +163,48 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies identity-less checkid_* request behavior. /// </summary> - [TestMethod] + [TestCase] public void NonIdentityRequest() { - IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); + var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId); authRequest.IsExtensionOnly = true; Assert.IsTrue(authRequest.IsExtensionOnly); var req = (SignedResponseRequest)authRequest.RedirectingResponse.OriginalMessage; - Assert.IsNotInstanceOfType(req, typeof(CheckIdRequest), "An unexpected SignedResponseRequest derived type was generated."); + Assert.IsNotInstanceOf<CheckIdRequest>(req, "An unexpected SignedResponseRequest derived type was generated."); + } + + /// <summary> + /// Verifies that discovery on identifiers that serve as OP identifiers and claimed identifiers + /// only generate OP Identifier auth requests. + /// </summary> + [TestCase] + public void DualIdentifierUsedOnlyAsOPIdentifierForAuthRequest() { + var rp = this.CreateRelyingParty(true); + var results = AuthenticationRequest.Create(GetMockDualIdentifier(), rp, this.realm, this.returnTo, false).ToList(); + Assert.AreEqual(1, results.Count); + Assert.IsTrue(results[0].IsDirectedIdentity); + + // Also test when dual identiifer support is turned on. + rp.SecuritySettings.AllowDualPurposeIdentifiers = true; + results = AuthenticationRequest.Create(GetMockDualIdentifier(), rp, this.realm, this.returnTo, false).ToList(); + Assert.AreEqual(1, results.Count); + Assert.IsTrue(results[0].IsDirectedIdentity); } /// <summary> /// Verifies that authentication requests are generated first for OPs that respond /// to authentication requests. /// </summary> - [TestMethod, Ignore] + [TestCase, Ignore("Not yet implemented")] public void UnresponsiveProvidersComeLast() { // TODO: code here Assert.Inconclusive("Not yet implemented."); } - private AuthenticationRequest_Accessor CreateAuthenticationRequest(Identifier claimedIdentifier, Identifier providerLocalIdentifier) { + private AuthenticationRequest CreateAuthenticationRequest(Identifier claimedIdentifier, Identifier providerLocalIdentifier) { ProviderEndpointDescription providerEndpoint = new ProviderEndpointDescription(OPUri, this.protocol.Version); - ServiceEndpoint endpoint = ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, providerLocalIdentifier, providerEndpoint, 10, 5); - ServiceEndpoint_Accessor endpointAccessor = ServiceEndpoint_Accessor.AttachShadow(endpoint); + IdentifierDiscoveryResult endpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, providerLocalIdentifier, providerEndpoint, 10, 5); OpenIdRelyingParty rp = this.CreateRelyingParty(); - AuthenticationRequest_Accessor authRequest = new AuthenticationRequest_Accessor(endpointAccessor, this.realm, this.returnTo, rp); - return authRequest; + return AuthenticationRequest.CreateForTest(endpoint, this.realm, this.returnTo, rp); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/FailedAuthenticationResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/FailedAuthenticationResponseTests.cs index a82634a..43d056f 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/FailedAuthenticationResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/FailedAuthenticationResponseTests.cs @@ -12,14 +12,14 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class FailedAuthenticationResponseTests : OpenIdTestBase { private FailedAuthenticationResponse response; private ProtocolException exception; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -27,12 +27,12 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { this.response = new FailedAuthenticationResponse(this.exception); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNull() { new FailedAuthenticationResponse(null); } - [TestMethod] + [TestCase] public void CommonProperties() { Assert.AreEqual(AuthenticationStatus.Failed, this.response.Status); Assert.AreSame(this.exception, this.response.Exception); @@ -40,7 +40,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { Assert.IsNull(this.response.FriendlyIdentifierForDisplay); } - [TestMethod] + [TestCase] public void CommonMethods() { Assert.IsNull(this.response.GetExtension<ClaimsRequest>()); Assert.IsNull(this.response.GetExtension(typeof(ClaimsRequest))); diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/IdentifierDiscoveryResultTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/IdentifierDiscoveryResultTests.cs new file mode 100644 index 0000000..896cf57 --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/IdentifierDiscoveryResultTests.cs @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------- +// <copyright file="IdentifierDiscoveryResultTests.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.Extensions.SimpleRegistration; + using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.RelyingParty; + using DotNetOpenAuth.Test.Messaging; + using NUnit.Framework; + + [TestFixture] + public class IdentifierDiscoveryResultTests : 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; + + [SetUp] + public override void SetUp() { + base.SetUp(); + } + + [TestCase] + public void Ctor() { + IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + Assert.AreEqual(this.claimedId, se.ClaimedIdentifier); + Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint); + Assert.AreSame(this.localId, se.ProviderLocalIdentifier); + CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.Capabilities); + Assert.AreEqual(this.servicePriority, se.ServicePriority); + } + + [TestCase] + public void CtorImpliedLocalIdentifier() { + IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, null, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + Assert.AreEqual(this.claimedId, se.ClaimedIdentifier); + Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint); + Assert.AreSame(this.claimedId, se.ProviderLocalIdentifier); + CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.Capabilities); + } + + [TestCase] + public void ProtocolDetection() { + IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + Assert.AreSame(Protocol.V20, se.Protocol); + se = IdentifierDiscoveryResult.CreateForClaimedIdentifier( + this.claimedId, + this.localId, + new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.OPIdentifierServiceTypeURI }), + this.servicePriority, + this.uriPriority); + Assert.AreSame(Protocol.V20, se.Protocol); + se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority); + Assert.AreSame(Protocol.V11, se.Protocol); + } + + [TestCase] + public void EqualsTests() { + IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + IdentifierDiscoveryResult se2 = IdentifierDiscoveryResult.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); + + IdentifierDiscoveryResult se3 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(new UriIdentifier(this.claimedId + "a"), this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + Assert.AreNotEqual(se, se3); + se3 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(new Uri(this.providerEndpoint.AbsoluteUri + "a"), this.v20TypeUris), this.servicePriority, this.uriPriority); + Assert.AreNotEqual(se, se3); + se3 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId + "a", new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + Assert.AreNotEqual(se, se3); + se3 = IdentifierDiscoveryResult.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. + var list = new List<IdentifierDiscoveryResult>(); + list.Add(se); + Assert.IsTrue(list.Contains(se2)); + } + + [TestCase] + public void GetFriendlyIdentifierForDisplay() { + Uri providerEndpoint = new Uri("http://someprovider"); + Identifier localId = "someuser"; + string[] serviceTypeUris = new string[] { + Protocol.V20.ClaimedIdentifierServiceTypeURI, + }; + IdentifierDiscoveryResult se; + + // strip of protocol, port, query and fragment + se = IdentifierDiscoveryResult.CreateForClaimedIdentifier( + "http://someprovider.somedomain.com:79/someuser?query#frag", + localId, + new ProviderEndpointDescription(providerEndpoint, serviceTypeUris), + null, + null); + Assert.AreEqual("someprovider.somedomain.com/someuser", se.FriendlyIdentifierForDisplay); + + // unescape characters + Uri foreignUri = new Uri("http://server崎/村"); + se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(foreignUri, localId, new ProviderEndpointDescription(providerEndpoint, serviceTypeUris), null, null); + Assert.AreEqual("server崎/村", se.FriendlyIdentifierForDisplay); + + // restore user supplied identifier to XRIs + se = IdentifierDiscoveryResult.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 = IdentifierDiscoveryResult.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); + } + + [TestCase] + public void IsTypeUriPresent() { + IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + Assert.IsTrue(se.IsTypeUriPresent(Protocol.Default.ClaimedIdentifierServiceTypeURI)); + Assert.IsFalse(se.IsTypeUriPresent("http://someother")); + } + + [TestCase, ExpectedException(typeof(ArgumentException))] + public void IsTypeUriPresentNull() { + IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + se.IsTypeUriPresent(null); + } + + [TestCase, ExpectedException(typeof(ArgumentException))] + public void IsTypeUriPresentEmpty() { + IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); + se.IsTypeUriPresent(string.Empty); + } + + [TestCase, ExpectedException(typeof(ArgumentNullException))] + public void IsExtensionSupportedNullType() { + var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null); + se.IsExtensionSupported((Type)null); + } + + [TestCase, ExpectedException(typeof(ArgumentException))] + public void IsTypeUriPresentNullString() { + var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null); + se.IsTypeUriPresent((string)null); + } + + [TestCase, ExpectedException(typeof(ArgumentException))] + public void IsTypeUriPresentEmptyString() { + var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null); + se.IsTypeUriPresent(string.Empty); + } + + [TestCase, ExpectedException(typeof(ArgumentNullException))] + public void IsExtensionSupportedNullExtension() { + var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null); + se.IsExtensionSupported((IOpenIdMessageExtension)null); + } + + [TestCase] + public void IsExtensionSupported() { + var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null); + Assert.IsFalse(se.IsExtensionSupported<ClaimsRequest>()); + Assert.IsFalse(se.IsExtensionSupported(new ClaimsRequest())); + Assert.IsFalse(se.IsTypeUriPresent("http://someextension/typeuri")); + + se = IdentifierDiscoveryResult.CreateForProviderIdentifier( + OPUri, + new ProviderEndpointDescription(OPUri, new[] { Protocol.V20.ClaimedIdentifierServiceTypeURI, "http://someextension", Constants.sreg_ns }), + null, + null); + Assert.IsTrue(se.IsExtensionSupported<ClaimsRequest>()); + Assert.IsTrue(se.IsExtensionSupported(new ClaimsRequest())); + Assert.IsTrue(se.IsTypeUriPresent("http://someextension")); + } + } +} diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/NegativeAuthenticationResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/NegativeAuthenticationResponseTests.cs index acf537e..dbb4a42 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/NegativeAuthenticationResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/NegativeAuthenticationResponseTests.cs @@ -13,16 +13,16 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class NegativeAuthenticationResponseTests : OpenIdTestBase { private const string UserSuppliedIdentifier = "=arnott"; private Protocol protocol; private NegativeAssertionResponse responseMessage; private NegativeAuthenticationResponse response; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); @@ -32,7 +32,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { this.response = new NegativeAuthenticationResponse(this.responseMessage); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNull() { new NegativeAuthenticationResponse(null); } @@ -40,7 +40,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies that immediate/setup modes are correctly detected. /// </summary> - [TestMethod] + [TestCase] public void ImmediateVsSetupModes() { this.responseMessage = new NegativeAssertionResponse(this.protocol.Version, RPUri, this.protocol.Args.Mode.cancel); this.response = new NegativeAuthenticationResponse(this.responseMessage); @@ -55,17 +55,17 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { this.responseMessage.ExtraData[AuthenticationRequest.UserSuppliedIdentifierParameterName] = UserSuppliedIdentifier; this.response = new NegativeAuthenticationResponse(this.responseMessage); Assert.AreEqual(AuthenticationStatus.SetupRequired, this.response.Status); - Assert.AreEqual<string>(UserSuppliedIdentifier, this.response.UserSuppliedIdentifier); + Assert.AreEqual(UserSuppliedIdentifier, (string)this.response.UserSuppliedIdentifier); } - [TestMethod] + [TestCase] public void CommonProperties() { Assert.IsNull(this.response.Exception); Assert.IsNull(this.response.ClaimedIdentifier); Assert.IsNull(this.response.FriendlyIdentifierForDisplay); } - [TestMethod] + [TestCase] public void CommonMethods() { Assert.IsNull(this.response.GetExtension<ClaimsRequest>()); Assert.IsNull(this.response.GetExtension(typeof(ClaimsRequest))); diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs index f6a57e7..b0ec395 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs @@ -12,40 +12,40 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class OpenIdRelyingPartyTests : OpenIdTestBase { - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } - [TestMethod] + [TestCase] public void CreateRequestDumbMode() { - var rp = new OpenIdRelyingParty(null); + var rp = this.CreateRelyingParty(true); Identifier id = this.GetMockIdentifier(ProtocolVersion.V20); var authReq = rp.CreateRequest(id, RPRealmUri, RPUri); CheckIdRequest requestMessage = (CheckIdRequest)authReq.RedirectingResponse.OriginalMessage; Assert.IsNull(requestMessage.AssociationHandle); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void SecuritySettingsSetNull() { var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore()); rp.SecuritySettings = null; } - [TestMethod] + [TestCase] public void ExtensionFactories() { var rp = new OpenIdRelyingParty(null); var factories = rp.ExtensionFactories; Assert.IsNotNull(factories); Assert.AreEqual(1, factories.Count); - Assert.IsInstanceOfType(factories[0], typeof(StandardOpenIdExtensionFactory)); + Assert.IsInstanceOf<StandardOpenIdExtensionFactory>(factories[0]); } - [TestMethod] + [TestCase] public void CreateRequest() { var rp = this.CreateRelyingParty(); StoreAssociation(rp, OPUri, HmacShaAssociation.Create("somehandle", new byte[20], TimeSpan.FromDays(1))); @@ -54,7 +54,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { Assert.IsNotNull(req); } - [TestMethod] + [TestCase] public void CreateRequests() { var rp = this.CreateRelyingParty(); StoreAssociation(rp, OPUri, HmacShaAssociation.Create("somehandle", new byte[20], TimeSpan.FromDays(1))); @@ -63,7 +63,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { Assert.AreEqual(1, requests.Count()); } - [TestMethod] + [TestCase] public void CreateRequestsWithEndpointFilter() { var rp = this.CreateRelyingParty(); StoreAssociation(rp, OPUri, HmacShaAssociation.Create("somehandle", new byte[20], TimeSpan.FromDays(1))); @@ -78,7 +78,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { Assert.AreEqual(0, requests.Count()); } - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void CreateRequestOnNonOpenID() { Uri nonOpenId = new Uri("http://www.microsoft.com/"); var rp = this.CreateRelyingParty(); @@ -86,7 +86,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { rp.CreateRequest(nonOpenId, RPRealmUri, RPUri); } - [TestMethod] + [TestCase] public void CreateRequestsOnNonOpenID() { Uri nonOpenId = new Uri("http://www.microsoft.com/"); var rp = this.CreateRelyingParty(); @@ -99,7 +99,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// Verifies that incoming positive assertions throw errors if they come from /// OPs that are not approved by <see cref="OpenIdRelyingParty.EndpointFilter"/>. /// </summary> - [TestMethod] + [TestCase] public void AssertionWithEndpointFilter() { var coordinator = new OpenIdCoordinator( rp => { diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs index 67255e3..2c70e0f 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs @@ -6,14 +6,14 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class OpenIdTextBoxTests : OpenIdTestBase { /// <summary> /// Verifies that the Text and Identifier properties interact correctly. /// </summary> - [TestMethod] + [TestCase] public void IdentifierTextInteraction() { var box = new OpenIdTextBox(); Assert.AreEqual(string.Empty, box.Text); diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs index 1418513..b0586a6 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs @@ -10,14 +10,14 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PositiveAnonymousResponseTests : OpenIdTestBase { private readonly Realm realm = new Realm("http://localhost/rp.aspx"); private readonly Uri returnTo = new Uri("http://localhost/rp.aspx"); - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } @@ -25,7 +25,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies that the Status property returns the correct value. /// </summary> - [TestMethod] + [TestCase] public void CtorAndProperties() { var responseMessage = new IndirectSignedResponse(Protocol.V20.Version, this.returnTo); var ext = new ClaimsResponse(); @@ -43,7 +43,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies the Provider property. /// </summary> - [TestMethod] + [TestCase] public void ProviderTest() { var responseMessage = new IndirectSignedResponse(Protocol.V20.Version, this.returnTo); responseMessage.ProviderEndpoint = OPUri; diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshotTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshotTests.cs new file mode 100644 index 0000000..e069c44 --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshotTests.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------- +// <copyright file="PositiveAuthenticationResponseSnapshotTests.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.OpenId.RelyingParty { + using System.Collections.Generic; + using System.IO; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters.Binary; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.RelyingParty; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + [TestClass] + public class PositiveAuthenticationResponseSnapshotTests : OpenIdTestBase { + /// <summary> + /// Verifies that the PositiveAuthenticationResponseSnapshot is serializable, + /// as required by the <see cref="OpenIdRelyingPartyAjaxControlBase"/> class. + /// </summary> + [TestMethod] + public void Serializable() { + var response = new Mock<IAuthenticationResponse>(MockBehavior.Strict); + response.Setup(o => o.ClaimedIdentifier).Returns(VanityUri); + response.Setup(o => o.FriendlyIdentifierForDisplay).Returns(VanityUri.AbsoluteUri); + response.Setup(o => o.Status).Returns(AuthenticationStatus.Authenticated); + response.Setup(o => o.Provider).Returns(new ProviderEndpointDescription(OPUri, Protocol.Default.Version)); + response.Setup(o => o.GetUntrustedCallbackArguments()).Returns(new Dictionary<string, string>()); + response.Setup(o => o.GetCallbackArguments()).Returns(new Dictionary<string, string>()); + var snapshot = new PositiveAuthenticationResponseSnapshot(response.Object); + MemoryStream ms = new MemoryStream(); + IFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, snapshot); + } + } +} diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs index 701bcae..25b0607 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs @@ -12,14 +12,15 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using DotNetOpenAuth.Test.Mocks; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PositiveAuthenticationResponseTests : OpenIdTestBase { private readonly Realm realm = new Realm("http://localhost/rp.aspx"); private readonly Uri returnTo = new Uri("http://localhost/rp.aspx"); - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } @@ -27,7 +28,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// <summary> /// Verifies good, positive assertions are accepted. /// </summary> - [TestMethod] + [TestCase] public void Valid() { PositiveAssertionResponse assertion = this.GetPositiveAssertion(); ClaimsResponse extension = new ClaimsResponse(); @@ -37,8 +38,8 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { var authResponseAccessor = PositiveAuthenticationResponse_Accessor.AttachShadow(authResponse); Assert.AreEqual(AuthenticationStatus.Authenticated, authResponse.Status); Assert.IsNull(authResponse.Exception); - Assert.AreEqual<string>(assertion.ClaimedIdentifier, authResponse.ClaimedIdentifier); - Assert.AreEqual<string>(authResponse.Endpoint.FriendlyIdentifierForDisplay, authResponse.FriendlyIdentifierForDisplay); + Assert.AreEqual((string)assertion.ClaimedIdentifier, (string)authResponse.ClaimedIdentifier); + Assert.AreEqual(authResponse.Endpoint.FriendlyIdentifierForDisplay, authResponse.FriendlyIdentifierForDisplay); Assert.AreSame(extension, authResponse.GetUntrustedExtension(typeof(ClaimsResponse))); Assert.AreSame(extension, authResponse.GetUntrustedExtension<ClaimsResponse>()); Assert.IsNull(authResponse.GetCallbackArgument("a")); @@ -46,11 +47,37 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { } /// <summary> + /// Verifies that discovery verification of a positive assertion can match a dual identifier. + /// </summary> + [TestCase] + public void DualIdentifierMatchesInAssertionVerification() { + PositiveAssertionResponse assertion = this.GetPositiveAssertion(true); + ClaimsResponse extension = new ClaimsResponse(); + assertion.Extensions.Add(extension); + var rp = CreateRelyingParty(); + rp.SecuritySettings.AllowDualPurposeIdentifiers = true; + new PositiveAuthenticationResponse(assertion, rp); // this will throw if it fails to find a match + } + + /// <summary> + /// Verifies that discovery verification of a positive assertion cannot match a dual identifier + /// if the default settings are in place. + /// </summary> + [TestCase, ExpectedException(typeof(ProtocolException))] + public void DualIdentifierNoMatchInAssertionVerificationByDefault() { + PositiveAssertionResponse assertion = this.GetPositiveAssertion(true); + ClaimsResponse extension = new ClaimsResponse(); + assertion.Extensions.Add(extension); + var rp = CreateRelyingParty(); + new PositiveAuthenticationResponse(assertion, rp); // this will throw if it fails to find a match + } + + /// <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))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void SpoofedClaimedIdDetectionSolicited() { PositiveAssertionResponse assertion = this.GetPositiveAssertion(); assertion.ProviderEndpoint = new Uri("http://rogueOP"); @@ -63,7 +90,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// Verifies that the RP rejects positive assertions with HTTP Claimed /// Cdentifiers when RequireSsl is set to true. /// </summary> - [TestMethod, ExpectedException(typeof(ProtocolException))] + [TestCase, ExpectedException(typeof(ProtocolException))] public void InsecureIdentifiersRejectedWithRequireSsl() { PositiveAssertionResponse assertion = this.GetPositiveAssertion(); var rp = CreateRelyingParty(); @@ -71,7 +98,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { var authResponse = new PositiveAuthenticationResponse(assertion, rp); } - [TestMethod] + [TestCase] public void GetCallbackArguments() { PositiveAssertionResponse assertion = this.GetPositiveAssertion(); var rp = CreateRelyingParty(); @@ -94,10 +121,34 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { Assert.IsNull(authResponse.GetCallbackArgument("a")); } + /// <summary> + /// Verifies that certain problematic claimed identifiers pass through to the RP response correctly. + /// </summary> + [TestCase] + public void ProblematicClaimedId() { + var providerEndpoint = new ProviderEndpointDescription(OpenIdTestBase.OPUri, Protocol.Default.Version); + string claimed_id = BaseMockUri + "a./b."; + var se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimed_id, claimed_id, providerEndpoint, null, null); + UriIdentifier identityUri = (UriIdentifier)se.ClaimedIdentifier; + var mockId = new MockIdentifier(identityUri, this.MockResponder, new IdentifierDiscoveryResult[] { se }); + + var positiveAssertion = this.GetPositiveAssertion(); + positiveAssertion.ClaimedIdentifier = mockId; + positiveAssertion.LocalIdentifier = mockId; + var rp = CreateRelyingParty(); + var authResponse = new PositiveAuthenticationResponse(positiveAssertion, rp); + Assert.AreEqual(AuthenticationStatus.Authenticated, authResponse.Status); + Assert.AreEqual(claimed_id, authResponse.ClaimedIdentifier.ToString()); + } + private PositiveAssertionResponse GetPositiveAssertion() { + return this.GetPositiveAssertion(false); + } + + private PositiveAssertionResponse GetPositiveAssertion(bool dualIdentifier) { Protocol protocol = Protocol.Default; PositiveAssertionResponse assertion = new PositiveAssertionResponse(protocol.Version, this.returnTo); - assertion.ClaimedIdentifier = this.GetMockIdentifier(protocol.ProtocolVersion, false); + assertion.ClaimedIdentifier = dualIdentifier ? this.GetMockDualIdentifier() : this.GetMockIdentifier(protocol.ProtocolVersion, false); assertion.LocalIdentifier = OPLocalIdentifiers[0]; assertion.ReturnTo = this.returnTo; assertion.ProviderEndpoint = OPUri; diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs index 851939e..2f6d218 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs @@ -10,20 +10,20 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using System.Linq; using System.Text; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class RelyingPartySecuritySettingsTests : OpenIdTestBase { private RelyingPartySecuritySettings settings; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); this.settings = new RelyingPartySecuritySettings(); } - [TestMethod] + [TestCase] public void Defaults() { Assert.IsFalse(this.settings.RejectUnsolicitedAssertions); Assert.IsFalse(this.settings.RequireSsl, "Default should be to not require SSL."); @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// Verifies that the <see cref="RelyingPartySecuritySettings.RequireSsl"/> property /// getter/setter are implemented correctly. /// </summary> - [TestMethod] + [TestCase] public void RequireSsl() { this.settings.RequireSsl = true; Assert.IsTrue(this.settings.RequireSsl); @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// Verifies that the <see cref="RelyingPartySecuritySettings.RequireDirectedIdentity"/> /// property getter/setter are implemented correctly. /// </summary> - [TestMethod] + [TestCase] public void RequireDirectedIdentity() { this.settings.RequireDirectedIdentity = true; Assert.IsTrue(this.settings.RequireDirectedIdentity); @@ -57,7 +57,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { /// Verifies that the <see cref="RelyingPartySecuritySettings.RequireAssociation"/> /// property getter/setter are implemented correctly. /// </summary> - [TestMethod] + [TestCase] public void RequireAssociation() { this.settings.RequireAssociation = true; Assert.IsTrue(this.settings.RequireAssociation); diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs deleted file mode 100644 index ff15aa3..0000000 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs +++ /dev/null @@ -1,195 +0,0 @@ -//----------------------------------------------------------------------- -// <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.Extensions.SimpleRegistration; - using DotNetOpenAuth.OpenId.Messages; - 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.AreEqual(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.AreEqual(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, port, query and fragment - se = ServiceEndpoint.CreateForClaimedIdentifier( - "http://someprovider.somedomain.com:79/someuser?query#frag", - localId, - new ProviderEndpointDescription(providerEndpoint, serviceTypeUris), - null, - null); - Assert.AreEqual("someprovider.somedomain.com/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); - } - - [TestMethod] - public void IsTypeUriPresent() { - ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); - Assert.IsTrue(se.IsTypeUriPresent(Protocol.Default.ClaimedIdentifierServiceTypeURI)); - Assert.IsFalse(se.IsTypeUriPresent("http://someother")); - } - - [TestMethod, ExpectedException(typeof(ArgumentException))] - public void IsTypeUriPresentNull() { - ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); - se.IsTypeUriPresent(null); - } - - [TestMethod, ExpectedException(typeof(ArgumentException))] - public void IsTypeUriPresentEmpty() { - ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority); - se.IsTypeUriPresent(string.Empty); - } - } -} diff --git a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs index 5a5182f..5b015ff 100644 --- a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs @@ -13,40 +13,40 @@ namespace DotNetOpenAuth.Test.OpenId { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class UriIdentifierTests : OpenIdTestBase { private string goodUri = "http://blog.nerdbank.net/"; private string relativeUri = "host/path"; private string badUri = "som%-)830w8vf/?.<>,ewackedURI"; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } - [TestMethod, ExpectedException(typeof(ArgumentNullException))] + [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullUri() { new UriIdentifier((Uri)null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorNullString() { new UriIdentifier((string)null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorBlank() { new UriIdentifier(string.Empty); } - [TestMethod, ExpectedException(typeof(UriFormatException))] + [TestCase, ExpectedException(typeof(UriFormatException))] public void CtorBadUri() { new UriIdentifier(this.badUri); } - [TestMethod] + [TestCase] public void CtorGoodUri() { var uri = new UriIdentifier(this.goodUri); Assert.AreEqual(new Uri(this.goodUri), uri.Uri); @@ -54,33 +54,33 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.IsFalse(uri.IsDiscoverySecureEndToEnd); } - [TestMethod] + [TestCase] public void CtorStringNoSchemeSecure() { var uri = new UriIdentifier("host/path", true); Assert.AreEqual("https://host/path", uri.Uri.AbsoluteUri); Assert.IsTrue(uri.IsDiscoverySecureEndToEnd); } - [TestMethod] + [TestCase] public void CtorStringHttpsSchemeSecure() { var uri = new UriIdentifier("https://host/path", true); Assert.AreEqual("https://host/path", uri.Uri.AbsoluteUri); Assert.IsTrue(uri.IsDiscoverySecureEndToEnd); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorStringHttpSchemeSecure() { new UriIdentifier("http://host/path", true); } - [TestMethod] + [TestCase] public void CtorUriHttpsSchemeSecure() { var uri = new UriIdentifier(new Uri("https://host/path"), true); Assert.AreEqual("https://host/path", uri.Uri.AbsoluteUri); Assert.IsTrue(uri.IsDiscoverySecureEndToEnd); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorUriHttpSchemeSecure() { new UriIdentifier(new Uri("http://host/path"), true); } @@ -93,44 +93,73 @@ namespace DotNetOpenAuth.Test.OpenId { /// they should NOT be stripped from claimed identifiers. So the UriIdentifier /// class, which serves both identifier types, must not do the stripping. /// </remarks> - [TestMethod] + [TestCase] public void DoesNotStripFragment() { Uri original = new Uri("http://a/b#c"); UriIdentifier identifier = new UriIdentifier(original); Assert.AreEqual(original.Fragment, identifier.Uri.Fragment); } - [TestMethod] + [TestCase] public void IsValid() { Assert.IsTrue(UriIdentifier.IsValidUri(this.goodUri)); Assert.IsFalse(UriIdentifier.IsValidUri(this.badUri)); Assert.IsTrue(UriIdentifier.IsValidUri(this.relativeUri), "URL lacking http:// prefix should have worked anyway."); } - [TestMethod] + [TestCase] public void TrimFragment() { Identifier noFragment = UriIdentifier.Parse("http://a/b"); Identifier fragment = UriIdentifier.Parse("http://a/b#c"); Assert.AreSame(noFragment, noFragment.TrimFragment()); - Assert.AreEqual(noFragment, fragment.TrimFragment()); + Assert.AreEqual(noFragment.ToString(), fragment.TrimFragment().ToString()); + + // Try the problematic ones + TestAsFullAndPartialTrust(fullTrust => { + Identifier noFrag = UriIdentifier.Parse("http://a/b./c"); + Identifier frag = UriIdentifier.Parse("http://a/b./c#d"); + Assert.AreSame(noFrag, noFrag.TrimFragment()); + Assert.AreEqual(noFrag.ToString(), frag.TrimFragment().ToString()); + }); } - [TestMethod] + [TestCase] public void ToStringTest() { Assert.AreEqual(this.goodUri, new UriIdentifier(this.goodUri).ToString()); + TestAsFullAndPartialTrust(fullTrust => { + Assert.AreEqual("http://abc/D./e.?Qq#Ff", new UriIdentifier("HTTP://ABC/D./e.?Qq#Ff").ToString()); + Assert.AreEqual("http://abc/D./e.?Qq", new UriIdentifier("HTTP://ABC/D./e.?Qq").ToString()); + Assert.AreEqual("http://abc/D./e.#Ff", new UriIdentifier("HTTP://ABC/D./e.#Ff").ToString()); + Assert.AreEqual("http://abc/", new UriIdentifier("HTTP://ABC").ToString()); + Assert.AreEqual("http://abc/?q", new UriIdentifier("HTTP://ABC?q").ToString()); + Assert.AreEqual("http://abc/#f", new UriIdentifier("HTTP://ABC#f").ToString()); + }); } - [TestMethod] + [TestCase] public void EqualsTest() { - Assert.AreEqual(new UriIdentifier(this.goodUri), new UriIdentifier(this.goodUri)); - // This next test is an interesting side-effect of passing off to Uri.Equals. But it's probably ok. - Assert.AreEqual(new UriIdentifier(this.goodUri), new UriIdentifier(this.goodUri + "#frag")); - Assert.AreNotEqual(new UriIdentifier(this.goodUri), new UriIdentifier(this.goodUri + "a")); - Assert.AreNotEqual(null, new UriIdentifier(this.goodUri)); - Assert.AreEqual(this.goodUri, new UriIdentifier(this.goodUri)); - } - - [TestMethod] + TestAsFullAndPartialTrust(fulltrust => { + Assert.AreEqual(new UriIdentifier(this.goodUri), new UriIdentifier(this.goodUri)); + // This next test is an interesting side-effect of passing off to Uri.Equals. But it's probably ok. + Assert.AreEqual(new UriIdentifier(this.goodUri), new UriIdentifier(this.goodUri + "#frag")); + Assert.AreEqual(new UriIdentifier("http://a/b./c."), new UriIdentifier("http://a/b./c.#frag")); + Assert.AreNotEqual(new UriIdentifier(this.goodUri), new UriIdentifier(this.goodUri + "a")); + Assert.AreNotEqual(null, new UriIdentifier(this.goodUri)); + Assert.IsTrue(new UriIdentifier(this.goodUri).Equals(this.goodUri)); + + Assert.AreEqual(Identifier.Parse("HTTP://WWW.FOO.COM/abc", true), Identifier.Parse("http://www.foo.com/abc", true)); + Assert.AreEqual(Identifier.Parse("HTTP://WWW.FOO.COM/abc", true), Identifier.Parse("http://www.foo.com/abc", false)); + Assert.AreEqual(Identifier.Parse("HTTP://WWW.FOO.COM/abc", false), Identifier.Parse("http://www.foo.com/abc", false)); + Assert.AreNotEqual(Identifier.Parse("http://www.foo.com/abc", true), Identifier.Parse("http://www.foo.com/ABC", true)); + Assert.AreNotEqual(Identifier.Parse("http://www.foo.com/abc", true), Identifier.Parse("http://www.foo.com/ABC", false)); + Assert.AreNotEqual(Identifier.Parse("http://www.foo.com/abc", false), Identifier.Parse("http://www.foo.com/ABC", false)); + + Assert.AreNotEqual(Identifier.Parse("http://a/b./c."), Identifier.Parse("http://a/b/c.")); + Assert.AreEqual(Identifier.Parse("http://a/b./c."), Identifier.Parse("http://a/b./c.")); + }); + } + + [TestCase] public void UnicodeTest() { string unicodeUrl = "http://nerdbank.org/opaffirmative/崎村.aspx"; Assert.IsTrue(UriIdentifier.IsValidUri(unicodeUrl)); @@ -140,63 +169,7 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.AreEqual(Uri.EscapeUriString(unicodeUrl), id.ToString()); } - [TestMethod] - public void HtmlDiscover_11() { - this.DiscoverHtml("html10prov", ProtocolVersion.V11, null, "http://a/b"); - this.DiscoverHtml("html10both", ProtocolVersion.V11, "http://c/d", "http://a/b"); - this.FailDiscoverHtml("html10del"); - - // Verify that HTML discovery generates the 1.x endpoints when appropriate - this.DiscoverHtml("html2010", ProtocolVersion.V11, "http://g/h", "http://e/f"); - this.DiscoverHtml("html1020", ProtocolVersion.V11, "http://g/h", "http://e/f"); - this.DiscoverHtml("html2010combinedA", ProtocolVersion.V11, "http://c/d", "http://a/b"); - this.DiscoverHtml("html2010combinedB", ProtocolVersion.V11, "http://c/d", "http://a/b"); - this.DiscoverHtml("html2010combinedC", ProtocolVersion.V11, "http://c/d", "http://a/b"); - } - - [TestMethod] - public void HtmlDiscover_20() { - this.DiscoverHtml("html20prov", ProtocolVersion.V20, null, "http://a/b"); - this.DiscoverHtml("html20both", ProtocolVersion.V20, "http://c/d", "http://a/b"); - this.FailDiscoverHtml("html20del"); - this.DiscoverHtml("html2010", ProtocolVersion.V20, "http://c/d", "http://a/b"); - this.DiscoverHtml("html1020", ProtocolVersion.V20, "http://c/d", "http://a/b"); - this.DiscoverHtml("html2010combinedA", ProtocolVersion.V20, "http://c/d", "http://a/b"); - this.DiscoverHtml("html2010combinedB", ProtocolVersion.V20, "http://c/d", "http://a/b"); - this.DiscoverHtml("html2010combinedC", ProtocolVersion.V20, "http://c/d", "http://a/b"); - this.FailDiscoverHtml("html20relative"); - } - - [TestMethod] - public void XrdsDiscoveryFromHead() { - this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml")); - this.DiscoverXrds("XrdsReferencedInHead.html", ProtocolVersion.V10, null, "http://a/b"); - } - - [TestMethod] - public void XrdsDiscoveryFromHttpHeader() { - WebHeaderCollection headers = new WebHeaderCollection(); - headers.Add("X-XRDS-Location", new Uri("http://localhost/xrds1020.xml").AbsoluteUri); - this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml")); - this.DiscoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion.V10, null, "http://a/b", headers); - } - - [TestMethod] - public void XrdsDirectDiscovery_10() { - this.FailDiscoverXrds("xrds-irrelevant"); - this.DiscoverXrds("xrds10", ProtocolVersion.V10, null, "http://a/b"); - this.DiscoverXrds("xrds11", ProtocolVersion.V11, null, "http://a/b"); - this.DiscoverXrds("xrds1020", ProtocolVersion.V10, null, "http://a/b"); - } - - [TestMethod] - public void XrdsDirectDiscovery_20() { - this.DiscoverXrds("xrds20", ProtocolVersion.V20, null, "http://a/b"); - this.DiscoverXrds("xrds2010a", ProtocolVersion.V20, null, "http://a/b"); - this.DiscoverXrds("xrds2010b", ProtocolVersion.V20, null, "http://a/b"); - } - - [TestMethod] + [TestCase] public void NormalizeCase() { // only the host name can be normalized in casing safely. Identifier id = "http://HOST:80/PaTH?KeY=VaLUE#fRag"; @@ -206,36 +179,66 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.AreEqual("https://host:80/PaTH?KeY=VaLUE#fRag", id.ToString()); } - [TestMethod] + /// <summary> + /// Verifies that URIs that contain base64 encoded path segments (such as Yahoo) are properly preserved. + /// </summary> + /// <remarks> + /// Yahoo includes a base64 encoded part as their last path segment, + /// which may end with a period. The default .NET Uri parser trims off + /// trailing periods, which breaks OpenID unless special precautions are taken. + /// </remarks> + [TestCase] + public void TrailingPeriodsNotTrimmed() { + TestAsFullAndPartialTrust(fullTrust => { + string claimedIdentifier = "https://me.yahoo.com/a/AsDf.#asdf"; + Identifier id = claimedIdentifier; + Assert.AreEqual(claimedIdentifier, id.OriginalString); + Assert.AreEqual(claimedIdentifier, id.ToString()); + + UriIdentifier idUri = new UriIdentifier(claimedIdentifier); + Assert.AreEqual(claimedIdentifier, idUri.OriginalString); + Assert.AreEqual(claimedIdentifier, idUri.ToString()); + if (fullTrust) { + Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri); + } + Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString()); + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString); + Assert.AreEqual(id.ToString(), new UriIdentifier((Uri)idUri).ToString(), "Round tripping UriIdentifier->Uri->UriIdentifier failed."); + + idUri = new UriIdentifier(new Uri(claimedIdentifier)); + Assert.AreEqual(claimedIdentifier, idUri.OriginalString); + Assert.AreEqual(claimedIdentifier, idUri.ToString()); + if (fullTrust) { + Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri); + } + Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString()); + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString); + Assert.AreEqual(id.ToString(), new UriIdentifier((Uri)idUri).ToString(), "Round tripping UriIdentifier->Uri->UriIdentifier failed."); + + claimedIdentifier = "https://me.yahoo.com:443/a/AsDf.#asdf"; + id = claimedIdentifier; + Assert.AreEqual(claimedIdentifier, id.OriginalString); + Assert.AreEqual("https://me.yahoo.com/a/AsDf.#asdf", id.ToString()); + }); + } + + [TestCase] public void HttpSchemePrepended() { UriIdentifier id = new UriIdentifier("www.yahoo.com"); Assert.AreEqual("http://www.yahoo.com/", id.ToString()); Assert.IsTrue(id.SchemeImplicitlyPrepended); } - ////[TestMethod, Ignore("The spec says http:// must be prepended in this case, but that just creates an invalid URI. Our UntrustedWebRequest will stop disallowed schemes.")] + ////[TestCase, Ignore("The spec says http:// must be prepended in this case, but that just creates an invalid URI. Our UntrustedWebRequest will stop disallowed schemes.")] public void CtorDisallowedScheme() { UriIdentifier id = new UriIdentifier(new Uri("ftp://host/path")); Assert.AreEqual("http://ftp://host/path", id.ToString()); Assert.IsTrue(id.SchemeImplicitlyPrepended); } - [TestMethod] - public void DiscoveryWithRedirects() { - Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, false); - - // Add a couple of chained redirect pages that lead to the claimedId. - Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); - Uri insecureMidpointUri = new Uri("http://localhost/insecureStop"); - this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri); - this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString())); - - // don't require secure SSL discovery for this test. - Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, false); - Assert.AreEqual(1, userSuppliedIdentifier.Discover(this.RequestHandler).Count()); - } - - [TestMethod] + [TestCase] public void TryRequireSslAdjustsIdentifier() { Identifier secureId; // Try Parse and ctor without explicit scheme @@ -256,180 +259,53 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.IsFalse(id.TryRequireSsl(out secureId)); Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd, "Although the TryRequireSsl failed, the created identifier should retain the Ssl status."); Assert.AreEqual("http://www.yahoo.com/", secureId.ToString()); - Assert.AreEqual(0, secureId.Discover(this.RequestHandler).Count(), "Since TryRequireSsl failed, the created Identifier should never discover anything."); + Assert.AreEqual(0, Discover(secureId).Count(), "Since TryRequireSsl failed, the created Identifier should never discover anything."); id = new UriIdentifier("http://www.yahoo.com"); Assert.IsFalse(id.TryRequireSsl(out secureId)); Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd); Assert.AreEqual("http://www.yahoo.com/", secureId.ToString()); - Assert.AreEqual(0, secureId.Discover(this.RequestHandler).Count()); - } - - [TestMethod] - public void DiscoverRequireSslWithSecureRedirects() { - Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true); - - // Add a couple of chained redirect pages that lead to the claimedId. - // All redirects should be secure. - Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); - Uri secureMidpointUri = new Uri("https://localhost/secureStop"); - this.MockResponder.RegisterMockRedirect(userSuppliedUri, secureMidpointUri); - this.MockResponder.RegisterMockRedirect(secureMidpointUri, new Uri(claimedId.ToString())); - - Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true); - Assert.AreEqual(1, userSuppliedIdentifier.Discover(this.RequestHandler).Count()); - } - - [TestMethod, ExpectedException(typeof(ProtocolException))] - public void DiscoverRequireSslWithInsecureRedirect() { - Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true); - - // Add a couple of chained redirect pages that lead to the claimedId. - // Include an insecure HTTP jump in those redirects to verify that - // the ultimate endpoint is never found as a result of high security profile. - Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); - Uri insecureMidpointUri = new Uri("http://localhost/insecureStop"); - this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri); - this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString())); - - Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true); - userSuppliedIdentifier.Discover(this.RequestHandler); + Assert.AreEqual(0, Discover(secureId).Count()); } - [TestMethod] - public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() { - var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); - Uri secureClaimedUri = new Uri("https://localhost/secureId"); - - string html = string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>", insecureXrdsSource); - this.MockResponder.RegisterMockResponse(secureClaimedUri, "text/html", html); - - Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true); - Assert.AreEqual(0, userSuppliedIdentifier.Discover(this.RequestHandler).Count()); - } - - [TestMethod] - public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() { - var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); - - string html = "<html><head></head><body></body></html>"; - WebHeaderCollection headers = new WebHeaderCollection { - { "X-XRDS-Location", insecureXrdsSource } - }; - this.MockResponder.RegisterMockResponse(VanityUriSsl, VanityUriSsl, "text/html", headers, html); - - Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true); - Assert.AreEqual(0, userSuppliedIdentifier.Discover(this.RequestHandler).Count()); - } - - [TestMethod] - public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() { - var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); - string html = string.Format( - @" - <html><head> - <meta http-equiv='X-XRDS-Location' content='{0}'/> <!-- this one will be insecure and ignored --> - <link rel='openid2.provider' href='{1}' /> - <link rel='openid2.local_id' href='{2}' /> - </head><body></body></html>", - HttpUtility.HtmlEncode(insecureXrdsSource), - HttpUtility.HtmlEncode(OPUriSsl.AbsoluteUri), - HttpUtility.HtmlEncode(OPLocalIdentifiersSsl[1].AbsoluteUri)); - this.MockResponder.RegisterMockResponse(VanityUriSsl, "text/html", html); - - Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true); - - // We verify that the XRDS was ignored and the LINK tags were used - // because the XRDS OP-LocalIdentifier uses different local identifiers. - Assert.AreEqual(OPLocalIdentifiersSsl[1], userSuppliedIdentifier.Discover(this.RequestHandler).Single().ProviderLocalIdentifier); - } - - [TestMethod] - public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() { - var insecureEndpoint = GetServiceEndpoint(0, ProtocolVersion.V20, 10, false); - var secureEndpoint = GetServiceEndpoint(1, ProtocolVersion.V20, 20, true); - UriIdentifier secureClaimedId = new UriIdentifier(VanityUriSsl, true); - this.MockResponder.RegisterMockXrdsResponse(secureClaimedId, new ServiceEndpoint[] { insecureEndpoint, secureEndpoint }); - Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, secureClaimedId.Discover(this.RequestHandler).Single().ProviderLocalIdentifier); - } - - private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect) { - this.Discover(url, version, expectedLocalId, providerEndpoint, expectSreg, useRedirect, null); - } - - private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect, WebHeaderCollection headers) { - Protocol protocol = Protocol.Lookup(version); - Uri baseUrl = new Uri("http://localhost/"); - UriIdentifier claimedId = new Uri(baseUrl, url); - UriIdentifier userSuppliedIdentifier = new Uri(baseUrl, "Discovery/htmldiscovery/redirect.aspx?target=" + url); - if (expectedLocalId == null) { - expectedLocalId = claimedId; - } - Identifier idToDiscover = useRedirect ? userSuppliedIdentifier : claimedId; - - string contentType; - if (url.EndsWith("html")) { - contentType = "text/html"; - } else if (url.EndsWith("xml")) { - contentType = "application/xrds+xml"; - } else { - throw new InvalidOperationException(); - } - this.MockResponder.RegisterMockResponse(new Uri(idToDiscover), claimedId, contentType, headers ?? new WebHeaderCollection(), LoadEmbeddedFile(url)); - - ServiceEndpoint expected = ServiceEndpoint.CreateForClaimedIdentifier( - claimedId, - expectedLocalId, - new ProviderEndpointDescription(new Uri(providerEndpoint), new string[] { protocol.ClaimedIdentifierServiceTypeURI }), // services aren't checked by Equals - null, - null); - - ServiceEndpoint se = idToDiscover.Discover(this.RequestHandler).FirstOrDefault(ep => ep.Equals(expected)); - Assert.IsNotNull(se, url + " failed to be discovered."); - - // Do extra checking of service type URIs, which aren't included in - // the ServiceEndpoint.Equals method. - Assert.AreEqual(expectSreg ? 2 : 1, se.ProviderSupportedServiceTypeUris.Count); - Assert.IsTrue(se.ProviderSupportedServiceTypeUris.Contains(protocol.ClaimedIdentifierServiceTypeURI)); - Assert.AreEqual(expectSreg, se.IsExtensionSupported<ClaimsRequest>()); - } - - private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) { - this.DiscoverXrds(page, version, expectedLocalId, providerEndpoint, null); + /// <summary> + /// Verifies that unicode hostnames are handled. + /// </summary> + [TestCase] + public void UnicodeHostSupport() { + var id = new UriIdentifier("http://server崎/村"); + Assert.AreEqual("server崎", id.Uri.Host); } - private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, WebHeaderCollection headers) { - if (!page.Contains(".")) { - page += ".xml"; + /// <summary> + /// Verifies SimpleUri behavior + /// </summary> + [TestCase] + public void SimpleUri() { + Assert.AreEqual("http://abc/D./e.?Qq#Ff", new UriIdentifier.SimpleUri("HTTP://ABC/D./e.?Qq#Ff").ToString()); + Assert.AreEqual("http://abc/D./e.?Qq", new UriIdentifier.SimpleUri("HTTP://ABC/D./e.?Qq").ToString()); + Assert.AreEqual("http://abc/D./e.#Ff", new UriIdentifier.SimpleUri("HTTP://ABC/D./e.#Ff").ToString()); + Assert.AreEqual("http://abc/", new UriIdentifier.SimpleUri("HTTP://ABC/").ToString()); + Assert.AreEqual("http://abc/", new UriIdentifier.SimpleUri("HTTP://ABC").ToString()); + Assert.AreEqual("http://abc/?q", new UriIdentifier.SimpleUri("HTTP://ABC?q").ToString()); + Assert.AreEqual("http://abc/#f", new UriIdentifier.SimpleUri("HTTP://ABC#f").ToString()); + + Assert.AreEqual("http://abc/a//b", new UriIdentifier.SimpleUri("http://abc/a//b").ToString()); + Assert.AreEqual("http://abc/a%2Fb/c", new UriIdentifier.SimpleUri("http://abc/a%2fb/c").ToString()); + Assert.AreEqual("http://abc/A/c", new UriIdentifier.SimpleUri("http://abc/%41/c").ToString()); + } + + private static void TestAsFullAndPartialTrust(Action<bool> action) { + // Test a bunch of interesting URLs both with scheme substitution on and off. + Assert.IsTrue(UriIdentifier_Accessor.schemeSubstitution, "Expected scheme substitution to be working."); + action(true); + + UriIdentifier_Accessor.schemeSubstitution = false; + try { + action(false); + } finally { + UriIdentifier_Accessor.schemeSubstitution = true; } - this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, false, headers); - this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, true, headers); - } - - private void DiscoverHtml(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool useRedirect) { - this.Discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, providerEndpoint, false, useRedirect); - } - - private void DiscoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) { - string page = scenario + ".html"; - this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, false); - this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, true); - } - - private void FailDiscover(string url) { - UriIdentifier userSuppliedId = new Uri(new Uri("http://localhost"), url); - - this.MockResponder.RegisterMockResponse(new Uri(userSuppliedId), userSuppliedId, "text/html", LoadEmbeddedFile(url)); - - Assert.AreEqual(0, userSuppliedId.Discover(this.RequestHandler).Count()); // ... but that no endpoint info is discoverable - } - - private void FailDiscoverHtml(string scenario) { - this.FailDiscover("/Discovery/htmldiscovery/" + scenario + ".html"); - } - - private void FailDiscoverXrds(string scenario) { - this.FailDiscover("/Discovery/xrdsdiscovery/" + scenario + ".xml"); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs index 46427bb..0c80821 100644 --- a/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs @@ -10,34 +10,34 @@ namespace DotNetOpenAuth.Test.OpenId { using System.Linq; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class XriIdentifierTests : OpenIdTestBase { private string goodXri = "=Andrew*Arnott"; private string badXri = "some\\wacky%^&*()non-XRI"; - [TestInitialize] + [SetUp] public override void SetUp() { base.SetUp(); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorNull() { new XriIdentifier(null); } - [TestMethod, ExpectedException(typeof(ArgumentException))] + [TestCase, ExpectedException(typeof(ArgumentException))] public void CtorBlank() { new XriIdentifier(string.Empty); } - [TestMethod, ExpectedException(typeof(FormatException))] + [TestCase, ExpectedException(typeof(FormatException))] public void CtorBadXri() { new XriIdentifier(this.badXri); } - [TestMethod] + [TestCase] public void CtorGoodXri() { var xri = new XriIdentifier(this.goodXri); Assert.AreEqual(this.goodXri, xri.OriginalXri); @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.IsFalse(xri.IsDiscoverySecureEndToEnd); } - [TestMethod] + [TestCase] public void CtorGoodXriSecure() { var xri = new XriIdentifier(this.goodXri, true); Assert.AreEqual(this.goodXri, xri.OriginalXri); @@ -53,7 +53,7 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.IsTrue(xri.IsDiscoverySecureEndToEnd); } - [TestMethod] + [TestCase] public void IsValid() { Assert.IsTrue(XriIdentifier.IsValidXri(this.goodXri)); Assert.IsFalse(XriIdentifier.IsValidXri(this.badXri)); @@ -62,412 +62,36 @@ namespace DotNetOpenAuth.Test.OpenId { /// <summary> /// Verifies 2.0 spec section 7.2#1 /// </summary> - [TestMethod] + [TestCase] public void StripXriScheme() { var xri = new XriIdentifier("xri://" + this.goodXri); Assert.AreEqual("xri://" + this.goodXri, xri.OriginalXri); Assert.AreEqual(this.goodXri, xri.CanonicalXri); } - [TestMethod] + [TestCase] public void TrimFragment() { Identifier xri = new XriIdentifier(this.goodXri); Assert.AreSame(xri, xri.TrimFragment()); } - [TestMethod] + [TestCase] public void ToStringTest() { Assert.AreEqual(this.goodXri, new XriIdentifier(this.goodXri).ToString()); } - [TestMethod] + [TestCase] public void EqualsTest() { Assert.AreEqual(new XriIdentifier(this.goodXri), new XriIdentifier(this.goodXri)); Assert.AreNotEqual(new XriIdentifier(this.goodXri), new XriIdentifier(this.goodXri + "a")); Assert.AreNotEqual(null, new XriIdentifier(this.goodXri)); - Assert.AreEqual(this.goodXri, new XriIdentifier(this.goodXri)); + Assert.IsTrue(new XriIdentifier(this.goodXri).Equals(this.goodXri)); } - [TestMethod] - public void Discover() { - string xrds = @"<?xml version='1.0' encoding='UTF-8'?> -<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'> - <Query>*Arnott</Query> - <Status ceid='off' cid='verified' code='100'/> - <Expires>2008-07-14T02:03:24.000Z</Expires> - <ProviderID>xri://=</ProviderID> - <LocalID>!9b72.7dd1.50a9.5ccd</LocalID> - <CanonicalID>=!9B72.7DD1.50A9.5CCD</CanonicalID> - - <Service priority='10'> - <ProviderID>xri://!!1008</ProviderID> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='default' select='false'/> - <Path select='true'>(+contact)</Path> - <Path match='null' select='false'/> - <URI append='qxri' priority='1'>http://1id.com/contact/</URI> - - </Service> - <Service priority='10'> - <ProviderID>xri://!!1008</ProviderID> - <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Type match='null' select='false'/> - <URI append='qxri' priority='1'>http://1id.com/</URI> - </Service> - - <Service priority='10'> - <ProviderID>xri://!!1008</ProviderID> - <Type select='true'>http://openid.net/signon/1.0</Type> - <URI append='none' priority='10'>http://1id.com/sso</URI> - </Service> -</XRD>"; - Dictionary<string, string> mocks = new Dictionary<string, string> { - { "https://xri.net/=Arnott?_xrd_r=application/xrd%2Bxml;sep=false", xrds }, - { "https://xri.net/=!9B72.7DD1.50A9.5CCD?_xrd_r=application/xrd%2Bxml;sep=false", xrds }, - }; - this.MockResponder.RegisterMockXrdsResponses(mocks); - - string expectedCanonicalId = "=!9B72.7DD1.50A9.5CCD"; - ServiceEndpoint se = this.VerifyCanonicalId("=Arnott", expectedCanonicalId); - Assert.AreEqual(Protocol.V10, se.Protocol); - Assert.AreEqual("http://1id.com/sso", se.ProviderEndpoint.ToString()); - Assert.AreEqual(se.ClaimedIdentifier, se.ProviderLocalIdentifier); - Assert.AreEqual("=Arnott", se.FriendlyIdentifierForDisplay); - } - - [TestMethod] - public void DiscoverCommunityInameCanonicalIDs() { - string llliResponse = @"<?xml version='1.0' encoding='UTF-8'?> -<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'> - <Query>*llli</Query> - <Status ceid='off' cid='verified' code='100'/> - <Expires>2008-07-14T02:21:06.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID>!72cd.a072.157e.a9c6</LocalID> - <CanonicalID>@!72CD.A072.157E.A9C6</CanonicalID> - <Service priority='10'> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>http://openid.net/signon/1.0</Type> - <URI append='none' priority='1'>https://login.llli.org/server/</URI> - </Service> - <Service priority='1'> - <ProviderID>xri://!!1003!103</ProviderID> - <Type match='null' select='false'/> - <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Path match='default'/> - <Path>(+index)</Path> - <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> - </Service> - <Service priority='10'> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://$res*auth*($v*2.0)</Type> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority='10'>http://resolve.ezibroker.net/resolve/@llli/</URI> - </Service> - <Service priority='10'> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='null'/> - <Path select='true'>(+contact)</Path> - <Path match='null'/> - <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> - </Service> -</XRD> -"; - string llliAreaResponse = @"<?xml version='1.0' encoding='UTF-8'?> -<XRD xmlns='xri://$xrd*($v*2.0)'> - <Query>*area</Query> - <Status cid='verified' code='100'>SUCCESS</Status> - <ServerStatus code='100'>SUCCESS</ServerStatus> - <Expires>2008-07-15T01:21:07.000Z</Expires> - <ProviderID>xri://!!1003</ProviderID> - <LocalID>0000.0000.3B9A.CA0C</LocalID> - <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C</CanonicalID> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>http://openid.net/signon/1.0</Type> - <URI append='none' priority='1'>https://login.llli.org/server/</URI> - </Service> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='null'/> - <Path select='true'>(+contact)</Path> - <Path match='null'/> - <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> - </Service> - <Service priority='1'> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Type match='null' select='false'/> - <Path>(+index)</Path> - <Path match='default'/> - <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> - </Service> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://$res*auth*($v*2.0)</Type> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI>http://resolve.ezibroker.net/resolve/@llli*area/</URI> - </Service> -</XRD>"; - string llliAreaCanadaUnattachedResponse = @"<?xml version='1.0' encoding='UTF-8'?> -<XRD xmlns='xri://$xrd*($v*2.0)'> - <Query>*canada.unattached</Query> - <Status cid='verified' code='100'>SUCCESS</Status> - <ServerStatus code='100'>SUCCESS</ServerStatus> - <Expires>2008-07-15T01:21:08.000Z</Expires> - <ProviderID>xri://!!1003</ProviderID> - <LocalID>0000.0000.3B9A.CA41</LocalID> - <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41</CanonicalID> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>http://openid.net/signon/1.0</Type> - <URI append='none' priority='1'>https://login.llli.org/server/</URI> - </Service> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='null'/> - <Path select='true'>(+contact)</Path> - <Path match='null'/> - <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> - </Service> - <Service priority='1'> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Type match='null' select='false'/> - <Path>(+index)</Path> - <Path match='default'/> - <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> - </Service> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://$res*auth*($v*2.0)</Type> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI>http://resolve.ezibroker.net/resolve/@llli*area*canada.unattached/</URI> - </Service> -</XRD>"; - string llliAreaCanadaUnattachedAdaResponse = @"<?xml version='1.0' encoding='UTF-8'?> -<XRD xmlns='xri://$xrd*($v*2.0)'> - <Query>*ada</Query> - <Status cid='verified' code='100'>SUCCESS</Status> - <ServerStatus code='100'>SUCCESS</ServerStatus> - <Expires>2008-07-15T01:21:10.000Z</Expires> - <ProviderID>xri://!!1003</ProviderID> - <LocalID>0000.0000.3B9A.CA01</LocalID> - <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01</CanonicalID> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>http://openid.net/signon/1.0</Type> - <URI append='none' priority='1'>https://login.llli.org/server/</URI> - </Service> - <Service> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='null'/> - <Path select='true'>(+contact)</Path> - <Path match='null'/> - <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI> - </Service> - <Service priority='1'> - <ProviderID>xri://!!1003!103</ProviderID> - <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Type match='null' select='false'/> - <Path>(+index)</Path> - <Path match='default'/> - <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI> - </Service> -</XRD>"; - string webResponse = @"<?xml version='1.0' encoding='UTF-8'?> -<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'> - <Query>*Web</Query> - <Status ceid='off' cid='verified' code='100'/> - <Expires>2008-07-14T02:21:12.000Z</Expires> - <ProviderID>xri://=</ProviderID> - <LocalID>!91f2.8153.f600.ae24</LocalID> - <CanonicalID>=!91F2.8153.F600.AE24</CanonicalID> - <Service priority='10'> - <Type select='true'>xri://+i-service*(+locator)*($v*1.0)</Type> - <Path select='true'>(+locator)</Path> - <MediaType match='default' select='false'/> - <URI append='qxri'>http://locator.fullxri.com/locator/</URI> - </Service> - <Service priority='10'> - <ProviderID>xri://=web</ProviderID> - <Type select='true'>xri://$res*auth*($v*2.0)</Type> - <Type select='true'>xri://$res*auth*($v*2.0)</Type> - <MediaType select='true'>application/xrds+xml</MediaType> - <URI append='qxri' priority='1'>https://resolve.freexri.com/ns/=web/</URI> - <URI append='qxri' priority='2'>http://resolve.freexri.com/ns/=web/</URI> - </Service> - <Service priority='10'> - <Type select='true'>http://openid.net/signon/1.0</Type> - <Type select='true'>http://specs.openid.net/auth/2.0/signon</Type> - <Path select='true'>(+login)</Path> - <Path match='default' select='false'/> - <MediaType match='default' select='false'/> - <URI append='none' priority='2'>http://authn.fullxri.com/authentication/</URI> - <URI append='none' priority='1'>https://authn.fullxri.com/authentication/</URI> - </Service> - <Service priority='10'> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='null' select='false'/> - <Path select='true'>(+contact)</Path> - <Path match='null' select='false'/> - <MediaType match='default' select='false'/> - <URI append='qxri'>http://contact.fullxri.com/contact/</URI> - </Service> - <KeyInfo xmlns='http://www.w3.org/2000/09/xmldsig#'> - <X509Data> - <X509Certificate> -MIIExzCCA6+gAwIBAgIJAM+MlFr0Sth6MA0GCSqGSIb3DQEBBQUAMIGdMR8wHQYD -VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE -CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs -YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs -YWluLmVkdTAeFw0wNjA4MTcxOTU5NTNaFw0xMTA4MTYxOTU5NTNaMIGdMR8wHQYD -VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE -CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs -YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs -YWluLmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL6uFqas4dK6 -A2wTZL0viRQNJrPyFnFBDSZGib/2ijhgzed/vvmZIBM9sFpwahcuR5hvyKUe37/c -/RSZXoNDi/eiNOx4qb0l9UB6bd8qvc4V1PnLE7L+ZYcmwrvTKm4x8qXMgEv1wca2 -FPsreHNPdLiTUZ8v0tDTWi3Mgi7y47VTzJaTkcfmO1nL6xAtln5sLdH0PbMM3LAp -T1d3nwI3VdbhqqZ+6+OKEuC8gk5iH4lfrbr6C9bYS6vzIKrotHpZ3N2aIC3NMjJD -PMw/mfCuADfRNlHXgZW+0zyUkwGTMDea8qgsoAMWJGdeTIw8I1I3RhnbgLzdsNQl -b/1ZXx1uJRUCAwEAAaOCAQYwggECMB0GA1UdDgQWBBQe+xSjYTrlfraJARjMxscb -j36jvDCB0gYDVR0jBIHKMIHHgBQe+xSjYTrlfraJARjMxscbj36jvKGBo6SBoDCB -nTEfMB0GA1UEAxMWU3VwZXJ2aWxsYWluOiBUaGUgUm9vdDELMAkGA1UEBhMCVVMx -ETAPBgNVBAgTCE5ldyBZb3JrMQ8wDQYDVQQHEwZHb3RoYW0xIDAeBgNVBAoTF1N1 -cGVydmlsbGFpbiBVbml2ZXJzaXR5MScwJQYJKoZIhvcNAQkBFhhwZW5ndWluQHN1 -cGVydmlsbGFpbi5lZHWCCQDPjJRa9ErYejAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 -DQEBBQUAA4IBAQC4SPBDGYAxfbXd8N5OvG0drM7a5hjXfcCZpiILlPSRpxp79yh7 -I5vVWxBxUfolwbei7PTBVy7CE27SUbSICeqWjcDCfjNjiZk6mLS80rm/TdLrHSyM -+Ujlw9MGcBGaLI+sdziDUMtTQDpeAyQTaGVbh1mx5874Hlo1VXqGYNo0RwR+iLfs -x48VuO6GbWVyxtktkE2ypz1KLWiyI056YynydRvuBCBHeRqGUixPlH9CrmeSCP2S -sfbiKnMOGXjIYbvbsTAMdW2iqg6IWa/fgxhvZoAXChM9bkhisJQc0qD0J5TJQwgr -uEyb50RJ7DWmXctSC0b3eymZ2lSXxAWNOsNy - </X509Certificate> - </X509Data> - </KeyInfo> -</XRD>"; - this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> { - { "https://xri.net/@llli?_xrd_r=application/xrd%2Bxml;sep=false", llliResponse }, - { "https://xri.net/@llli*area?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaResponse }, - { "https://xri.net/@llli*area*canada.unattached?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedResponse }, - { "https://xri.net/@llli*area*canada.unattached*ada?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedAdaResponse }, - { "https://xri.net/=Web?_xrd_r=application/xrd%2Bxml;sep=false", webResponse }, - }); - this.VerifyCanonicalId("@llli", "@!72CD.A072.157E.A9C6"); - this.VerifyCanonicalId("@llli*area", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C"); - this.VerifyCanonicalId("@llli*area*canada.unattached", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41"); - this.VerifyCanonicalId("@llli*area*canada.unattached*ada", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01"); - this.VerifyCanonicalId("=Web", "=!91F2.8153.F600.AE24"); - } - - [TestMethod] - public void DiscoveryCommunityInameDelegateWithoutCanonicalID() { - this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> { - { "https://xri.net/=Web*andrew.arnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?> -<XRD xmlns='xri://$xrd*($v*2.0)'> - <Query>*andrew.arnott</Query> - <Status cid='absent' code='100'>Success</Status> - <ServerStatus code='100'>Success</ServerStatus> - <Expires>2008-07-14T03:30:59.722Z</Expires> - <ProviderID>=!91F2.8153.F600.AE24</ProviderID> - <Service> - <Type select='true'>http://openid.net/signon/1.0</Type> - <Path select='true'>(+login)</Path> - <Path match='default'/> - <MediaType match='default'/> - <URI append='none' priority='2'>http://www.myopenid.com/server</URI> - <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate> - </Service> - <Service> - <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='null'/> - <Path select='true'>(+contact)</Path> - <Path match='null'/> - <MediaType match='default'/> - <URI append='qxri'>http://contact.freexri.com/contact/</URI> - </Service> - <Service> - <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> - <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Path select='true'>(+index)</Path> - <Path match='default'/> - <MediaType match='default'/> - <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI> - </Service> - <Service> - <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> - <Type select='true'>http://openid.net/signon/1.0</Type> - <Path select='true'>(+login)</Path> - <Path match='default'/> - <MediaType match='default'/> - <URI append='none' priority='2'>http://authn.freexri.com/authentication/</URI> - <URI append='none' priority='1'>https://authn.freexri.com/authentication/</URI> - </Service> - <ServedBy>OpenXRI</ServedBy> -</XRD>" }, - { "https://xri.net/@id*andrewarnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?> -<XRD xmlns='xri://$xrd*($v*2.0)'> - <Query>*andrewarnott</Query> - <Status cid='absent' code='100'>Success</Status> - <ServerStatus code='100'>Success</ServerStatus> - <Expires>2008-07-14T03:31:00.466Z</Expires> - <ProviderID>@!B1E8.C27B.E41C.25C3</ProviderID> - <Service> - <Type select='true'>http://openid.net/signon/1.0</Type> - <Path select='true'>(+login)</Path> - <Path match='default'/> - <MediaType match='default'/> - <URI append='none' priority='2'>http://www.myopenid.com/server</URI> - <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate> - </Service> - <Service> - <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> - <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match='null'/> - <Path select='true'>(+contact)</Path> - <Path match='null'/> - <MediaType match='default'/> - <URI append='qxri'>http://contact.freexri.com/contact/</URI> - </Service> - <Service> - <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID> - <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Path select='true'>(+index)</Path> - <Path match='default'/> - <MediaType match='default'/> - <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI> - </Service> - <ServedBy>OpenXRI</ServedBy> -</XRD>" }, - }); - // Consistent with spec section 7.3.2.3, we do not permit - // delegation on XRI discovery when there is no CanonicalID present. - this.VerifyCanonicalId("=Web*andrew.arnott", null); - this.VerifyCanonicalId("@id*andrewarnott", null); - } - - [TestMethod, Ignore] // XRI parsing and normalization is not implemented (yet). + [TestCase, Ignore("XRI parsing and normalization is not implemented (yet).")] public void NormalizeCase() { Identifier id = "=!9B72.7dd1.50a9.5ccd"; Assert.AreEqual("=!9B72.7DD1.50A9.5CCD", id.ToString()); } - - private ServiceEndpoint VerifyCanonicalId(Identifier iname, string expectedClaimedIdentifier) { - ServiceEndpoint se = iname.Discover(this.RequestHandler).FirstOrDefault(); - if (expectedClaimedIdentifier != null) { - Assert.IsNotNull(se); - Assert.AreEqual(expectedClaimedIdentifier, se.ClaimedIdentifier.ToString(), "i-name {0} discovery resulted in unexpected CanonicalId", iname); - Assert.IsTrue(se.ProviderSupportedServiceTypeUris.Count > 0); - } else { - Assert.IsNull(se); - } - return se; - } } } diff --git a/src/DotNetOpenAuth.Test/Test References/DotNetOpenAuth.accessor b/src/DotNetOpenAuth.Test/Test References/DotNetOpenAuth.accessor deleted file mode 100644 index 35adf51..0000000 --- a/src/DotNetOpenAuth.Test/Test References/DotNetOpenAuth.accessor +++ /dev/null @@ -1 +0,0 @@ -DotNetOpenAuth.dll diff --git a/src/DotNetOpenAuth.Test/TestBase.cs b/src/DotNetOpenAuth.Test/TestBase.cs index c6508f6..8aeca2d 100644 --- a/src/DotNetOpenAuth.Test/TestBase.cs +++ b/src/DotNetOpenAuth.Test/TestBase.cs @@ -5,38 +5,43 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test { + using System; using System.IO; using System.Reflection; using System.Web; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth.Messages; using log4net; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; /// <summary> /// The base class that all test classes inherit from. /// </summary> public class TestBase { - /// <summary> - /// The full path to the directory that contains the test ASP.NET site. - /// </summary> - internal static readonly string TestWebDirectory = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\..\src\DotNetOpenAuth.TestWeb")); - private MessageDescriptionCollection messageDescriptions = new MessageDescriptionCollection(); /// <summary> - /// Gets or sets the test context which provides - /// information about and functionality for the current test run. - /// </summary> - public TestContext TestContext { get; set; } - - /// <summary> /// Gets the logger that tests should use. /// </summary> internal static ILog TestLogger { get { return TestUtilities.TestLogger; } } + /// <summary> + /// Gets the full path to the directory that contains the test ASP.NET site. + /// </summary> + internal string TestWebDirectory { + get { + // System.IO.Path.GetDirectoryName(new System.Uri(basePath).LocalPath) + string basePath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath); + string relativePath = @"src\DotNetOpenAuth.TestWeb"; + for (int i = 0; !Directory.Exists(Path.Combine(basePath, relativePath)) && i < 4; i++) { + relativePath = "..\\" + relativePath; + } + return Path.GetFullPath(relativePath); + } + } + internal MessageDescriptionCollection MessageDescriptions { get { return this.messageDescriptions; } } @@ -44,7 +49,7 @@ namespace DotNetOpenAuth.Test { /// <summary> /// The TestInitialize method for the test cases. /// </summary> - [TestInitialize] + [SetUp] public virtual void SetUp() { log4net.Config.XmlConfigurator.Configure(Assembly.GetExecutingAssembly().GetManifestResourceStream("DotNetOpenAuth.Test.Logging.config")); MessageBase.LowSecurityMode = true; @@ -55,7 +60,7 @@ namespace DotNetOpenAuth.Test { /// <summary> /// The TestCleanup method for the test cases. /// </summary> - [TestCleanup] + [TearDown] public virtual void Cleanup() { log4net.LogManager.Shutdown(); } diff --git a/src/DotNetOpenAuth.Test/UriUtilTests.cs b/src/DotNetOpenAuth.Test/UriUtilTests.cs index 29e740d..a2cf1a2 100644 --- a/src/DotNetOpenAuth.Test/UriUtilTests.cs +++ b/src/DotNetOpenAuth.Test/UriUtilTests.cs @@ -9,11 +9,11 @@ namespace DotNetOpenAuth.Test { using System.Collections.Generic; using System.Linq; using System.Text; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class UriUtilTests { - [TestMethod] + [TestCase] public void QueryStringContainPrefixedParametersNull() { Assert.IsFalse(UriUtil.QueryStringContainPrefixedParameters(null, "prefix.")); } diff --git a/src/DotNetOpenAuth.Test/UtilTests.cs b/src/DotNetOpenAuth.Test/UtilTests.cs index 5ea4d0c..0930e8d 100644 --- a/src/DotNetOpenAuth.Test/UtilTests.cs +++ b/src/DotNetOpenAuth.Test/UtilTests.cs @@ -9,14 +9,14 @@ namespace DotNetOpenAuth.Test { using System.Collections.Generic; using System.Linq; using System.Text; - using Microsoft.VisualStudio.TestTools.UnitTesting; + using NUnit.Framework; - [TestClass] + [TestFixture] public class UtilTests { /// <summary> /// Verifies ToStringDeferred generates a reasonable string for an empty, multi-line list. /// </summary> - [TestMethod] + [TestCase] public void ToStringDeferredEmptyMultiLine() { Assert.AreEqual("[]", Util.ToStringDeferred(Enumerable.Empty<string>(), true).ToString()); } diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index 569a7bc..f6f3d03 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -1,16 +1,11 @@ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth", "DotNetOpenAuth\DotNetOpenAuth.csproj", "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.Test", "DotNetOpenAuth.Test\DotNetOpenAuth.Test.csproj", "{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}" -EndProject +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20B5E173-C3C4-49F8-BD25-E69044075B4D}" ProjectSection(SolutionItems) = preProject ..\build.proj = ..\build.proj - DotNetOpenAuth.vsmdi = DotNetOpenAuth.vsmdi + ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent = ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent ..\LICENSE.txt = ..\LICENSE.txt - LocalTestRun.testrunconfig = LocalTestRun.testrunconfig ..\doc\README.Bin.html = ..\doc\README.Bin.html ..\doc\README.html = ..\doc\README.html ..\samples\README.html = ..\samples\README.html @@ -32,11 +27,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{B4C6F647-C046-4B54-BE12-7701C4119EE7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OpenID", "OpenID", "{034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth", "OAuth", "{1E2CBAA5-60A3-4AED-912E-541F5753CDC6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InfoCard", "InfoCard", "{8A5CEDB9-7F8A-4BE2-A1B9-97130F453277}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{E9ED920D-1F83-48C0-9A4B-09CCE505FE6D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project Templates", "Project Templates", "{B9EB8729-4B54-4453-B089-FE6761BA3057}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth", "DotNetOpenAuth\DotNetOpenAuth.csproj", "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.Test", "DotNetOpenAuth.Test\DotNetOpenAuth.Test.csproj", "{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.ApplicationBlock", "..\samples\DotNetOpenAuth.ApplicationBlock\DotNetOpenAuth.ApplicationBlock.csproj", "{AA78D112-D889-414B-A7D4-467B34C7B663}" EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "DotNetOpenAuth.TestWeb", "DotNetOpenAuth.TestWeb", "{47A84EF7-68C3-4D47-926A-9CCEA6518531}" +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "DotNetOpenAuth.TestWeb", "DotNetOpenAuth.TestWeb\", "{47A84EF7-68C3-4D47-926A-9CCEA6518531}" ProjectSection(WebsiteProperties) = preProject - TargetFramework = "3.5" + TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5" ProjectReferences = "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}|DotNetOpenAuth.dll;{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}|DotNetOpenAuth.Test.dll;" Debug.AspNetCompiler.VirtualPath = "/DotNetOpenAuth.TestWeb" Debug.AspNetCompiler.PhysicalPath = "DotNetOpenAuth.TestWeb\" @@ -61,9 +70,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdProviderWebForms", ". EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdProviderMvc", "..\samples\OpenIdProviderMvc\OpenIdProviderMvc.csproj", "{AEA29D4D-396F-47F6-BC81-B58D4B855245}" EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "InfoCardRelyingParty", "..\samples\InfoCardRelyingParty", "{6EB90284-BD15-461C-BBF2-131CF55F7C8B}" +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "InfoCardRelyingParty", "..\samples\InfoCardRelyingParty\", "{6EB90284-BD15-461C-BBF2-131CF55F7C8B}" ProjectSection(WebsiteProperties) = preProject - TargetFramework = "3.5" + TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5" ProjectReferences = "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}|DotNetOpenAuth.dll;" Debug.AspNetCompiler.VirtualPath = "/InfoCardRelyingParty" Debug.AspNetCompiler.PhysicalPath = "..\samples\InfoCardRelyingParty\" @@ -81,15 +90,16 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "InfoCardRelyingParty", "..\ Release.AspNetCompiler.Debug = "False" VWDPort = "59719" DefaultWebSiteLanguage = "Visual Basic" + StartServerOnDebug = "false" EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdRelyingPartyMvc", "..\samples\OpenIdRelyingPartyMvc\OpenIdRelyingPartyMvc.csproj", "{07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdRelyingPartyWebForms", "..\samples\OpenIdRelyingPartyWebForms\OpenIdRelyingPartyWebForms.csproj", "{1E8AEA89-BF69-47A1-B290-E8B0FE588700}" EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OpenIdRelyingPartyClassicAsp", "..\samples\OpenIdRelyingPartyClassicAsp", "{BBACD972-014D-478F-9B07-56B9E1D4CC73}" +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OpenIdRelyingPartyClassicAsp", "..\samples\OpenIdRelyingPartyClassicAsp\", "{BBACD972-014D-478F-9B07-56B9E1D4CC73}" ProjectSection(WebsiteProperties) = preProject - TargetFramework = "2.0" + TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5" Debug.AspNetCompiler.VirtualPath = "/OpenIdRelyingPartyClassicAsp" Debug.AspNetCompiler.PhysicalPath = "..\samples\OpenIdRelyingPartyClassicAsp\" Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\OpenIdRelyingPartyClassicAsp\" @@ -105,81 +115,38 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OpenIdRelyingPartyClassicAs Release.AspNetCompiler.FixedNames = "false" Release.AspNetCompiler.Debug = "False" VWDPort = "10318" + StartServerOnDebug = "false" EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthConsumerWpf", "..\samples\OAuthConsumerWpf\OAuthConsumerWpf.csproj", "{6EC36418-DBC5-4AD1-A402-413604AA7A08}" - ProjectSection(ProjectDependencies) = postProject - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191} = {7ADCCD5C-AC2B-4340-9410-FE3A31A48191} - EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OpenID", "OpenID", "{034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth", "OAuth", "{1E2CBAA5-60A3-4AED-912E-541F5753CDC6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdOfflineProvider", "..\samples\OpenIdOfflineProvider\OpenIdOfflineProvider.csproj", "{5C65603B-235F-47E6-B536-06385C60DE7F}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InfoCard", "InfoCard", "{8A5CEDB9-7F8A-4BE2-A1B9-97130F453277}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFormsRelyingParty", "..\projecttemplates\WebFormsRelyingParty\WebFormsRelyingParty.csproj", "{A78F8FC6-7B03-4230-BE41-761E400D6810}" EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OAuthConsumer", "..\samples\OAuthConsumer", "{9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}" - ProjectSection(WebsiteProperties) = preProject - TargetFramework = "3.5" - ProjectReferences = "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}|DotNetOpenAuth.dll;{AA78D112-D889-414B-A7D4-467B34C7B663}|DotNetOpenAuth.ApplicationBlock.dll;" - Debug.AspNetCompiler.VirtualPath = "/OAuthConsumer" - Debug.AspNetCompiler.PhysicalPath = "..\samples\OAuthConsumer\" - Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthConsumer\" - Debug.AspNetCompiler.Updateable = "true" - Debug.AspNetCompiler.ForceOverwrite = "true" - Debug.AspNetCompiler.FixedNames = "false" - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.VirtualPath = "/OAuthConsumer" - Release.AspNetCompiler.PhysicalPath = "..\samples\OAuthConsumer\" - Release.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthConsumer\" - Release.AspNetCompiler.Updateable = "true" - Release.AspNetCompiler.ForceOverwrite = "true" - Release.AspNetCompiler.FixedNames = "false" - Release.AspNetCompiler.Debug = "False" - VWDPort = "59721" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RelyingPartyLogic", "..\projecttemplates\RelyingPartyLogic\RelyingPartyLogic.csproj", "{17932639-1F50-48AF-B0A5-E2BF832F82CC}" + ProjectSection(ProjectDependencies) = postProject + {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} = {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} EndProjectSection EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OAuthServiceProvider", "..\samples\OAuthServiceProvider", "{7ADCCD5C-AC2B-4340-9410-FE3A31A48191}" - ProjectSection(WebsiteProperties) = preProject - TargetFramework = "3.5" - ProjectReferences = "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}|DotNetOpenAuth.dll;" - Debug.AspNetCompiler.VirtualPath = "/OAuthServiceProvider" - Debug.AspNetCompiler.PhysicalPath = "..\samples\OAuthServiceProvider\" - Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthServiceProvider\" - Debug.AspNetCompiler.Updateable = "true" - Debug.AspNetCompiler.ForceOverwrite = "true" - Debug.AspNetCompiler.FixedNames = "false" - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.VirtualPath = "/OAuthServiceProvider" - Release.AspNetCompiler.PhysicalPath = "..\samples\OAuthServiceProvider\" - Release.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthServiceProvider\" - Release.AspNetCompiler.Updateable = "true" - Release.AspNetCompiler.ForceOverwrite = "true" - Release.AspNetCompiler.FixedNames = "false" - Release.AspNetCompiler.Debug = "False" - VWDPort = "65169" - VWDDynamicPort = "false" - EndProjectSection +Project("{C8D11400-126E-41CD-887F-60BD40844F9E}") = "RelyingPartyDatabase", "..\projecttemplates\RelyingPartyDatabase\RelyingPartyDatabase.dbproj", "{2B4261AC-25AC-4B8D-B459-1C42B6B1401D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdOfflineProvider", "..\samples\OpenIdOfflineProvider\OpenIdOfflineProvider.csproj", "{5C65603B-235F-47E6-B536-06385C60DE7F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcRelyingParty", "..\projecttemplates\MvcRelyingParty\MvcRelyingParty.csproj", "{152B7BAB-E884-4A59-8067-440971A682B3}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{E9ED920D-1F83-48C0-9A4B-09CCE505FE6D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoRelyingParty", "..\samples\OpenIdWebRingSsoRelyingParty\OpenIdWebRingSsoRelyingParty.csproj", "{B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project Templates", "Project Templates", "{B9EB8729-4B54-4453-B089-FE6761BA3057}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoProvider", "..\samples\OpenIdWebRingSsoProvider\OpenIdWebRingSsoProvider.csproj", "{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFormsRelyingParty", "..\projecttemplates\WebFormsRelyingParty\WebFormsRelyingParty.csproj", "{A78F8FC6-7B03-4230-BE41-761E400D6810}" +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "OpenIdRelyingPartyWebFormsVB", "..\samples\OpenIdRelyingPartyWebFormsVB\OpenIdRelyingPartyWebFormsVB.vbproj", "{F289B925-4307-4BEC-B411-885CE70E3379}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RelyingPartyLogic", "..\projecttemplates\RelyingPartyLogic\RelyingPartyLogic.csproj", "{17932639-1F50-48AF-B0A5-E2BF832F82CC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthConsumer", "..\samples\OAuthConsumer\OAuthConsumer.csproj", "{9529606E-AF76-4387-BFB7-3D10A5B399AA}" ProjectSection(ProjectDependencies) = postProject - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} = {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} + {E135F455-0669-49F8-9207-07FCA8C8FC79} = {E135F455-0669-49F8-9207-07FCA8C8FC79} EndProjectSection EndProject -Project("{C8D11400-126E-41CD-887F-60BD40844F9E}") = "RelyingPartyDatabase", "..\projecttemplates\RelyingPartyDatabase\RelyingPartyDatabase.dbproj", "{2B4261AC-25AC-4B8D-B459-1C42B6B1401D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthServiceProvider", "..\samples\OAuthServiceProvider\OAuthServiceProvider.csproj", "{E135F455-0669-49F8-9207-07FCA8C8FC79}" EndProject Global - GlobalSection(TestCaseManagementSettings) = postSolution - CategoryFile = DotNetOpenAuth.vsmdi - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeAnalysis|Any CPU = CodeAnalysis|Any CPU Debug|Any CPU = Debug|Any CPU @@ -252,18 +219,6 @@ Global {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Debug|Any CPU.Build.0 = Debug|Any CPU {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.ActiveCfg = Release|Any CPU {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.Build.0 = Release|Any CPU - {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Release|Any CPU.Build.0 = Debug|Any CPU - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Release|Any CPU.Build.0 = Debug|Any CPU {5C65603B-235F-47E6-B536-06385C60DE7F}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU {5C65603B-235F-47E6-B536-06385C60DE7F}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU {5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -291,28 +246,68 @@ Global {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.Build.0 = Release|Any CPU {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.Deploy.0 = Release|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.Build.0 = Release|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.Build.0 = Release|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.Build.0 = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.Build.0 = Release|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {CD57219F-24F4-4136-8741-6063D0D7A031} = {20B5E173-C3C4-49F8-BD25-E69044075B4D} - {AA78D112-D889-414B-A7D4-467B34C7B663} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} - {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} + {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} + {AA78D112-D889-414B-A7D4-467B34C7B663} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} + {2A59DE0A-B76A-4B42-9A33-04D34548353D} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {AEA29D4D-396F-47F6-BC81-B58D4B855245} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {1E8AEA89-BF69-47A1-B290-E8B0FE588700} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {BBACD972-014D-478F-9B07-56B9E1D4CC73} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {2A59DE0A-B76A-4B42-9A33-04D34548353D} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {F289B925-4307-4BEC-B411-885CE70E3379} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {6EC36418-DBC5-4AD1-A402-413604AA7A08} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} - {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} - {7ADCCD5C-AC2B-4340-9410-FE3A31A48191} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} + {9529606E-AF76-4387-BFB7-3D10A5B399AA} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} + {E135F455-0669-49F8-9207-07FCA8C8FC79} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} + {6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} {5C65603B-235F-47E6-B536-06385C60DE7F} = {E9ED920D-1F83-48C0-9A4B-09CCE505FE6D} {A78F8FC6-7B03-4230-BE41-761E400D6810} = {B9EB8729-4B54-4453-B089-FE6761BA3057} {17932639-1F50-48AF-B0A5-E2BF832F82CC} = {B9EB8729-4B54-4453-B089-FE6761BA3057} {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} = {B9EB8729-4B54-4453-B089-FE6761BA3057} + {152B7BAB-E884-4A59-8067-440971A682B3} = {B9EB8729-4B54-4453-B089-FE6761BA3057} EndGlobalSection EndGlobal diff --git a/src/DotNetOpenAuth.vsmdi b/src/DotNetOpenAuth.vsmdi deleted file mode 100644 index 6ca6b0c..0000000 --- a/src/DotNetOpenAuth.vsmdi +++ /dev/null @@ -1,540 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006"> - <TestList name="Hosted ASP.NET tests" id="5a637793-05cb-476f-9f0c-18e7e7a4b205" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6"> - <Description>Tests that require hosting the test ASP.NET web site to run. These tests tend to be a bit slower than the rest due to the overhead of loading ASP.NET.</Description> - <TestLinks> - <TestLink id="896fd3db-5edb-ed16-b11a-e01769735f89" name="BadRequestsGenerateValidErrorResponsesHosted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a6d6decf-0c19-16d8-edf6-ee70e56877e1" name="AspHostBasicTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - </TestLinks> - </TestList> - <TestList name="Fast" id="7e880f0f-2224-4f4c-a555-910a3bc2c0e5" parentListId="f0eeb325-0558-48a3-9a99-952133d8148e"> - <Description>Fast running unit tests</Description> - <TestLinks> - <TestLink id="89de77d8-729a-7efe-9667-71b1f5d78859" name="CtorBadXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="300ae1f7-fc61-1d41-b262-f8c830b6e115" name="RemoveTest1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="12304550-5647-5e61-64b4-a5e051f20a03" name="IsExtensionSupported" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="734dd45c-6320-26a9-e412-62ecacfd285a" name="CtorNullAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="70c08ce3-cbd0-d553-61c0-a6d2ca203dc4" name="IsExtensionSupportedNullExtension" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1d5fb5a9-e15c-d99c-7a7e-95a4c4d123c2" name="DirectRequestsUsePost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6b218bf7-a4e9-8dac-d2c2-9bc3ee3ffc3e" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="74f2623d-98c5-7c2b-a2af-02dfa7ff5601" name="GetHttpVerbTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1c531011-403a-0821-d630-d5433d968f31" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="73c6c979-205d-2216-d98d-2dd136b352c6" name="UtcCreationDateConvertsToUniversal" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e7aacb49-62ef-637d-ada2-0a12d836414d" name="ExtensionFactory" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="49a266cf-4ab6-3fdc-f4fd-21533f42c7cb" name="CtorWithProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d12e8df0-1195-ab75-2275-7c8f854ddf98" name="UserSetupUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="21cf1f9a-063f-395a-f8aa-92c190c69146" name="SignaturesMatchKnownGood" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d570770a-74e4-50ec-8eb9-91bd81c093ad" name="ParseNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a9f7897c-b299-807b-0612-384732cd10c9" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4bec8d2-0531-34ab-8d50-bca260b58c61" name="ReadFromRequestWithContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a314e3b9-36a5-bfbb-3e15-e5003f22cf87" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0f80456a-5465-dd68-bfb0-ba27b676187c" name="EqualsTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1bfbe1e1-3827-824f-27ad-4c990b0e22ab" name="Defaults" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ca9f3da7-e19f-b58b-54fe-54fa56ab9556" name="AddByKeyAndValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d766edce-59de-a03d-830a-0f0477521cff" name="ApplyHeadersToResponseNullAspNetResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9fae8ab4-8436-eba1-3e4b-51511998fa8e" name="UnsolicitedAssertionRejected" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="84e718d7-bb82-e7d1-31be-471e2c154053" name="Item" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="889ba616-43dc-8a7f-ee13-46288969d617" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fdf5b3df-239b-26fd-c1a2-152057195b7e" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="555edc3b-5abf-7e46-b4f6-ddf44800b5df" name="SpreadSregToAXBasic" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="149a95cf-a538-f853-e11b-3133c15579c5" name="RequestTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f3cbbcda-49ff-fc43-140b-f362081654c3" name="CtorNullTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="352d9fd6-cf38-4b72-478f-e3e17ace55f5" name="NoValueLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fa52f2db-fc1e-ba31-cc5e-0bcc05998187" name="NoValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="65752c29-fa1f-7b88-bbec-5329af8db4d8" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cd1142a5-f77a-5626-a739-65eb0228bf7d" name="ProtocolDetection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="68532d6d-a0cf-5883-17e2-6060707ba9ae" name="DecodeOobToNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="188ce83b-3117-adb5-4b89-12f2b09be1de" name="CtorSimpleConsumer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="93c157e8-1293-3aff-f616-66502872b37d" name="DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f6ecb459-cc64-36ee-438c-4514e9413586" name="AddAttributeByPrimitives" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="03e293d0-dbe8-ad09-1ddd-de7be2cf9276" name="CopyTo" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c87bee54-0edd-1051-b5f8-2233692249ba" name="DiscoverCommunityInameCanonicalIDs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5fa10f12-3de5-1783-0a97-9802d5469dfa" name="AddAttributeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5ab06bb5-d047-8c3a-6b91-5951e0a55cc5" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="454165a2-c26e-5740-09a9-d234db052ba3" name="InvalidRealmNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="40e1121e-8ff3-df73-203b-04baab671a0c" name="ImplicitConversionToStringTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="779b1f99-fe67-185c-f165-66787bf6e39a" name="BasicEncodingTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="456c3468-9081-4879-7e7e-8299bd8c7f68" name="IsReadOnly" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d6951a97-9d0b-31c1-7a29-01fbb986c5a9" name="SpoofedClaimedIdDetectionSolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1cde79f8-99b7-7090-f898-ba96a607875f" name="IsReturnUrlDiscoverableValidButNoMatch" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="910f8448-5454-8ae5-cba3-690c7f375576" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="83aba528-c8ea-f464-177e-2ea8ae2cfd0b" name="Birthdates" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3c4e0438-94f5-a132-3949-8d94718e4839" name="PassThruCollection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b63c4b89-3889-6dcf-8890-c92fc44c0b10" name="VerifyBadTimestampIsRejected" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9126d9e0-14dc-490b-3cd3-d3e424d38f9e" name="BinarySerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4c399759-263f-5eba-8855-de14f197e647" name="QueryStringContainPrefixedParametersNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1757957f-17bb-ef9f-39f8-c008863ec033" name="AssuranceLevels" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c11e5541-0a92-85ab-4f90-0db7766ebdcb" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8aecb3a5-2cb5-143d-aa99-9514fa8dfacb" name="AddAttributeByValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ef6cebca-f8da-edf6-0217-8bb854710090" name="DiscoveryCommunityInameDelegateWithoutCanonicalID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b09311d4-4dea-6786-3e59-9c62fe16e301" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ad56539c-6156-5f62-a98a-b24ae0159cc6" name="XmlSerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="54eae9ed-bed1-eeda-b6ea-045c8f7e2ba5" name="SendIndirectMessage301GetNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2d0ee03a-f082-768c-a0db-574ac8efeffb" name="Valid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6c20a52a-bab7-e84e-faca-fd79ec5303d9" name="CtorCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7fb8d29c-c8ea-7f88-ed42-ae7368d6a429" name="CtorNullStore" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="50986611-9de6-a112-2fe8-691210989f45" name="IsTypeUriPresent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c63c9935-54a0-398a-f44b-214e17faf1f1" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="163c8ba8-f829-c21e-a5a1-3c4565ec4425" name="UnifyExtensionsAsSregNoExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="054484ce-12c5-83ad-49a4-b241cd81557d" name="ClaimedIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e8337858-a320-8aad-51aa-402e65a90b75" name="ReplayDetectionTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="507cd1b6-1010-0bca-cf7f-f96e3f4f6c6c" name="QueryBeforeSettingUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1dcbaac6-0b11-8d8f-50d7-237574abbab1" name="ToDictionaryWithSkippedNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4e3cef90-06f2-f5dd-3510-a8595b552af5" name="GetHttpVerbOutOfRangeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="715dcbdd-28f5-3c33-7d88-e0a1b648d89a" name="CreateRequestDumbMode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a1ff4ada-fe5d-d2f3-b7fb-8e72db02b3c3" name="Full" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="85a71d28-5f2f-75ce-9008-94982438bb5f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f334cc44-b2d0-2d67-358a-532def3bee80" name="ContainsKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fa05cc5f-2aaf-da22-ff52-caf1c3c6bb08" name="InsecureIdentifiersRejectedWithRequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c1c7e292-3e87-4fe5-1f6f-0ef2f86ebbce" name="GetHttpDeliveryMethodOutOfRangeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7debb527-142a-6ca6-3b9b-1e131c18e801" name="AccessTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="98e7a0f9-ab6c-7ff1-3a2c-00d8244e1bec" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a25369b4-b1b8-09a1-7a47-aacc1480e51c" name="FilePartAsFile" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="af7cb01c-950e-23d7-0f32-082b7af8b382" name="CtorNullToObject" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a260d196-066f-b0ae-a40e-fb9d962b28a4" name="XrdsDirectDiscovery_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="13acd546-c82e-324c-220d-34f42a6d705e" name="DeserializeSimple" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b56cdf04-0d29-8b13-468c-fb4b4258c619" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d474830d-3636-522c-1564-1b83e7a844d3" name="EmptyLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f44fb549-fc8a-7469-6eed-09d9f86cebff" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="63e5025b-5ccf-5f13-6e05-d1e44502a6e9" name="RequestBadPreferredScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d80bf2a0-bcd5-f1c1-4835-8e5ceb523387" name="GetPublicFacingUrlSSLForwarder3" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f20bd439-e277-dc27-4ec4-5d5949d1c6bf" name="RequestUsingAuthorizationHeaderScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3fc3ac8d-7772-b620-0927-f4bd3a24ce2f" name="SendNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5f3758b3-1410-c742-e623-b964c01b0633" name="AuthenticationTimeUtcConvertsToUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9d4a230d-9e74-dc1b-ecdc-bf875b56e1b3" name="CtorNullVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f8d8d555-7a04-ab6e-918a-3dae32f4b52b" name="IsExtensionSupportedNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7b1fb2c4-39c0-0d39-700c-96d992f5a01f" name="AuthenticationTimeUtcSetUnspecified" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="00089858-d849-1e5f-4fb5-31d8d0590233" name="VerifyArgumentNotNullThrows" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="54a65e0b-1857-72b9-797b-fe3d9a082131" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f17424d2-ed4b-1ea0-a339-733f5092d9d0" name="MaximumAuthenticationAgeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d067c55c-3715-ed87-14a2-c07349813c94" name="IsDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8fc08a6d-6dcf-6256-42ff-073d4e4b6859" name="RequireDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2a7b77c3-27d5-7788-e664-5d20118d223b" name="OPRejectsHttpNoEncryptionAssociateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1f46ce86-bc66-3f5c-4061-3f851cf6dd7f" name="HtmlDiscover_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f9d176e-4137-63bd-ee2a-6b79fde70d0d" name="Clear" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fc7af2d7-6262-d761-335b-ef3ec029484d" name="DeserializeVerifyElementOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="df159af7-abf5-089c-b592-e6f535dab1c1" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="577b8522-8516-4f62-22db-76227bf82f4c" name="UserSetupUrlNotRequiredInV1SetupOrV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ac4ff1af-8333-e54e-0322-27d8824d7573" name="RequestUsingAuthorizationHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e78ab82c-3b49-468a-b2ad-ca038e98ff07" name="GetEnumerator" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8e86c2fd-24b9-44c5-7cda-d66aa7cd4418" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="06ec5bce-5a78-89c3-0cda-fa8bddfea27d" name="SetCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e2287de6-cbd2-4298-3fb8-297013749e70" name="SendIndirectMessageFormPostNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="db8d66cc-8206-57cc-0ce5-c8117813d77c" name="UnifyExtensionsasSregFromSchemaOpenIdNet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ef8a2274-4e58-0dde-4c5c-7f286865fc3a" name="SendReplayProtectedMessageSetsNonce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2eb7c3dd-08aa-bfc0-edc8-e14bd82f7507" name="IdentifierTextInteraction" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0d99e0a9-295e-08a6-bc31-2abb79c00ff8" name="IsReturnUrlDiscoverableRequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f41ce7ab-5500-7eea-ab4d-8c646bffff23" name="HttpSchemePrepended" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="38239ff0-1dfd-1116-55df-2790243dc768" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="04be6602-31a2-f4ae-8fdb-b9ad2ac370be" name="PrepareMessageForReceiving" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2c2b48d0-8009-e7e0-9ff4-34f9973f59da" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5dd2e6c9-ff0f-48de-3a1a-cbab61370843" name="SetCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="182203f3-5a16-b736-ea8c-b59f6bf7df66" name="InvalidRealmTwoWildcards2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4fd5baa2-8f39-8bf6-db8f-aa92592bfc06" name="CtorRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="385c302d-b546-c164-2a59-2e35f75d7d60" name="RemoveStructDeclaredProperty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="870cce9d-5b17-953c-028f-827ec8b56da2" name="GetInvalidMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="97f0277a-86e6-5b5a-8419-c5253cabf2e0" name="UserAuthorizationUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="643add47-e9f3-20b8-d8e0-69e3f8926d33" name="CreateRequestsWithEndpointFilter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d66a3b7a-1738-f6b3-aed1-e9bc80734ae9" name="CtorNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f3f84a10-317f-817a-1988-fddc10b75c20" name="AddTwoAttributes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5e2555a0-c07a-6488-c0e9-40ececd5021f" name="Serbian" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e03f0038-5bb7-92f2-87a7-00a7d2c31a77" name="MessageExpirationWithoutTamperResistance" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1f3ea08b-9880-635f-368f-9fcd3e25f3cd" name="ReadFromRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9104f36f-6652-dcbb-a8ae-0d6fc34d76ed" name="AddCallbackArgumentClearsPreviousArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f49bcd49-76fb-bfea-b119-4e0f70159f80" name="OpenIdProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4b313bb-cebc-a854-ffbd-6c955d850a05" name="VerifyGoodTimestampIsAccepted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="77047207-0571-72d5-71bd-586b878bcc0c" name="Base64Member" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f919a731-cc5c-88c6-5582-639b272d64fc" name="IsReturnUrlDiscoverableValidResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a8bd3730-1660-dca9-87ec-23bc9dc39ab9" name="CtorGoodXriSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4a00f3ab-f405-95a7-d745-2fcf7787eb56" name="GetNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4537b23-bb5e-5c6f-da53-64b34472f0dc" name="ChannelGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="75fa4664-bb0e-3a54-de29-c18ac712b231" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="94ba9fd3-851d-13b2-e273-6294b167c13e" name="HttpsSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cbfeb75b-d031-7df3-c281-3c9e1c450042" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5460f9c6-ec9d-969d-5aff-b946d6776e25" name="CtorWithNullProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d5f4e610-eabe-1dc0-ab3f-7c9dcb17fcc3" name="CtorImpliedLocalIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="64d8c630-76c6-e420-937b-19c889dd7f59" name="CtorNonMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fba4d9a6-d8c7-a063-7c07-4a27c38c94a9" name="InvalidRealmBadWildcard3" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="be41e9c1-ecde-cc80-37d0-4126225e4cda" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="48115dc0-1323-bab0-c540-695a2160e0a3" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a79e43c9-ad5a-5543-51ff-22271ec87ab0" name="PrepareMessageForSendingNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7ca16e07-126d-58ac-2ac5-a09a8bf77592" name="InvalidRealmBadWildcard1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="832dbf28-5bf2-bd95-9029-bf798349d917" name="GetCallbackArguments" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="11108b79-f360-9f7c-aebc-2d11bebff96a" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="53cbbf4a-89d3-122b-0d88-662f3022ce26" name="OpenIdMaxAuthenticationTime" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="20646985-c84a-db8e-f982-ec55d61eaacd" name="ResponseNonceSetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c2c78c43-7f50-ffc3-affb-e60de2b76c94" name="CreateQueryStringNullDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="dc6af36d-0efc-9291-a603-2af7bac2a269" name="ErrorMessagesAsHttp400" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cc584a29-684c-75e8-3d77-96201d9ba537" name="ProviderTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ef20222d-b2e2-d593-17fa-512041020643" name="InvalidRealmNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b2e1bba0-ab24-cdd5-906c-a3655814ab2d" name="SendSetsTimestamp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f1a3fc4-77ec-2ae3-668c-9e18f9ab0ebe" name="SendIndirectMessage301Get" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e4403d9e-73c1-967d-345c-4a2c83880d4e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6ce37652-2f47-6952-fb6d-568c2ca85224" name="TransformAXFormatTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a145f430-8062-5ad7-0cf5-b51eba0f8de7" name="HttpsSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a6ea74e5-8681-4eb4-a51b-5051e5f7603c" name="NonFieldOrPropertyMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="066ce22f-103c-56ee-0250-d9e28d43ffcd" name="Values" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="bb542259-4c10-4b88-1b3c-f842b0bb49a9" name="ImmediateVsSetupModes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="71564ca7-7845-92b3-7433-2f2beeb6b9f7" name="VerifyNonZeroLengthOnNonEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7ea157db-cf32-529f-f1d3-b3351f17725a" name="CtorSimpleServiceProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c905ca57-e427-3833-c2dd-17ca9f6962cd" name="SendIndirectMessageFormPost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="50594141-1a00-b4ab-d794-5b06e67327e5" name="IsTypeUriPresentNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c7f6459d-9e6e-b4bc-cae8-65f5a3785403" name="SendIndirectMessageNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="aa79cdf5-e0bc-194e-fdbb-78369c19c30f" name="ConstantFieldMemberInvalidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="dd9e3279-2d7e-e88e-ccfa-ef213055fc3d" name="SendDirectedNoRecipientMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b384002f-26a9-7dde-c3f6-9ceff34dd8e2" name="GetRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5a77a48f-00d6-da6f-5ef7-c897ebf8fe6b" name="EscapeUriDataStringRfc3986Tests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="81f670d0-d314-c53c-9d91-c0765dfc30c1" name="MessagePartsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="efd570c9-5e74-17e4-f332-ac257c8e8aff" name="RealmReturnToMismatchV1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fda58c48-e03a-73a3-4294-9a49e776ffb6" name="CtorWithTextMessageAndInnerException" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e7a41771-7dda-be44-0755-e06300f3cd92" name="IsSaneTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ae384709-e9a4-0142-20ba-6adb6b40b3e2" name="CtorStringHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3b70dd09-384d-5b99-222b-dc8ce8e791f2" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f787ae5d-b8fc-0862-a527-9157d11bbed7" name="UntrustedWebRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8df5d75f-bd4d-ce4e-2faf-6106b623de42" name="AddAttributeRequestStrangeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="51a08d94-c327-4d28-1f0c-f7920ea54870" name="ValidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="bc3e979b-09ea-c45d-5714-2d1fb00244cf" name="IncomingMessageMissingRequiredParameters" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="051a85ed-eef9-9437-507d-d6208b6a8f74" name="DiscoveryWithRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="be6a14aa-c0d9-cf61-286a-236b92239597" name="EnumerableCache" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5a4df395-962e-0b7c-de71-abcb7e8930db" name="CreateFiltersDelegatingIdentifiers" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="bf73c7f2-33b1-8e18-c4f6-cb8609388754" name="DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="feb0a53e-1592-b878-b70c-1a272d9c6908" name="SpreadSregToAxTargetedAtOPFormat" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e071a119-c7e9-1a55-b132-72e161fea598" name="CtorAndProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a6e464af-42df-1ba4-17e5-b955352664b5" name="RPOnlyRenegotiatesOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0290975f-02ce-d8a7-d723-5dae623cab46" name="CtorNullTokenManager" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3b535521-90c8-7f49-545f-bcfc4ad16d40" name="UnresponsiveProvidersComeLast" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4bd86299-18d7-abbe-e5d2-1afad17279e9" name="Parse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0443f5f8-aa08-80d5-dcc6-261802debe5a" name="XrdsDirectDiscovery_10" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6c95f443-463e-2856-f500-b9029645e44c" name="RequestNullRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3744e1f1-6be1-f27f-78e9-5410d356ccf4" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="afafb5ef-662e-2da3-35b8-1d67bb0d79ce" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d0a92f93-9bb4-1821-81cf-e9b50e3e7d62" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="17267cde-a296-8293-5bd1-9ca629817e4b" name="OpenIdRelyingParty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5a21f2d2-f916-fb16-1460-caff8c24a9fc" name="GetHttpDeliveryMethodTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cab73921-470b-331f-e601-b44805b67c81" name="GetAttributeValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="14ce54ee-5507-ac70-5514-99b7b83ba3d6" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ebd84587-bbc2-9889-c500-b6fbdf2bf209" name="GetRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="565140c9-c9fe-9466-1e39-740d7e368cb5" name="TryParse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="782d64c8-46af-a624-b3f6-a65aeaa57bfe" name="LastLineNotTerminatedLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="30a8eab6-6423-26af-da1a-ec304935fe43" name="RemoveNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e9bc3f63-aeb1-d84d-8abc-fc6ed77955e6" name="SignedResponsesIncludeExtraDataInSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9e209599-5924-f624-48de-ed31588cb425" name="EncodeDecode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3027bfe5-3612-7089-16cc-d6a2a556a41f" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="95e1fc36-2500-2721-1919-35e9e8349a1c" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9af943f7-b289-1a24-8e3e-bfbd7a55b4c7" name="CtorGoodUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ae1ef27c-fbfe-c57e-a1e0-c1ef9de4ea23" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="196be55e-a3e5-adf3-9f15-13ba6cce0701" name="ValidRealmsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="bb68f1dc-3854-fc11-2ea6-d45f892d76fa" name="NistAssuranceLevelSetVarious" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="88aaa032-b18a-b334-937b-66837c5f987c" name="AssociateRenegotiateBitLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="dd5be0e2-a1fc-3369-0b11-78b728eeaba5" name="CtorNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0362a92c-a21c-f718-6b1e-3d154c14acd0" name="RequestUsingPost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="264cd371-e830-c09b-5511-18f54d4c69d5" name="RespondSimpleValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4a009f39-66b1-9cc5-ea8b-13b75ab22a5b" name="ContainsKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9bdc56c0-33ce-b46c-4031-bd3252b499a6" name="PrivateAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="32604ca2-2577-9c33-f778-ff7e4c985ce5" name="RequestTokenUriWithOAuthParametersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fdf439d0-3b74-4d32-d395-d5a2559ed88b" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3df1f62b-4fb4-d399-cf7f-40b72001d9d6" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7c688207-e58a-86ed-90d0-687e28ee7e38" name="MultiPartPostAscii" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8b11aa63-4c0f-41ff-f70c-882aacf939fe" name="CtorCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="83271647-7da8-70b1-48a3-f1253a636088" name="IsExtensionSupportedEmptyString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f583b298-139a-e733-dde6-f9dc4b73d4bf" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b671ea40-3b8c-58d5-7788-f776810c49be" name="UnicodeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5435ab79-de25-e2fc-0b2d-b05d5686d27d" name="IsUrlWithinRealmTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="501fa941-c1ac-d4ef-56e7-46827788b571" name="GetRequestNoContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="01e33554-07cc-ff90-46f8-7d0ca036c9f6" name="ToDictionaryNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="33dd2f79-cd69-f1c0-0e47-f7a17b5a8a8b" name="MultiPartPostMultiByteCharacters" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0215f125-3936-484e-a8d0-d940d85bbc27" name="AppendQueryArgsNullDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7c048c58-c456-3406-995f-adb742cc2501" name="DeserializeInvalidMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="704a32d0-3f50-d462-f767-fd9cf1981b7f" name="ProviderVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f362baf3-da5b-1b8c-39ae-7c9b2051270a" name="AuthenticationTimeUtcSetUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="decb3fef-ef61-6794-5bc6-f7ff722a146e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3bb818b4-5423-ad91-8cd9-8606ec85d2cb" name="ReadFromRequestAuthorizationScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="30f3c12b-e510-de63-5acd-ae8e32866592" name="CreateQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9302541c-9c30-9ce7-66db-348ee4e9f6ee" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="997253fb-7591-c151-1705-02976b400f27" name="AddAttributeTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9bf0528f-c3ab-9a38-fd8a-fd14bade0d0b" name="EnumerableCacheCurrentThrowsAfter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8346368c-9c8a-de76-18dd-5faeeac3917d" name="OPRejectsMismatchingAssociationAndSessionTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3d0effa3-894a-630c-02b0-ada4b5cef795" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3c438474-63f3-b56c-dcba-1ed923fcdbdd" name="CreateResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="47706bc6-7bee-0385-62b4-4f9cec6cc702" name="CtorWithTextMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2495fc9b-d766-5ae7-7324-f044c4ce1242" name="AddNullValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fb6c270f-ff72-73f4-b8b3-82851537427c" name="MultiVersionedMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="10245b55-8130-e0aa-e211-4a16fa14d0b1" name="ClearValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c9d67d40-1903-8319-0f7c-d70db4846380" name="SendWithoutAspNetContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="aa93fd70-1b5f-1ce7-c9dd-3c70f9e271ac" name="PostMultipart" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e97cee09-4163-d83f-f65f-14e424294172" name="ExtensionsAreIdentifiedAsSignedOrUnsigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4fd129a-a7c3-dc1e-2b4a-5059a4207a8a" name="Send" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cc0031b8-1fdb-cd87-97c1-c6f893c296e0" name="TooManyBindingElementsProvidingSameProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="152e7a3a-21f9-eabf-0065-08597a0cc9a6" name="AuthorizationHeaderScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="93041654-1050-3878-6b90-656a7e2e3cfd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e2ab77b2-a6dc-f165-1485-140b9b3d916f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="035cd43a-23d5-af91-12ee-0a0ce78b3548" name="XrdsDiscoveryFromHttpHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="dbf7855c-0cc6-309f-b5f5-022e0b95fe3b" name="QueryStringLookupWithoutQuery" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="abb0610a-c06f-0767-ac99-f37a2b573d1b" name="ParameterPrefix" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="32532d1f-d817-258d-ca72-021772bfc185" name="UriEncodeDecode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f3345901-2e76-34dd-32f1-0b312d6e1c1e" name="IsReturnUrlDiscoverableNotSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a2b3835e-8edb-89aa-ba6c-f10b28a3af81" name="ReadFromRequestQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1deb0ca9-923a-8ef7-7a24-d5d5af04acdf" name="SpecAppendixAExample" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b32b6295-d4a9-3369-f072-28a71e84d4e8" name="SerializationWithUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2fb2563b-b908-2fad-5efc-522a68c76780" name="ValidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ee052b02-681f-2303-3cc6-37f7b2319579" name="RequireAssociation" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fb0e478e-0f55-b257-75fe-2ab60b57292e" name="SendInvalidMessageTransport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a4aa113a-57b5-a52c-c4e3-f70d6702badb" name="Default" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ee7a04ba-0419-e08f-b838-01ec0f2a838e" name="UnsolicitedAssertion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="547cfee6-bbb4-6138-a114-fc0eb6cc94f6" name="PrivateAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5803e93d-e256-86ba-e10e-499d2f813c6d" name="Trivial" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="beb086e9-5eb7-fb8f-480a-70ede9efd70d" name="CreateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="78f622a3-750c-12c5-afc6-470c1bf71d85" name="ProtocolDetectionWithoutClues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0c1a0323-092a-34b3-1601-1f941569efab" name="CtorGoodXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8538caf8-48bd-7cf8-6ad8-15e1c3766f92" name="CtorNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e1958fc5-a979-88b2-b593-3bc89ad6ad4e" name="GetEnumeratorUntyped" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4893153-bb84-bf45-7889-8350a7e1db66" name="DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5218fba2-d1af-e1f4-7641-9ae1d4975430" name="DirectResponsesSentUsingKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a1a0178c-cd4a-1651-8535-3c9ee3d40821" name="ToDictionaryWithNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0f36556d-ece7-eb70-8597-a9d085165c2c" name="Sign" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="19d2219e-c04d-fa3a-5e26-92448f35f21d" name="RespondNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="eb932fc7-76c7-b63f-e1e6-a59dea8e4da1" name="AddAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a59c5dc0-de4d-8136-8545-2e2e9616de46" name="SerializationWithXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0aa1bc22-0b26-3977-5983-5dc4a454cea5" name="OptionalNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c205832e-711c-62d0-5f5e-78f1250ea7cc" name="AuthenticationTimeUtcSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3536ba12-fdb0-2ac9-3fef-00a2dd8e9a65" name="SharedAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e7b8770d-b26c-e7b3-e80e-fac46285f59d" name="PassThruList" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="14acb719-f090-018f-b870-9a5acb1d7179" name="AddAuthLevelTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="aef95d4e-ad69-0eca-6528-7fce78512336" name="EqualityTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1b66e135-bdab-c2ed-18d8-aa89b46a57fc" name="RPRejectsUnrecognizedAssociationType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1ea8bd07-75a5-bfc0-5f8c-1a78d04240c2" name="TryGetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7b89844d-f60a-fb66-c48d-e483864c66b5" name="RespondTooManyValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b9cda1a0-83cd-cf4b-b61f-4faa75fa37ba" name="ReceivedReplayProtectedMessageTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7f9c4a9e-de7a-555c-543d-db89b757588e" name="AppendQueryArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cf58b7b9-9718-f6cd-1839-fc53174598f2" name="IsExtensionSupportedNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9aa6a81e-c198-c0fd-0252-003b856b7674" name="ConstantFieldMemberValidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d6b120b7-fc16-6815-927e-af382cd44bbd" name="ReceivedInvalidSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b71d12f6-58a1-cf82-d06e-e57c0a3ea55c" name="RPRejectsUnencryptedSuggestion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="77e5af06-b02d-692e-b32f-40ea39e77fbd" name="FriendlyIdentifierForDisplay" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3c67903e-15ce-9ed4-34c8-f77059af79ca" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="87593646-8db5-fb47-3a5b-bf84d7d828c2" name="InvalidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="65f16786-7296-ee46-8a8f-82f18b211234" name="AddByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c891c6bc-da47-d4ab-b450-f3e3a0d6cba8" name="NoAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="47e8fae9-542d-1ebb-e17c-568cf9594539" name="RelativeUriDecodeFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f3af5fd8-f661-dc4f-4539-947b081a8b54" name="ReceivedReplayProtectedMessageJustOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a14ddf08-796b-6cf1-a9bf-856dd50520fa" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3aa4e498-fd14-8274-22da-895436c1659e" name="AssociateUnencrypted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5b4fee50-7c15-8c6b-3398-c82279646e5f" name="RequiredOptionalLists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="59295023-d248-e9c4-68b9-65f6ea38490c" name="VerifyArgumentNotNullDoesNotThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e137b84a-d2a7-9af6-d15d-a92417668ccf" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a94ee2ec-02df-b535-1d2e-0c5db9c76b49" name="ReceiveUnrecognizedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="88ae5661-da27-91c5-4d78-1f43cd716127" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cea48223-04e2-d336-0ac4-255c514bd188" name="RoundTripFullStackTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ba35acc7-78d2-6710-57ac-6843210d4202" name="UserSetupUrlRequiredInV1Immediate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f18b514c-4f78-5421-8bdf-8b0f1fdf2282" name="HandleLifecycle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1e2ae78c-d2f3-a808-2b82-eca9f9f2e458" name="Keys" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9173c754-a358-91cc-a8f0-2c2703a55da8" name="AssertionWithEndpointFilter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6badbaa8-33d1-13c4-c1f9-aef73a9ac5bf" name="InvalidRawBirthdate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0435e38a-71f2-d58d-9c07-d97d830a1578" name="ExtensionResponsesAreSigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f2ea001-a4f8-ff0d-5d12-74180e0bf610" name="HttpsSignatureVerificationNotApplicable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fe55cc74-98eb-c6c7-622f-77ad3e304c10" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ca5360e1-ca08-d00f-6ade-7c9441db4294" name="CreateQueryStringEmptyCollection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4735a071-3c06-509b-05f5-912ab0e39f13" name="InvalidRealmBadProtocol" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6fbd433d-cd54-b206-6df3-fbd591690a4d" name="HtmlDiscover_11" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="175b6eb8-a448-4e07-7fed-001355edc128" name="PassThruArray" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="777af676-ee70-0e16-799b-85b9ec33cd63" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d083396b-db68-1318-e62b-6dc9f89e26bd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f6a81c5-cd04-0ca0-22ee-d4213f9cf147" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="72d3f240-67f2-0b04-bd31-a99c3f7a8e12" name="SharedAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3cd9447e-9ffd-f706-37bb-e7eb5828e430" name="InvalidRealmEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="28fe030c-d36e-13cf-475c-7813210bf886" name="AddAttributeRequestAgain" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e344ba35-96b7-d441-c174-8c8b295fd157" name="AddCallbackArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2e23dc5a-93ea-11a5-d00d-02d294794e5f" name="AssociateDiffieHellmanOverHttps" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8d0df47c-c381-0487-6c19-77548ad7fc13" name="UnifyExtensionsAsSregWithBothSregAndAX" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2237b8ce-94ce-28c1-7eb2-14e59f47e926" name="UnifyExtensionsAsSregFromAXSchemaOrg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a2f79410-e593-9155-e03d-8168cbb9f091" name="GetPublicFacingUrlSSLForwarder1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="069995aa-4136-610b-3f41-df80a138c244" name="AppendQueryArgsNullUriBuilder" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="457d6b32-d224-8a06-5e34-dbef3e935655" name="HttpSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="309fdc0f-150c-5992-9a79-63be5f479d89" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2e1b27e8-2e3e-0290-2bee-d88e2914efd9" name="SpreadSregToAXNoExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c15c3ab5-e969-efc9-366d-78ebc43ce08f" name="Fetch" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7bf8e806-68a1-86bc-8d91-9a99d237d35c" name="CreateRequestMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a883dc73-d6be-e59a-6da2-0db1d4452679" name="BindingElementsOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5dcd69c3-e979-7316-4551-a73fe4645dcd" name="SecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f533bf9e-daa1-b26a-4789-372f3a9291d6" name="TryRequireSslAdjustsIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6ef9df5a-d069-0103-5260-593808f232da" name="XrdsDiscoveryFromHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="313faac6-6357-5468-2d4d-4c9fba001678" name="TryParseNoThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7cbe4350-38d0-db7e-335c-93d9398fc95b" name="ExtensionOnlyFacadeLevel" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f5cfa57-bcb4-39af-e769-2d7c34e2598e" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ff78d828-b437-aaeb-e48a-85a5ad1fe396" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="29e45877-ca7a-85de-5c39-6d43befe1a1e" name="DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c351c660-d583-d869-0129-2e312665d815" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f063a3c6-5a36-2801-53d7-5142416199a9" name="ImplicitConversionFromStringTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="809afd59-8f10-ce37-6630-06b59351a05a" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="02333934-cfea-2fb6-5e08-7a24be050f44" name="CreateRequestsOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5298ecb0-bcad-9022-8b93-87793eb2c669" name="UnsolicitedDelegatingIdentifierRejection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b97c4a66-a832-401c-a98b-6342ad7bcdcf" name="LanguagePreferenceEncodingDecoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="068dcefa-8f2b-52c3-fe79-576c84c5648b" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="315b5857-7697-8222-f94c-f6f10d539491" name="BaseSignatureStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cd219db4-4f6e-8ff4-f957-c8428d38c118" name="HttpSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="864578a5-61a2-bc5d-1d19-17093885bea3" name="InvalidRealmTwoWildcards1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8a5c9404-1e77-68cf-229a-ef7ed413e6e7" name="OptionalNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ab653060-5bec-3dc6-78ee-a5ef7d393b1d" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2599d559-d036-5dd2-0b5b-fb229c3bf486" name="InvalidRealmBadWildcard2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9684f7bf-cdda-a2c5-0822-29cb0add3835" name="ResponseNonceGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e0b191fa-aa68-bd0f-c9d0-f8cba23003f3" name="RequestUsingHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f341c47f-ac23-2fc0-fcf5-c999fe8d2611" name="SetBodyToByteStream" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="643d722c-2c2b-fbd8-a499-5a852ef14dc7" name="PrepareMessageForSending" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="79c0d33a-f7f2-fd69-1b4d-57ee3ece2cca" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="937b85f4-1ef3-84d1-a567-8bba079a33a9" name="Properties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="53bc1962-e7c2-04b6-cafa-0f6fde7592a9" name="ReadFromRequestNoContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c057a3e5-b527-62a9-c19b-abb82e6be621" name="SendIndirectMessage301GetEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0dc9284e-cba4-9d87-8955-19639578c70d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="384fecbf-f18e-edcb-a2eb-fb0322f031aa" name="ApplyHeadersToResponseNullListenerResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e3a3b3b6-e05f-0a99-e20c-af91a9065819" name="AssociateRequestDeterminedBySecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d3c4624f-f78a-2ff3-199a-77c922059718" name="Best" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7f3144c7-95a1-affa-1a37-9e6169c19be6" name="SharedAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="aedfde98-4357-5b63-7dca-cced838ee416" name="Provider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b71e8878-b20e-5d96-bce4-7f10831ceaf8" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ddf4f3ec-07bb-09e8-b5e8-0837cb8cb684" name="IsReturnUrlDiscoverableNoResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a18ae750-318b-bb1f-c2b3-c31da845c085" name="Count" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5b451648-5ca1-4395-333d-bbcb098f4a45" name="NoRequestedExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cb9a8325-abf5-5d97-a94e-a6d34f2b51e1" name="AssociateRenegotiateLimitedByRPSecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5271f941-565f-5977-6884-82cef09161db" name="ParseEndUserSuppliedXriIdentifer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7cf52613-a212-8a0f-843f-37f08740c125" name="SpreadSregToAxNoOpIfOPSupportsSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7c8eac5a-0455-e038-0e9a-10e59d459452" name="CtorUriHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="bdba0004-be80-f5c1-1aae-487db09bdf04" name="GetReturnToArgumentDoesNotReturnExtraArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ccfda025-cb1a-a2ff-78bd-5e9af885ae0b" name="ToDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="64142858-d52e-be06-d11f-6be326c6176b" name="RespondTwoValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="03b47440-3d09-ab28-97f1-39809f5703b6" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="34357633-4745-6fba-9316-493d3c6c5b90" name="ParseEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="85a0dec0-983c-8f21-b093-a2179624cc88" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5c66a1b8-5b20-2e3b-8427-d6ff4640ac53" name="BadRequestsGenerateValidErrorResponses" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4ba7ca33-72f1-3fc6-d37c-65134eda904d" name="AddDeclaredValueThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="06b350b0-79d1-9393-7620-cd919061898c" name="ParseEndUserSuppliedUriIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="44afc59c-60fc-3179-b5a6-1e58e7752d54" name="ApplyHeadersToResponseNullHeaders" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="46ec24da-deb7-27c7-6dc6-52090e4fd1fb" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e9a5efc6-fde8-8fa4-0bda-2675a4a7e06b" name="DefaultReferenceTypeDeclaredPropertyHasNoKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b70b4bd5-6dae-b4ad-349c-c3ad70603773" name="ReadFromRequestQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5f02e24c-2972-c598-ca71-ea362b2fe7d8" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="98a2ece8-c9e6-e6f3-c65e-f915b22077fa" name="RequestUsingGet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2cfefc4a-918a-3e16-0670-53eb33634525" name="GeneratesOnlyRequiredElements" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="da8fcfa9-bd2c-eca0-ecbf-90364f84e8e5" name="AddExtraFieldThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fb91e9dd-fc3b-d8a7-a5d7-d215d5ba880f" name="CtorStringHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5211652f-1c25-fd4b-890d-05d2178a60e2" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7650ec62-b144-f36f-8b56-31ad20521d0e" name="DoesNotStripFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e2b1ae2a-8f30-b6b3-bca6-ef28fc5a0175" name="ClaimedIdAndLocalIdSpecifiedIsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="58d69d1e-3bd2-3379-0af1-188f9cff2dd0" name="IsTypeUriPresentEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="495dd486-08dd-d365-7a84-67d96fef8460" name="SendIndirectedUndirectedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="24fb403f-be35-278e-9beb-e11177f2cd1e" name="FormDataSerializeMatchesLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7cdabb8a-aefa-e90e-c32e-047404b64c2d" name="SerializeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f6979feb-7016-4e2b-14e2-e6c2c392419f" name="RemoveByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="58df167c-cf19-351c-cb09-5c52ae9f97be" name="DeserializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a778f331-f14e-9d6e-f942-a023423540f6" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="90f06a50-7b81-05ec-3dc0-7b3e8ade8cfa" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="be00d3ef-f24d-eb8a-d251-4d691736ee6f" name="AddAttributeRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="593e1d86-a6c2-c937-a1b4-6d25a595a1f1" name="EnumerableCacheCurrentThrowsBefore" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="030ac3cf-cfb6-ca47-f822-ec1d2979f0b3" name="Defaults" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3fe0b432-dbb4-b334-e504-a83fe5ffdbaf" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d5d2c553-97db-8d6c-c984-982299d6091d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="628a417f-4ddb-5965-bd4a-86c8de565c8f" name="AssociateDiffieHellmanOverHttp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="10c44d72-2789-2afe-3b27-091dea97546e" name="RequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="724cc3e8-c13c-5cc6-ce14-25c51ad6297d" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="24506e06-a678-66cc-48ee-b7f11f18a6e8" name="StripXriScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c79dd056-8fff-3393-f125-4b83cf02cb3b" name="RequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5a0d31d9-9c70-2a28-3e8c-46e8e047ac2d" name="ReceiveNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4b44b825-36cc-77f8-3a4a-5892c540f577" name="GetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f17128c1-5953-5391-ed75-c33774eacbfc" name="LastLineNotTerminated" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="671ddaf5-238d-a517-b0f3-d79bd591a396" name="EmptyMailAddress" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d8118997-ecf7-7130-f068-5e2bc867786d" name="SerializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e9c2087b-1c52-5bb9-bf4e-9046cf281e36" name="DiscoverRequireSslWithInsecureRedirect" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f69f1c0c-e258-95fb-4fcb-ad14bfc40e3c" name="Discover" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="25e2c593-2e69-6215-90c0-67f269939865" name="CtorEmptyTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e1e9dde8-30e6-6ce0-d5a6-4e22e0347ac4" name="UnifyExtensionsAsSregWithAX" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f50a0bdb-380e-30f6-492a-a6dd9664d0f0" name="ExtensionOnlyChannelLevel" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="64b41c6c-2b67-af35-0c93-df41bd6f2dbb" name="Store" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="55b078e4-3933-d4e0-1151-a0a61321638e" name="ReadFromRequestAuthorization" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2d82ac4b-99b4-a132-eb62-d943e02d1498" name="ApplyHeadersToResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="599add9e-e9eb-5e8a-ce6b-6dc73c2bb408" name="DataContractNamespace" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="643c9887-3f12-300e-fdac-17ae59652712" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ed7efca3-c3c1-bc4a-cef7-eaf984749355" name="ValidMessageReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="44ced969-83dd-201d-a660-e3744ee81cf8" name="ConstructorTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1c5d54e2-d96a-d3a6-aeac-95f137b96421" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cfc4032d-936a-6532-09d5-4a1267e57d63" name="GetPublicFacingUrlSSLForwarder2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e9cceef5-383d-92f0-a8bb-f3e207582836" name="RealmReturnToMismatchV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e6b412e5-3a53-e717-6393-254e1c93e239" name="PassThruDoubleCache" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8375c7bb-b539-3396-885a-a3ca220078ec" name="InsufficientlyProtectedMessageSent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="924b5295-0d39-5c89-8794-22518091e05a" name="CtorNullToString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a63c169c-4e9a-bcba-b7cd-c4c5280cd652" name="PrepareMessageForSendingNonExtendableMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="63944cb8-4c61-c42c-906f-986fa793370b" name="SignatureTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="77934ac4-bd65-7ad8-9c53-9c9447f9e175" name="GetReturnToArgumentAndNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="735b7a56-0f6f-77d8-8968-6708792a7ce8" name="UnifyExtensionsAsSregWithAXSchemaOpenIdNet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4a5b601d-475d-e6cc-1fec-19a2850681ad" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9bcc2d64-870f-7675-a314-fbb975446817" name="IsApprovedDeterminesReturnedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3e676e31-3b6d-9d12-febd-d632ece804ec" name="RPRejectsMismatchingAssociationAndSessionBitLengths" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9026e58c-8582-0852-3c3c-9eadfd544cbc" name="VerifyNonZeroLengthOnEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0f56721c-ef8f-84be-28b7-d909614c2f85" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ae8b7cba-696e-2362-d5e1-79a9c202a994" name="EmptyLineLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="41ff051f-03d5-5f06-c6e4-615360cac08a" name="ReadFromRequestDisallowedHttpMethod" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d647fd93-40b3-24d5-25fc-661c0d58335c" name="SendIndirectMessageFormPostNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="62c6ee5b-ac29-461c-2373-bf620e948825" name="InvalidRealmNoScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="115283b9-d95c-9a92-2197-96685ee8e96a" name="TwoExtensionsSameTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d24a246f-5017-49f0-8030-e44a68ba534d" name="GetPublicFacingUrlSSLForwarder4" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="80719076-10fd-20a7-7ff3-a0aa2bc661cb" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b2b54c72-1d26-8c28-ebf5-7a5a4beeec43" name="VerifyNonZeroLengthOnNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c4001e1c-75ad-236b-284f-318905d2bc3a" name="CreateRequestOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a66588b5-989d-8f8e-4994-4a066220516b" name="FileSerializeMatchesLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c0d7cfcc-4f7e-e7df-3de2-b578c4c3d6ee" name="SpreadSregToAxMultipleSchemas" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="32e95494-d0bb-cfc7-a8d6-652f8816c6b4" name="ReadFromResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f967c0af-c04c-d156-4faf-8978bfcab5d7" name="RequiredNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="248f0afc-979f-a86f-e7de-fdeb4f9dd3ea" name="CtorBadUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="536ecd26-4bda-a35e-5af8-666eb9b44940" name="NullValueEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fa2e5bbd-4c41-f2b1-e875-38c6ef011fa1" name="RandomCharactersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3e2f1dad-3684-587c-9039-8d116582be10" name="GetReturnToArgumentEmptyKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="00ed61cd-46cd-9c0e-f044-38d784c8bcfb" name="DecodeEmptyStringFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="09b892f2-96e9-45b7-d082-b0bb512c1dd4" name="RequiredNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d5912d3e-441c-a20e-20a2-0b9f0220a762" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="439c8c16-2ba5-eb3b-b631-ce50ec48eba0" name="CtorNullMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a6295302-c78f-4122-ce88-94fc30980262" name="CtorStringNoSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f12bf351-584c-bc51-c315-a67f1076927c" name="ReturnToDoesNotMatchRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b4b00582-dcc9-7672-0c02-52432b074a92" name="GetNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f1e1aa37-c712-6096-22fa-394008f0820a" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cb48421f-f4ff-3994-3abc-4be35f8bfd99" name="AssociateQuietlyFailsAfterHttpError" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="660ad25a-b02b-1b17-7d6e-3af3303fa7bc" name="ModeEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="736a09b4-f56e-0176-6c1c-81db0fbe3412" name="CtorUriHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9f880280-aa8f-91bb-4a5f-3fe044b6815a" name="CreateVerificationCode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="10a8b8e5-e147-838c-0708-be98d5e4490e" name="CtorFull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6daa360b-71e4-a972-143f-01b801fada84" name="DeserializeWithExtraFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8bbc6a02-b5a4-ea8e-2a77-8d1b6671ceb5" name="ImplicitConverstionFromUriTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d6088ffe-ccf5-9738-131b-0fc1bc7e3707" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="90d3c411-8895-a07f-7a21-258b9d43c5b2" name="InvalidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="121983e3-1336-70cb-8d2a-498629e92bec" name="GetReturnToArgumentNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5e0c892d-7ad8-6d56-1f1d-2fb6236670d6" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b58e4791-68c0-1bc0-2e48-e1351459ee46" name="UserSetupUrlSetForV1Immediate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c23e762d-4162-cb9e-47b3-455a568b5072" name="SendIndirectMessageFormPostEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f70b368e-da33-bc64-6096-1b467d49a9d4" name="NonIdentityRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="46877579-ba4c-c30c-38c4-9c6ad3922390" name="InsufficientlyProtectedMessageReceived" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b191e585-49d9-df8e-c156-307f798db169" name="AddAttributeRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3772f97f-3fe6-3fc0-350d-4085e7c4329e" name="Test" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="44091d36-98db-2115-8647-7bd7cd308796" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9e59b8d8-2fc4-b425-b5c4-c0a9fde3bf4d" name="SetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="90557d85-db17-e9ab-e17b-32d6cc9fd437" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="534bee09-36e1-c3e0-f6af-bc191b10aa48" name="CtorNullSigner" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9986fea9-8d64-9ada-60cb-ab95adb50fb7" name="ToStringDeferredEmptyMultiLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="de1cdd00-a226-0d43-62b6-0c1ad325be8c" name="RequiredMinAndMaxVersions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cbdfd707-7ba8-4b8f-9d58-17b125aa4cd4" name="SendIndirectMessage301GetNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5aa4dfa9-9691-bfe0-7d81-587cfa519a55" name="DirectResponsesReceivedAsKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8fd673c8-977a-7b66-72cb-38c7054796c7" name="DiscoverRequireSslWithSecureRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cc9200bf-1399-d40a-9754-6415f0b7bcf8" name="CreateRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - </TestLinks> - </TestList> - <TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6"> - <RunConfiguration id="abbd81c0-7d7b-4c98-878c-05dbead62c27" name="Local Test Run" storage="localtestrun.testrunconfig" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, PublicKeyToken=b03f5f7f11d50a3a" /> - </TestList> - <TestList name="Slow" id="c426db8a-259c-4eac-b887-152be3e06ece" parentListId="f0eeb325-0558-48a3-9a99-952133d8148e"> - <Description>All tests</Description> - <TestLinks> - <TestLink id="b350ddb1-f4e5-e79c-af5e-f4195767f294" name="TestPublic" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7c3603c8-3686-807b-7840-1f04f8f307f5" name="AssociateDH" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7fa99410-3aa3-10c3-10a0-27bb9288c900" name="CheckIdSharedHmacSha1Association" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="85469029-ffe0-f6f7-b56a-9ffd48fa137b" name="AssociateClearText" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e6dadcc3-60ff-f60c-0c9a-2ebd5cf91df0" name="CheckIdSharedHmacSha256Association" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - </TestLinks> - </TestList> - <TestList name="Unit tests" id="f0eeb325-0558-48a3-9a99-952133d8148e" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6" /> -</TestLists>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs b/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs index 37f9c78..980d90f 100644 --- a/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs +++ b/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs @@ -144,6 +144,7 @@ using System.Reflection; /// The conversion cannot be performed. /// </exception> public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + Contract.Assume(destinationType != null, "Missing contract."); if (destinationType.IsInstanceOfType(value)) { return value; } diff --git a/src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs b/src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs index 6ba9c4b..61c0fd8 100644 --- a/src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs +++ b/src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.ComponentModel { return null; } - MemberInfo identifierParse = typeof(Identifier).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public); + MemberInfo identifierParse = typeof(Identifier).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null); return CreateInstanceDescriptor(identifierParse, new object[] { value.ToString() }); } diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd index b639a29..3164ec5 100644 --- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd +++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd @@ -319,6 +319,32 @@ </xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute name="allowDualPurposeIdentifiers" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Controls whether identifiers that are both OP Identifiers and Claimed Identifiers + should ever be recognized as claimed identifiers. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="allowApproximateIdentifierDiscovery" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Controls whether certain Claimed Identifiers that exploit + features that .NET does not have the ability to send exact HTTP requests for will + still be allowed by using an approximate HTTP request. + Only impacts hosts running under partial trust. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="protectDownlevelReplayAttacks" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Controls whether the relying party should take special care + to protect users against replay attacks when interoperating with OpenID 1.1 Providers. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="behaviors"> @@ -361,6 +387,27 @@ </xs:choice> </xs:complexType> </xs:element> + <xs:element name="discoveryServices"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="add"> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="remove"> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="clear"> + <xs:complexType> + <!--tag is empty--> + </xs:complexType> + </xs:element> + </xs:choice> + </xs:complexType> + </xs:element> <xs:element name="store"> <xs:annotation> <xs:documentation> diff --git a/src/DotNetOpenAuth/Configuration/MessagingElement.cs b/src/DotNetOpenAuth/Configuration/MessagingElement.cs index 9e957fe..f130dbc 100644 --- a/src/DotNetOpenAuth/Configuration/MessagingElement.cs +++ b/src/DotNetOpenAuth/Configuration/MessagingElement.cs @@ -32,6 +32,11 @@ namespace DotNetOpenAuth.Configuration { private const string MaximumClockSkewConfigName = "clockSkew"; /// <summary> + /// The name of the attribute that controls whether messaging rules are strictly followed. + /// </summary> + private const string StrictConfigName = "strict"; + + /// <summary> /// Gets the actual maximum message lifetime that a program should allow. /// </summary> /// <value>The sum of the <see cref="MaximumMessageLifetime"/> and @@ -83,6 +88,24 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> + /// Gets or sets a value indicating whether messaging rules are strictly + /// adhered to. + /// </summary> + /// <value><c>true</c> by default.</value> + /// <remarks> + /// Strict will require that remote parties adhere strictly to the specifications, + /// even when a loose interpretation would not compromise security. + /// <c>true</c> is a good default because it shakes out interoperability bugs in remote services + /// so they can be identified and corrected. But some web sites want things to Just Work + /// more than they want to file bugs against others, so <c>false</c> is the setting for them. + /// </remarks> + [ConfigurationProperty(StrictConfigName, DefaultValue = true)] + internal bool Strict { + get { return (bool)this[StrictConfigName]; } + set { this[StrictConfigName] = value; } + } + + /// <summary> /// Gets or sets the configuration for the <see cref="UntrustedWebRequestHandler"/> class. /// </summary> /// <value>The untrusted web request.</value> diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs index cdf4fd3..2ee2e91 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs @@ -5,8 +5,10 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Configuration { + using System; using System.Configuration; using System.Diagnostics.Contracts; + using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.RelyingParty; /// <summary> @@ -25,11 +27,21 @@ namespace DotNetOpenAuth.Configuration { private const string SecuritySettingsConfigName = "security"; /// <summary> - /// Gets the name of the <behaviors> sub-element. + /// The name of the <behaviors> sub-element. /// </summary> private const string BehaviorsElementName = "behaviors"; /// <summary> + /// The name of the <discoveryServices> sub-element. + /// </summary> + private const string DiscoveryServicesElementName = "discoveryServices"; + + /// <summary> + /// The built-in set of identifier discovery services. + /// </summary> + private static readonly TypeConfigurationCollection<IIdentifierDiscoveryService> defaultDiscoveryServices = new TypeConfigurationCollection<IIdentifierDiscoveryService>(new Type[] { typeof(UriDiscoveryService), typeof(XriDiscoveryProxyService) }); + + /// <summary> /// Initializes a new instance of the <see cref="OpenIdRelyingPartyElement"/> class. /// </summary> public OpenIdRelyingPartyElement() { @@ -62,5 +74,25 @@ namespace DotNetOpenAuth.Configuration { get { return (TypeConfigurationElement<IRelyingPartyApplicationStore>)this[StoreConfigName] ?? new TypeConfigurationElement<IRelyingPartyApplicationStore>(); } set { this[StoreConfigName] = value; } } + + /// <summary> + /// Gets or sets the services to use for discovering service endpoints for identifiers. + /// </summary> + /// <remarks> + /// If no discovery services are defined in the (web) application's .config file, + /// the default set of discovery services built into the library are used. + /// </remarks> + [ConfigurationProperty(DiscoveryServicesElementName, IsDefaultCollection = false)] + [ConfigurationCollection(typeof(TypeConfigurationCollection<IIdentifierDiscoveryService>))] + internal TypeConfigurationCollection<IIdentifierDiscoveryService> DiscoveryServices { + get { + var configResult = (TypeConfigurationCollection<IIdentifierDiscoveryService>)this[DiscoveryServicesElementName]; + return configResult != null && configResult.Count > 0 ? configResult : defaultDiscoveryServices; + } + + set { + this[DiscoveryServicesElementName] = value; + } + } } } diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs index d10d9bd..1bf2ebc 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs @@ -66,6 +66,21 @@ namespace DotNetOpenAuth.Configuration { private const string PrivateSecretMaximumAgeConfigName = "privateSecretMaximumAge"; /// <summary> + /// Gets the name of the @allowDualPurposeIdentifiers attribute. + /// </summary> + private const string AllowDualPurposeIdentifiersConfigName = "allowDualPurposeIdentifiers"; + + /// <summary> + /// Gets the name of the @allowApproximateIdentifierDiscovery attribute. + /// </summary> + private const string AllowApproximateIdentifierDiscoveryConfigName = "allowApproximateIdentifierDiscovery"; + + /// <summary> + /// Gets the name of the @protectDownlevelReplayAttacks attribute. + /// </summary> + private const string ProtectDownlevelReplayAttacksConfigName = "protectDownlevelReplayAttacks"; + + /// <summary> /// Initializes a new instance of the <see cref="OpenIdRelyingPartySecuritySettingsElement"/> class. /// </summary> public OpenIdRelyingPartySecuritySettingsElement() { @@ -183,6 +198,43 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> + /// Gets or sets a value indicating whether identifiers that are both OP Identifiers and Claimed Identifiers + /// should ever be recognized as claimed identifiers. + /// </summary> + /// <value> + /// The default value is <c>false</c>, per the OpenID 2.0 spec. + /// </value> + [ConfigurationProperty(AllowDualPurposeIdentifiersConfigName, DefaultValue = false)] + public bool AllowDualPurposeIdentifiers { + get { return (bool)this[AllowDualPurposeIdentifiersConfigName]; } + set { this[AllowDualPurposeIdentifiersConfigName] = value; } + } + + /// <summary> + /// Gets or sets a value indicating whether certain Claimed Identifiers that exploit + /// features that .NET does not have the ability to send exact HTTP requests for will + /// still be allowed by using an approximate HTTP request. + /// </summary> + /// <value> + /// The default value is <c>true</c>. + /// </value> + [ConfigurationProperty(AllowApproximateIdentifierDiscoveryConfigName, DefaultValue = true)] + public bool AllowApproximateIdentifierDiscovery { + get { return (bool)this[AllowApproximateIdentifierDiscoveryConfigName]; } + set { this[AllowApproximateIdentifierDiscoveryConfigName] = value; } + } + + /// <summary> + /// Gets or sets a value indicating whether the Relying Party should take special care + /// to protect users against replay attacks when interoperating with OpenID 1.1 Providers. + /// </summary> + [ConfigurationProperty(ProtectDownlevelReplayAttacksConfigName, DefaultValue = RelyingPartySecuritySettings.ProtectDownlevelReplayAttacksDefault)] + public bool ProtectDownlevelReplayAttacks { + get { return (bool)this[ProtectDownlevelReplayAttacksConfigName]; } + set { this[ProtectDownlevelReplayAttacksConfigName] = value; } + } + + /// <summary> /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. /// </summary> /// <returns>The newly created security settings object.</returns> @@ -200,6 +252,9 @@ namespace DotNetOpenAuth.Configuration { settings.RejectUnsolicitedAssertions = this.RejectUnsolicitedAssertions; settings.RejectDelegatingIdentifiers = this.RejectDelegatingIdentifiers; settings.IgnoreUnsignedExtensions = this.IgnoreUnsignedExtensions; + settings.AllowDualPurposeIdentifiers = this.AllowDualPurposeIdentifiers; + settings.AllowApproximateIdentifierDiscovery = this.AllowApproximateIdentifierDiscovery; + settings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks; return settings; } diff --git a/src/DotNetOpenAuth/Configuration/ReportingElement.cs b/src/DotNetOpenAuth/Configuration/ReportingElement.cs index ab1437f..2374448 100644 --- a/src/DotNetOpenAuth/Configuration/ReportingElement.cs +++ b/src/DotNetOpenAuth/Configuration/ReportingElement.cs @@ -69,7 +69,7 @@ namespace DotNetOpenAuth.Configuration { /// Gets or sets a value indicating whether this reporting is enabled. /// </summary> /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value> - [ConfigurationProperty(EnabledAttributeName, DefaultValue = false)] + [ConfigurationProperty(EnabledAttributeName, DefaultValue = true)] internal bool Enabled { get { return (bool)this[EnabledAttributeName]; } set { this[EnabledAttributeName] = value; } diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index f904c45..919d1f2 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -1,8 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> + <ProjectRoot Condition="'$(ProjectRoot)' == ''">$(MSBuildProjectDirectory)\..\..\</ProjectRoot> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + </PropertyGroup> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" /> + <PropertyGroup> <ProductVersion>9.0.30729</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</ProjectGuid> @@ -10,26 +14,46 @@ <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>DotNetOpenAuth</RootNamespace> <AssemblyName>DotNetOpenAuth</AssemblyName> - <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <StandardCopyright> Copyright (c) 2009, Andrew Arnott. All rights reserved. Code licensed under the Ms-PL License: http://opensource.org/licenses/ms-pl.html </StandardCopyright> + <FileUpgradeFlags> + </FileUpgradeFlags> + <OldToolsVersion>3.5</OldToolsVersion> + <UpgradeBackupLocation /> + <IsWebBootstrapper>false</IsWebBootstrapper> + <TargetFrameworkProfile /> + <PublishUrl>publish\</PublishUrl> + <Install>true</Install> + <InstallFrom>Disk</InstallFrom> + <UpdateEnabled>false</UpdateEnabled> + <UpdateMode>Foreground</UpdateMode> + <UpdateInterval>7</UpdateInterval> + <UpdateIntervalUnits>Days</UpdateIntervalUnits> + <UpdatePeriodically>false</UpdatePeriodically> + <UpdateRequired>false</UpdateRequired> + <MapFileExtensions>true</MapFileExtensions> + <ApplicationRevision>0</ApplicationRevision> + <ApplicationVersion>1.0.0.%2a</ApplicationVersion> + <UseApplicationTrust>false</UseApplicationTrust> + <BootstrapperEnabled>true</BootstrapperEnabled> + <ApplicationIcon>DotNetOpenAuth.ico</ApplicationIcon> + <DocumentationFile>$(OutputPath)DotNetOpenAuth.xml</DocumentationFile> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> - <OutputPath>..\..\bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <AllowUnsafeBlocks>false</AllowUnsafeBlocks> - <DocumentationFile>..\..\bin\Debug\DotNetOpenAuth.xml</DocumentationFile> <RunCodeAnalysis>false</RunCodeAnalysis> - <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules> + <CodeAnalysisRules> + </CodeAnalysisRules> <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking> <CodeContractsCustomRewriterAssembly> </CodeContractsCustomRewriterAssembly> @@ -58,18 +82,18 @@ http://opensource.org/licenses/ms-pl.html <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs> <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> - <OutputPath>..\..\bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <AllowUnsafeBlocks>false</AllowUnsafeBlocks> - <DocumentationFile>..\..\bin\Release\DotNetOpenAuth.xml</DocumentationFile> <RunCodeAnalysis>true</RunCodeAnalysis> - <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules> + <CodeAnalysisRules> + </CodeAnalysisRules> <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking> <CodeContractsCustomRewriterAssembly> </CodeContractsCustomRewriterAssembly> @@ -98,18 +122,15 @@ http://opensource.org/licenses/ms-pl.html <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs> <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly> - </PropertyGroup> - <PropertyGroup> - <SignAssembly>true</SignAssembly> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' "> <DebugSymbols>true</DebugSymbols> - <OutputPath>..\..\bin\CodeAnalysis\</OutputPath> <DefineConstants>$(DefineConstants);CONTRACTS_FULL;DEBUG;TRACE</DefineConstants> - <DocumentationFile>..\..\bin\CodeAnalysis\DotNetOpenAuth.xml</DocumentationFile> <DebugType>full</DebugType> <PlatformTarget>AnyCPU</PlatformTarget> - <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules> + <CodeAnalysisRules> + </CodeAnalysisRules> <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression> <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile> <ErrorReport>prompt</ErrorReport> @@ -142,15 +163,11 @@ http://opensource.org/licenses/ms-pl.html <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs> <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly> + <CodeAnalysisRuleSet>Migrated rules for DotNetOpenAuth.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\log4net.dll</HintPath> - </Reference> - <Reference Include="Microsoft.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\Microsoft.Contracts.dll</HintPath> </Reference> <Reference Include="PresentationFramework"> <RequiredTargetFramework>3.0</RequiredTargetFramework> @@ -178,9 +195,7 @@ http://opensource.org/licenses/ms-pl.html <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> <Reference Include="System.Web" /> - <Reference Include="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\System.Web.Abstractions.dll</HintPath> + <Reference Include="System.Web.Abstractions"> <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> <Reference Include="System.Web.Extensions"> @@ -189,16 +204,11 @@ http://opensource.org/licenses/ms-pl.html <Reference Include="System.Web.Extensions.Design"> <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> - <Reference Include="System.Web.Mobile" /> - <Reference Include="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\System.Web.Mvc.dll</HintPath> - </Reference> - <Reference Include="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\..\lib\System.Web.Routing.dll</HintPath> + <Reference Include="System.Web.Mobile" Condition=" '$(ClrVersion)' != '4' " /> + <Reference Include="System.Web.Routing"> <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> + <Reference Include="System.Xaml" Condition=" '$(ClrVersion)' == '4' " /> <Reference Include="System.XML" /> <Reference Include="System.Xml.Linq"> <RequiredTargetFramework>3.5</RequiredTargetFramework> @@ -206,6 +216,18 @@ http://opensource.org/licenses/ms-pl.html <Reference Include="WindowsBase"> <RequiredTargetFramework>3.0</RequiredTargetFramework> </Reference> + <Reference Include="System.ComponentModel.DataAnnotations"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + </ItemGroup> + <ItemGroup Condition=" '$(ClrVersion)' == '4' "> + <Reference Include="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/> + </ItemGroup> + <ItemGroup Condition=" '$(ClrVersion)' != '4' "> + <!-- MVC 2 can run on CLR 2 (it doesn't require CLR 4) but since MVC 2 apps tend to use type forwarding, + it's a more broadly consumable idea to bind against MVC 1 for the library unless we're building on CLR 4, + which will definitely have MVC 2 available. --> + <Reference Include="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/> </ItemGroup> <ItemGroup> <Compile Include="ComponentModel\ClaimTypeSuggestions.cs" /> @@ -276,7 +298,10 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Messaging\OutgoingWebResponseActionResult.cs" /> <Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" /> <Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" /> + <Compile Include="Messaging\Reflection\IMessagePartOriginalEncoder.cs" /> <Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" /> + <Compile Include="Mvc\OpenIdHelper.cs" /> + <Compile Include="Mvc\OpenIdAjaxOptions.cs" /> <Compile Include="OAuth\ChannelElements\ICombinedOpenIdProviderTokenManager.cs" /> <Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" /> <Compile Include="OAuth\ChannelElements\IConsumerTokenManager.cs" /> @@ -302,6 +327,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OAuth\ConsumerSecuritySettings.cs" /> <Compile Include="OAuth\DesktopConsumer.cs" /> <Compile Include="GlobalSuppressions.cs" /> + <Compile Include="Messaging\IMessageWithBinaryData.cs" /> <Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" /> <Compile Include="Messaging\ChannelEventArgs.cs" /> <Compile Include="Messaging\ITamperProtectionChannelBindingElement.cs" /> @@ -429,12 +455,14 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OpenId\Extensions\UI\UIUtilities.cs" /> <Compile Include="OpenId\Extensions\UI\UIModes.cs" /> <Compile Include="OpenId\Extensions\UI\UIRequest.cs" /> + <Compile Include="OpenId\HostMetaDiscoveryService.cs" /> <Compile Include="OpenId\Identifier.cs" /> <Compile Include="OpenId\IdentifierContract.cs" /> <Compile Include="OpenId\Extensions\ExtensionsInteropHelper.cs" /> <Compile Include="OpenId\Interop\AuthenticationResponseShim.cs" /> <Compile Include="OpenId\Interop\ClaimsResponseShim.cs" /> <Compile Include="OpenId\Interop\OpenIdRelyingPartyShim.cs" /> + <Compile Include="OpenId\IIdentifierDiscoveryService.cs" /> <Compile Include="OpenId\Messages\CheckAuthenticationRequest.cs" /> <Compile Include="OpenId\Messages\CheckAuthenticationResponse.cs" /> <Compile Include="OpenId\Messages\CheckIdRequest.cs" /> @@ -501,6 +529,9 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OpenId\RelyingParty\AssociationPreference.cs" /> <Compile Include="OpenId\RelyingParty\AuthenticationRequest.cs" /> <Compile Include="OpenId\RelyingParty\AuthenticationRequestMode.cs" /> + <Compile Include="OpenId\RelyingParty\DuplicateRequestedHostsComparer.cs" /> + <Compile Include="OpenId\RelyingParty\IProviderEndpoint.cs" /> + <Compile Include="OpenId\RelyingParty\OpenIdAjaxRelyingParty.cs" /> <Compile Include="OpenId\RelyingParty\SelectorButtonContract.cs" /> <Compile Include="OpenId\RelyingParty\SelectorProviderButton.cs" /> <Compile Include="OpenId\RelyingParty\SelectorOpenIdButton.cs" /> @@ -508,14 +539,13 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OpenId\RelyingParty\IRelyingPartyBehavior.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdSelector.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.cs" /> - <Compile Include="OpenId\RelyingParty\IXrdsProviderEndpointContract.cs" /> <Compile Include="OpenId\RelyingParty\IAuthenticationRequestContract.cs" /> <Compile Include="OpenId\RelyingParty\NegativeAuthenticationResponse.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdAjaxTextBox.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdButton.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdEventArgs.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdLogin.cs" /> - <Compile Include="OpenId\RelyingParty\OpenIdMobileTextBox.cs" /> + <Compile Include="OpenId\RelyingParty\OpenIdMobileTextBox.cs" Condition=" '$(ClrVersion)' != '4' " /> <Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyControlBase.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdTextBox.cs" /> <Compile Include="OpenId\RelyingParty\PopupBehavior.cs" /> @@ -525,9 +555,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OpenId\RelyingParty\FailedAuthenticationResponse.cs" /> <Compile Include="OpenId\RelyingParty\IAuthenticationRequest.cs" /> <Compile Include="OpenId\RelyingParty\IAuthenticationResponse.cs" /> - <Compile Include="OpenId\RelyingParty\IProviderEndpoint.cs" /> <Compile Include="OpenId\RelyingParty\ISetupRequiredAuthenticationResponse.cs" /> - <Compile Include="OpenId\RelyingParty\IXrdsProviderEndpoint.cs" /> <Compile Include="OpenId\RelyingParty\OpenIdRelyingParty.cs" /> <Compile Include="OpenId\OpenIdStrings.Designer.cs"> <DependentUpon>OpenIdStrings.resx</DependentUpon> @@ -542,7 +570,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OpenId\RelyingParty\PrivateSecretManager.cs" /> <Compile Include="OpenId\RelyingParty\RelyingPartySecuritySettings.cs" /> <Compile Include="OpenId\RelyingParty\SelectorButton.cs" /> - <Compile Include="OpenId\RelyingParty\ServiceEndpoint.cs" /> + <Compile Include="OpenId\IdentifierDiscoveryResult.cs" /> <Compile Include="OpenId\OpenIdXrdsHelper.cs" /> <Compile Include="OpenId\RelyingParty\SimpleXrdsProviderEndpoint.cs" /> <Compile Include="OpenId\RelyingParty\StandardRelyingPartyApplicationStore.cs" /> @@ -550,7 +578,9 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OpenId\RelyingParty\WellKnownProviders.cs" /> <Compile Include="OpenId\SecuritySettings.cs" /> <Compile Include="Messaging\UntrustedWebRequestHandler.cs" /> + <Compile Include="OpenId\UriDiscoveryService.cs" /> <Compile Include="OpenId\UriIdentifier.cs" /> + <Compile Include="OpenId\XriDiscoveryProxyService.cs" /> <Compile Include="OpenId\XriIdentifier.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="OAuth\Messages\UnauthorizedTokenRequest.cs" /> @@ -586,6 +616,7 @@ http://opensource.org/licenses/ms-pl.html </ItemGroup> <ItemGroup> <None Include="Configuration\DotNetOpenAuth.xsd" /> + <None Include="Migrated rules for DotNetOpenAuth.ruleset" /> <None Include="OAuth\ClassDiagram.cd" /> <None Include="OAuth\Messages\OAuth Messages.cd" /> <None Include="Messaging\Bindings\Bindings.cd" /> @@ -618,7 +649,7 @@ http://opensource.org/licenses/ms-pl.html </EmbeddedResource> </ItemGroup> <ItemGroup> - <EmbeddedResource Include="OpenId\RelyingParty\openid_login.gif" /> + <EmbeddedResource Include="OpenId\RelyingParty\openid_login.png" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="OpenId\RelyingParty\login_failure.png" /> @@ -681,14 +712,49 @@ http://opensource.org/licenses/ms-pl.html <ItemGroup> <EmbeddedResource Include="OpenId\RelyingParty\OpenIdSelector.css" /> </ItemGroup> - <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> - <Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" /> - <Import Project="..\..\tools\JavascriptPacker.targets" /> - <PropertyGroup> - <CompileDependsOn>$(CompileDependsOn);CheckForCodeContracts</CompileDependsOn> - </PropertyGroup> - <Target Name="CheckForCodeContracts"> - <Error Condition=" '$(CodeContractsImported)' != 'true' " - Text="This project requires Code Contracts. Please install from: http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx"/> + <ItemGroup> + <BootstrapperPackage Include="Microsoft.Net.Client.3.5"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName> + <Install>false</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1</ProductName> + <Install>true</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1"> + <Visible>False</Visible> + <ProductName>Windows Installer 3.1</ProductName> + <Install>true</Install> + </BootstrapperPackage> + <Content Include="DotNetOpenAuth.ico" /> + </ItemGroup> + + <ItemGroup> + <SignDependsOn Include="BuildUnifiedProduct" /> + <DelaySignedAssemblies Include="$(ILMergeOutputAssembly); + $(OutputPath)CodeContracts\$(ProductName).Contracts.dll; + " /> + </ItemGroup> + <PropertyGroup> + <!-- Don't sign the non-unified version of the assembly. --> + <SuppressTargetPathDelaySignedAssembly>true</SuppressTargetPathDelaySignedAssembly> + </PropertyGroup> + + <Target Name="BuildUnifiedProduct" + DependsOnTargets="Build" + Inputs="@(ILMergeInputAssemblies)" + Outputs="$(ILMergeOutputAssembly)"> + <MakeDir Directories="$(ILMergeOutputAssemblyDirectory)" /> + <ILMerge ExcludeFile="$(ProjectRoot)ILMergeInternalizeExceptions.txt" + InputAssemblies="@(ILMergeInputAssemblies)" + OutputFile="$(ILMergeOutputAssembly)" + KeyFile="$(PublicKeyFile)" + DelaySign="true" + /> </Target> + + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> </Project> diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.ico b/src/DotNetOpenAuth/DotNetOpenAuth.ico Binary files differnew file mode 100644 index 0000000..e227dbe --- /dev/null +++ b/src/DotNetOpenAuth/DotNetOpenAuth.ico diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs index e436846..9b1bcfa 100644 --- a/src/DotNetOpenAuth/GlobalSuppressions.cs +++ b/src/DotNetOpenAuth/GlobalSuppressions.cs @@ -57,3 +57,6 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnIncomingRequest(DotNetOpenAuth.OpenId.Provider.IRequest)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.Provider.ProviderSecuritySettings)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Mvc")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Mvc", Scope = "namespace", Target = "DotNetOpenAuth.Mvc")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")] diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs index 86c1118..ae45229 100644 --- a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs +++ b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs @@ -268,6 +268,7 @@ namespace DotNetOpenAuth.InfoCard { [Category(InfoCardCategory), DefaultValue(PrivacyUrlDefault)] [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "We construct a Uri to validate the format of the string.")] [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "That overload is NOT the same.")] + [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "This can take ~/ paths.")] public string PrivacyUrl { get { return (string)this.ViewState[PrivacyUrlViewStateKey] ?? PrivacyUrlDefault; @@ -570,24 +571,28 @@ namespace DotNetOpenAuth.InfoCard { Panel supportedPanel = new Panel(); - if (!this.DesignMode) { - // At the user agent, assume InfoCard is not supported until - // the JavaScript discovers otherwise and reveals this panel. - supportedPanel.Style[HtmlTextWriterStyle.Display] = "none"; - } + try { + if (!this.DesignMode) { + // At the user agent, assume InfoCard is not supported until + // the JavaScript discovers otherwise and reveals this panel. + supportedPanel.Style[HtmlTextWriterStyle.Display] = "none"; + } - supportedPanel.Controls.Add(this.CreateInfoCardImage()); + supportedPanel.Controls.Add(this.CreateInfoCardImage()); - // trigger the selector at page load? - if (this.AutoPopup && !this.Page.IsPostBack) { - this.Page.ClientScript.RegisterStartupScript( - typeof(InfoCardSelector), - "selector_load_trigger", - this.GetInfoCardSelectorActivationScript(true), - true); + // trigger the selector at page load? + if (this.AutoPopup && !this.Page.IsPostBack) { + this.Page.ClientScript.RegisterStartupScript( + typeof(InfoCardSelector), + "selector_load_trigger", + this.GetInfoCardSelectorActivationScript(true), + true); + } + return supportedPanel; + } catch { + supportedPanel.Dispose(); + throw; } - - return supportedPanel; } /// <summary> @@ -624,10 +629,15 @@ namespace DotNetOpenAuth.InfoCard { Contract.Ensures(Contract.Result<Panel>() != null); Panel unsupportedPanel = new Panel(); - if (this.UnsupportedTemplate != null) { - this.UnsupportedTemplate.InstantiateIn(unsupportedPanel); + try { + if (this.UnsupportedTemplate != null) { + this.UnsupportedTemplate.InstantiateIn(unsupportedPanel); + } + return unsupportedPanel; + } catch { + unsupportedPanel.Dispose(); + throw; } - return unsupportedPanel; } /// <summary> @@ -692,13 +702,18 @@ namespace DotNetOpenAuth.InfoCard { private Image CreateInfoCardImage() { // add clickable image Image image = new Image(); - image.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(typeof(InfoCardSelector), InfoCardImage.GetImageManifestResourceStreamName(this.ImageSize)); - image.AlternateText = InfoCardStrings.SelectorClickPrompt; - image.ToolTip = this.ToolTip; - image.Style[HtmlTextWriterStyle.Cursor] = "hand"; - - image.Attributes["onclick"] = this.GetInfoCardSelectorActivationScript(false); - return image; + try { + image.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(typeof(InfoCardSelector), InfoCardImage.GetImageManifestResourceStreamName(this.ImageSize)); + image.AlternateText = InfoCardStrings.SelectorClickPrompt; + image.ToolTip = this.ToolTip; + image.Style[HtmlTextWriterStyle.Cursor] = "hand"; + + image.Attributes["onclick"] = this.GetInfoCardSelectorActivationScript(false); + return image; + } catch { + image.Dispose(); + throw; + } } /// <summary> diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs index 00eb1af..a6d3dcf 100644 --- a/src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs +++ b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4918 +// Runtime Version:4.0.30104.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.InfoCard { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class InfoCardStrings { diff --git a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs index 124f9f8..2ac2b7e 100644 --- a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs +++ b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs @@ -74,7 +74,13 @@ namespace DotNetOpenAuth.InfoCard { public void AddDecryptingToken(X509Certificate2 certificate) { Contract.Requires<ArgumentNullException>(certificate != null); Contract.Requires<ArgumentException>(certificate.HasPrivateKey); - this.AddDecryptingToken(new X509SecurityToken(certificate)); + var cert = new X509SecurityToken(certificate); + try { + this.AddDecryptingToken(cert); + } catch { + cert.Dispose(); + throw; + } } #if CONTRACTS_FULL diff --git a/src/DotNetOpenAuth/InfoCard/Token/Token.cs b/src/DotNetOpenAuth/InfoCard/Token/Token.cs index 7fa9a95..89fa3a3 100644 --- a/src/DotNetOpenAuth/InfoCard/Token/Token.cs +++ b/src/DotNetOpenAuth/InfoCard/Token/Token.cs @@ -49,16 +49,18 @@ namespace DotNetOpenAuth.InfoCard { byte[] decryptedBytes; string decryptedString; - using (XmlReader tokenReader = XmlReader.Create(new StringReader(tokenXml))) { - Contract.Assume(tokenReader != null); // BCL contract should say XmlReader.Create result != null - if (IsEncrypted(tokenReader)) { - Logger.InfoCard.DebugFormat("Incoming SAML token, before decryption: {0}", tokenXml); - decryptedBytes = decryptor.DecryptToken(tokenReader); - decryptedString = Encoding.UTF8.GetString(decryptedBytes); - Contract.Assume(decryptedString != null); // BCL contracts should be enhanced here - } else { - decryptedBytes = Encoding.UTF8.GetBytes(tokenXml); - decryptedString = tokenXml; + using (StringReader xmlReader = new StringReader(tokenXml)) { + using (XmlReader tokenReader = XmlReader.Create(xmlReader)) { + Contract.Assume(tokenReader != null); // BCL contract should say XmlReader.Create result != null + if (IsEncrypted(tokenReader)) { + Logger.InfoCard.DebugFormat("Incoming SAML token, before decryption: {0}", tokenXml); + decryptedBytes = decryptor.DecryptToken(tokenReader); + decryptedString = Encoding.UTF8.GetString(decryptedBytes); + Contract.Assume(decryptedString != null); // BCL contracts should be enhanced here + } else { + decryptedBytes = Encoding.UTF8.GetBytes(tokenXml); + decryptedString = tokenXml; + } } } diff --git a/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs b/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs index 48b7794..4ac871a 100644 --- a/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs +++ b/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs @@ -226,7 +226,9 @@ namespace DotNetOpenAuth.InfoCard { int charMapLength = charMap.Length; byte[] raw = Convert.FromBase64String(ppid); - raw = SHA1.Create().ComputeHash(raw); + using (HashAlgorithm hasher = SHA1.Create()) { + raw = hasher.ComputeHash(raw); + } StringBuilder callSign = new StringBuilder(); diff --git a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs index dd34d90..c9bc1d3 100644 --- a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs +++ b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs @@ -90,11 +90,16 @@ namespace DotNetOpenAuth.Messaging { public override StreamReader GetResponseReader() { this.ResponseStream.Seek(0, SeekOrigin.Begin); string contentEncoding = this.Headers[HttpResponseHeader.ContentEncoding]; - if (string.IsNullOrEmpty(contentEncoding)) { - return new StreamReader(this.ResponseStream); - } else { - return new StreamReader(this.ResponseStream, Encoding.GetEncoding(contentEncoding)); + Encoding encoding = null; + if (!string.IsNullOrEmpty(contentEncoding)) { + try { + encoding = Encoding.GetEncoding(contentEncoding); + } catch (ArgumentException ex) { + Logger.Messaging.ErrorFormat("Encoding.GetEncoding(\"{0}\") threw ArgumentException: {1}", contentEncoding, ex); + } } + + return encoding != null ? new StreamReader(this.ResponseStream, encoding) : new StreamReader(this.ResponseStream); } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index 831283a..cc411c8 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -695,27 +695,28 @@ namespace DotNetOpenAuth.Messaging { WebHeaderCollection headers = new WebHeaderCollection(); headers.Add(HttpResponseHeader.ContentType, "text/html"); - 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)); + using (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(); + OutgoingWebResponse response = new OutgoingWebResponse { + Status = HttpStatusCode.OK, + Headers = headers, + Body = bodyWriter.ToString(), + OriginalMessage = message + }; + + return response; } - bodyWriter.WriteLine( - IndirectMessageFormPostFormat, - HttpUtility.HtmlEncode(message.Recipient.AbsoluteUri), - hiddenFields); - bodyWriter.Flush(); - OutgoingWebResponse response = new OutgoingWebResponse { - Status = HttpStatusCode.OK, - Headers = headers, - Body = bodyWriter.ToString(), - OriginalMessage = message - }; - - return response; } /// <summary> @@ -872,7 +873,18 @@ namespace DotNetOpenAuth.Messaging { HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient); httpRequest.CachePolicy = this.CachePolicy; httpRequest.Method = "POST"; - this.SendParametersInEntity(httpRequest, fields); + + var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData; + if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { + var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData); + + // When sending multi-part, all data gets send as multi-part -- even the non-binary data. + multiPartFields.AddRange(fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); + this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields); + } else { + ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart); + this.SendParametersInEntity(httpRequest, fields); + } return httpRequest; } @@ -942,6 +954,19 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Sends the given parameters in the entity stream of an HTTP request in multi-part format. + /// </summary> + /// <param name="httpRequest">The HTTP request.</param> + /// <param name="fields">The parameters to send.</param> + /// <remarks> + /// This method calls <see cref="HttpWebRequest.GetRequestStream()"/> and closes + /// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>. + /// </remarks> + protected void SendParametersInEntityAsMultipart(HttpWebRequest httpRequest, IEnumerable<MultipartPostPart> fields) { + httpRequest.PostMultipartNoGetResponse(this.WebRequestHandler, fields); + } + + /// <summary> /// Verifies the integrity and applicability of an incoming message. /// </summary> /// <param name="message">The message just received.</param> @@ -953,7 +978,7 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentNullException>(message != null); if (Logger.Channel.IsInfoEnabled) { - var messageAccessor = this.MessageDescriptions.GetAccessor(message); + var messageAccessor = this.MessageDescriptions.GetAccessor(message, true); Logger.Channel.InfoFormat( "Processing incoming {0} ({1}) message:{2}{3}", message.GetType().Name, diff --git a/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs new file mode 100644 index 0000000..f411cf5 --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------- +// <copyright file="IMessageWithBinaryData.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + + /// <summary> + /// The interface that classes must implement to be serialized/deserialized + /// as protocol or extension messages that uses POST multi-part data for binary content. + /// </summary> + [ContractClass(typeof(IMessageWithBinaryDataContract))] + public interface IMessageWithBinaryData : IDirectedProtocolMessage { + /// <summary> + /// Gets the parts of the message that carry binary data. + /// </summary> + /// <value>A list of parts. Never null.</value> + IList<MultipartPostPart> BinaryData { get; } + + /// <summary> + /// Gets a value indicating whether this message should be sent as multi-part POST. + /// </summary> + bool SendAsMultipart { get; } + } + + /// <summary> + /// The contract class for the <see cref="IMessageWithBinaryData"/> interface. + /// </summary> + [ContractClassFor(typeof(IMessageWithBinaryData))] + internal sealed class IMessageWithBinaryDataContract : IMessageWithBinaryData { + #region IMessageWithBinaryData Members + + /// <summary> + /// Gets the parts of the message that carry binary data. + /// </summary> + /// <value>A list of parts. Never null.</value> + IList<MultipartPostPart> IMessageWithBinaryData.BinaryData { + get { + Contract.Ensures(Contract.Result<IList<MultipartPostPart>>() != null); + throw new NotImplementedException(); + } + } + + /// <summary> + /// Gets a value indicating whether this message should be sent as multi-part POST. + /// </summary> + bool IMessageWithBinaryData.SendAsMultipart { + get { throw new NotImplementedException(); } + } + + #endregion + + #region IMessage Properties + + /// <summary> + /// Gets the version of the protocol or extension this message is prepared to implement. + /// </summary> + /// <value></value> + /// <remarks> + /// Implementations of this interface should ensure that this property never returns null. + /// </remarks> + Version IMessage.Version { + get { + Contract.Ensures(Contract.Result<Version>() != null); + return default(Version); // dummy return + } + } + + /// <summary> + /// Gets the extra, non-standard Protocol parameters included in the message. + /// </summary> + /// <value></value> + /// <remarks> + /// Implementations of this interface should ensure that this property never returns null. + /// </remarks> + IDictionary<string, string> IMessage.ExtraData { + get { + Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null); + return default(IDictionary<string, string>); + } + } + + #endregion + + #region IDirectedProtocolMessage Members + + /// <summary> + /// Gets the preferred method of transport for the message. + /// </summary> + /// <remarks> + /// For indirect messages this will likely be GET+POST, which both can be simulated in the user agent: + /// the GET with a simple 301 Redirect, and the POST with an HTML form in the response with javascript + /// to automate submission. + /// </remarks> + HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods { + get { throw new NotImplementedException(); } + } + + /// <summary> + /// Gets the URL of the intended receiver of this message. + /// </summary> + Uri IDirectedProtocolMessage.Recipient { + get { throw new NotImplementedException(); } + } + + #endregion + + #region IProtocolMessage Members + + /// <summary> + /// Gets the level of protection this message requires. + /// </summary> + MessageProtections IProtocolMessage.RequiredProtection { + get { throw new NotImplementedException(); } + } + + /// <summary> + /// Gets a value indicating whether this is a direct or indirect message. + /// </summary> + MessageTransport IProtocolMessage.Transport { + get { throw new NotImplementedException(); } + } + + #endregion + + #region IMessage 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> + void IMessage.EnsureValidMessage() { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs index f2c9add..6f8c4f9 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4918 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.Messaging { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class MessagingStrings { @@ -70,6 +70,15 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Looks up a localized string similar to Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false.. + /// </summary> + internal static string BinaryDataRequiresMultipart { + get { + return ResourceManager.GetString("BinaryDataRequiresMultipart", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed.. /// </summary> internal static string CurrentHttpContextRequired { @@ -385,6 +394,15 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Looks up a localized string similar to An HttpContext.Current.Session object is required.. + /// </summary> + internal static string SessionRequired { + get { + return ResourceManager.GetString("SessionRequired", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Message signature was incorrect.. /// </summary> internal static string SignatureInvalid { diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx index 3d4e317..bdf4f68 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx @@ -297,4 +297,10 @@ <data name="StreamMustHaveKnownLength" xml:space="preserve"> <value>The stream must have a known length.</value> </data> + <data name="BinaryDataRequiresMultipart" xml:space="preserve"> + <value>Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false.</value> + </data> + <data name="SessionRequired" xml:space="preserve"> + <value>An HttpContext.Current.Session object is required.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index 04d91de..6a55bc9 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -89,7 +89,7 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Transforms an OutgoingWebResponse to an MVC-friendly ActionResult. /// </summary> - /// <param name="response">The response to send to the uesr agent.</param> + /// <param name="response">The response to send to the user agent.</param> /// <returns>The <see cref="ActionResult"/> instance to be returned by the Controller's action method.</returns> public static ActionResult AsActionResult(this OutgoingWebResponse response) { Contract.Requires<ArgumentNullException>(response != null); @@ -107,14 +107,7 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); HttpContext context = HttpContext.Current; - // We use Request.Url for the full path to the server, and modify it - // with Request.RawUrl to capture both the cookieless session "directory" if it exists - // and the original path in case URL rewriting is going on. We don't want to be - // fooled by URL rewriting because we're comparing the actual URL with what's in - // the return_to parameter in some cases. - // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless - // session, but not the URL rewriting problem. - return new Uri(context.Request.Url, context.Request.RawUrl); + return HttpRequestInfo.GetPublicFacingUrl(context.Request, context.Request.ServerVariables); } /// <summary> @@ -153,6 +146,63 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentNullException>(requestHandler != null); Contract.Requires<ArgumentNullException>(parts != null); + PostMultipartNoGetResponse(request, requestHandler, parts); + return requestHandler.GetResponse(request); + } + + /// <summary> + /// Assembles a message comprised of the message on a given exception and all inner exceptions. + /// </summary> + /// <param name="exception">The exception.</param> + /// <returns>The assembled message.</returns> + public static string ToStringDescriptive(this Exception exception) { + // The input being null is probably bad, but since this method is called + // from a catch block, we don't really want to throw a new exception and + // hide the details of this one. + if (exception == null) { + Logger.Messaging.Error("MessagingUtilities.GetAllMessages called with null input."); + } + + StringBuilder message = new StringBuilder(); + while (exception != null) { + message.Append(exception.Message); + exception = exception.InnerException; + if (exception != null) { + message.Append(" "); + } + } + + return message.ToString(); + } + + /// <summary> + /// Flattens the specified sequence of sequences. + /// </summary> + /// <typeparam name="T">The type of element contained in the sequence.</typeparam> + /// <param name="sequence">The sequence of sequences to flatten.</param> + /// <returns>A sequence of the contained items.</returns> + [Obsolete("Use Enumerable.SelectMany instead.")] + public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> sequence) { + ErrorUtilities.VerifyArgumentNotNull(sequence, "sequence"); + + foreach (IEnumerable<T> subsequence in sequence) { + foreach (T item in subsequence) { + yield return item; + } + } + } + + /// <summary> + /// Sends a multipart HTTP POST request (useful for posting files) but doesn't call GetResponse on it. + /// </summary> + /// <param name="request">The HTTP request.</param> + /// <param name="requestHandler">The request handler.</param> + /// <param name="parts">The parts to include in the POST entity.</param> + internal static void PostMultipartNoGetResponse(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable<MultipartPostPart> parts) { + Contract.Requires<ArgumentNullException>(request != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + Contract.Requires<ArgumentNullException>(parts != null); + Reporting.RecordFeatureUse("MessagingUtilities.PostMultipart"); parts = parts.CacheGeneratedResults(); string boundary = Guid.NewGuid().ToString(); @@ -193,33 +243,6 @@ namespace DotNetOpenAuth.Messaging { requestStream.Dispose(); } } - - return requestHandler.GetResponse(request); - } - - /// <summary> - /// Assembles a message comprised of the message on a given exception and all inner exceptions. - /// </summary> - /// <param name="exception">The exception.</param> - /// <returns>The assembled message.</returns> - public static string ToStringDescriptive(this Exception exception) { - // The input being null is probably bad, but since this method is called - // from a catch block, we don't really want to throw a new exception and - // hide the details of this one. - if (exception == null) { - Logger.Messaging.Error("MessagingUtilities.GetAllMessages called with null input."); - } - - StringBuilder message = new StringBuilder(); - while (exception != null) { - message.Append(exception.Message); - exception = exception.InnerException; - if (exception != null) { - message.Append(" "); - } - } - - return message.ToString(); } /// <summary> @@ -324,6 +347,7 @@ namespace DotNetOpenAuth.Messaging { } } +#if !CLR4 /// <summary> /// Copies the contents of one stream to another. /// </summary> @@ -339,8 +363,9 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentNullException>(copyTo != null); Contract.Requires<ArgumentException>(copyFrom.CanRead, MessagingStrings.StreamUnreadable); Contract.Requires<ArgumentException>(copyTo.CanWrite, MessagingStrings.StreamUnwritable); - return CopyTo(copyFrom, copyTo, int.MaxValue); + return CopyUpTo(copyFrom, copyTo, int.MaxValue); } +#endif /// <summary> /// Copies the contents of one stream to another. @@ -353,7 +378,7 @@ namespace DotNetOpenAuth.Messaging { /// Copying begins at the streams' current positions. /// The positions are NOT reset after copying is complete. /// </remarks> - internal static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) { + internal static int CopyUpTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) { Contract.Requires<ArgumentNullException>(copyFrom != null); Contract.Requires<ArgumentNullException>(copyTo != null); Contract.Requires<ArgumentException>(copyFrom.CanRead, MessagingStrings.StreamUnreadable); @@ -755,7 +780,10 @@ namespace DotNetOpenAuth.Messaging { if (throwOnNullKey) { throw new ArgumentException(MessagingStrings.UnexpectedNullKey); } else { - Logger.OpenId.WarnFormat("Null key with value {0} encountered while translating NameValueCollection to Dictionary.", nvc[key]); + // Only emit a warning if there was a non-empty value. + if (!string.IsNullOrEmpty(nvc[key])) { + Logger.OpenId.WarnFormat("Null key with value {0} encountered while translating NameValueCollection to Dictionary.", nvc[key]); + } } } else { dictionary.Add(key, nvc[key]); @@ -847,7 +875,7 @@ namespace DotNetOpenAuth.Messaging { /// by using appropriate character escaping. /// </summary> /// <param name="value">The untrusted string value to be escaped to protected against XSS attacks. May be null.</param> - /// <returns>The escaped string.</returns> + /// <returns>The escaped string, surrounded by single-quotes.</returns> internal static string GetSafeJavascriptValue(string value) { if (value == null) { return "null"; diff --git a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs index 06cb5fc..cc655cf 100644 --- a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs +++ b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs @@ -57,7 +57,7 @@ namespace DotNetOpenAuth.Messaging { this.ResponseStream = new MemoryStream(response.ContentLength < 0 ? 4 * 1024 : (int)response.ContentLength); using (Stream responseStream = response.GetResponseStream()) { // BUGBUG: strictly speaking, is the response were exactly the limit, we'd report it as truncated here. - this.IsResponseTruncated = responseStream.CopyTo(this.ResponseStream, maximumBytesToRead) == maximumBytesToRead; + this.IsResponseTruncated = responseStream.CopyUpTo(this.ResponseStream, maximumBytesToRead) == maximumBytesToRead; this.ResponseStream.Seek(0, SeekOrigin.Begin); } } @@ -183,7 +183,10 @@ namespace DotNetOpenAuth.Messaging { /// would transmit the message that normally would be transmitted via a user agent redirect. /// </summary> /// <param name="channel">The channel to use for encoding.</param> - /// <returns>The URL that would transmit the original message.</returns> + /// <returns> + /// The URL that would transmit the original message. This URL may exceed the normal 2K limit, + /// and should therefore be broken up manually and POSTed as form fields when it exceeds this length. + /// </returns> /// <remarks> /// This is useful for desktop applications that will spawn a user agent to transmit the message /// rather than cause a redirect. diff --git a/src/DotNetOpenAuth/Messaging/ProtocolException.cs b/src/DotNetOpenAuth/Messaging/ProtocolException.cs index 25f8eee..fb9ec6d 100644 --- a/src/DotNetOpenAuth/Messaging/ProtocolException.cs +++ b/src/DotNetOpenAuth/Messaging/ProtocolException.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Messaging { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; + using System.Security; using System.Security.Permissions; /// <summary> @@ -79,7 +80,11 @@ namespace DotNetOpenAuth.Messaging { /// <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> +#if CLR4 + [SecurityCritical] +#else [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] +#endif public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { base.GetObjectData(info, context); throw new NotImplementedException(); diff --git a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartOriginalEncoder.cs b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartOriginalEncoder.cs new file mode 100644 index 0000000..9ad55c9 --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartOriginalEncoder.cs @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------- +// <copyright file="IMessagePartOriginalEncoder.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging.Reflection { + /// <summary> + /// An interface describing how various objects can be serialized and deserialized between their object and string forms. + /// </summary> + /// <remarks> + /// Implementations of this interface must include a default constructor and must be thread-safe. + /// </remarks> + public interface IMessagePartOriginalEncoder : IMessagePartEncoder { + /// <summary> + /// Encodes the specified value as the original value that was formerly decoded. + /// </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> + string EncodeAsOriginalString(object value); + } +} diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs index 5493ba6..3b41b35 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs @@ -66,7 +66,20 @@ namespace DotNetOpenAuth.Messaging.Reflection { internal MessageDictionary GetDictionary(IMessage message) { Contract.Requires<ArgumentNullException>(message != null); Contract.Ensures(Contract.Result<MessageDictionary>() != null); - return new MessageDictionary(message, this); + return this.GetDictionary(message, false); + } + + /// <summary> + /// Gets a dictionary that provides read/write access to a message. + /// </summary> + /// <param name="message">The message the dictionary should provide access to.</param> + /// <param name="getOriginalValues">A value indicating whether this message dictionary will retrieve original values instead of normalized ones.</param> + /// <returns>The dictionary accessor to the message</returns> + [Pure] + internal MessageDictionary GetDictionary(IMessage message, bool getOriginalValues) { + Contract.Requires<ArgumentNullException>(message != null); + Contract.Ensures(Contract.Result<MessageDictionary>() != null); + return new MessageDictionary(message, this, getOriginalValues); } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs index ff8b74b..125742c 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs @@ -78,7 +78,19 @@ namespace DotNetOpenAuth.Messaging.Reflection { [Pure] internal MessageDictionary GetAccessor(IMessage message) { Contract.Requires<ArgumentNullException>(message != null); - return this.Get(message).GetDictionary(message); + return this.GetAccessor(message, false); + } + + /// <summary> + /// Gets the dictionary that provides read/write access to a message. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="getOriginalValues">A value indicating whether this message dictionary will retrieve original values instead of normalized ones.</param> + /// <returns>The dictionary.</returns> + [Pure] + internal MessageDictionary GetAccessor(IMessage message, bool getOriginalValues) { + Contract.Requires<ArgumentNullException>(message != null); + return this.Get(message).GetDictionary(message, getOriginalValues); } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs index f6eed24..2b60a9c 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs @@ -30,17 +30,24 @@ namespace DotNetOpenAuth.Messaging.Reflection { private readonly MessageDescription description; /// <summary> + /// Whether original string values should be retrieved instead of normalized ones. + /// </summary> + private readonly bool getOriginalValues; + + /// <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> /// <param name="description">The message description.</param> + /// <param name="getOriginalValues">A value indicating whether this message dictionary will retrieve original values instead of normalized ones.</param> [Pure] - internal MessageDictionary(IMessage message, MessageDescription description) { + internal MessageDictionary(IMessage message, MessageDescription description, bool getOriginalValues) { Contract.Requires<ArgumentNullException>(message != null); Contract.Requires<ArgumentNullException>(description != null); this.message = message; this.description = description; + this.getOriginalValues = getOriginalValues; } /// <summary> @@ -103,7 +110,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { 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) { + if (pair.Value.GetValue(this.message, this.getOriginalValues) != null) { keys.Add(pair.Key); } } @@ -126,8 +133,8 @@ namespace DotNetOpenAuth.Messaging.Reflection { 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)); + if (part.GetValue(this.message, this.getOriginalValues) != null) { + values.Add(part.GetValue(this.message, this.getOriginalValues)); } } @@ -167,7 +174,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { MessagePart part; if (this.description.Mapping.TryGetValue(key, out part)) { // Never throw KeyNotFoundException for declared properties. - return part.GetValue(this.message); + return part.GetValue(this.message, this.getOriginalValues); } else { return this.message.ExtraData[key]; } @@ -223,7 +230,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <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); + (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message, this.getOriginalValues) != null); } /// <summary> @@ -237,7 +244,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { } else { MessagePart part; if (this.description.Mapping.TryGetValue(key, out part)) { - if (part.GetValue(this.message) != null) { + if (part.GetValue(this.message, this.getOriginalValues) != null) { part.SetValue(this.message, null); return true; } @@ -255,7 +262,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { public bool TryGetValue(string key, out string value) { MessagePart part; if (this.description.Mapping.TryGetValue(key, out part)) { - value = part.GetValue(this.message); + value = part.GetValue(this.message, this.getOriginalValues); return value != null; } return this.message.ExtraData.TryGetValue(key, out value); @@ -306,7 +313,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { 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); + return string.Equals(part.GetValue(this.message, this.getOriginalValues), item.Value, StringComparison.Ordinal); } else { return this.message.ExtraData.Contains(item); } diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index 08e2411..b876ec4 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { using System.Net.Security; using System.Reflection; using System.Xml; + using DotNetOpenAuth.Configuration; using DotNetOpenAuth.OpenId; /// <summary> @@ -73,10 +74,10 @@ namespace DotNetOpenAuth.Messaging.Reflection { Contract.Assume(str != null); return bool.Parse(str); }; - Func<string, Identifier> safeIdentfier = str => { + Func<string, Identifier> safeIdentifier = str => { Contract.Assume(str != null); ErrorUtilities.VerifyFormat(str.Length > 0, MessagingStrings.NonEmptyStringExpected); - return Identifier.Parse(str); + return Identifier.Parse(str, true); }; Func<byte[], string> safeFromByteArray = bytes => { Contract.Assume(bytes != null); @@ -90,14 +91,14 @@ namespace DotNetOpenAuth.Messaging.Reflection { Contract.Assume(str != null); return new Realm(str); }; - Map<Uri>(uri => uri.AbsoluteUri, safeUri); - Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc)); - Map<byte[]>(safeFromByteArray, safeToByteArray); - Map<Realm>(realm => realm.ToString(), safeRealm); - Map<Identifier>(id => id.ToString(), safeIdentfier); - Map<bool>(value => value.ToString().ToLowerInvariant(), safeBool); - Map<CultureInfo>(c => c.Name, str => new CultureInfo(str)); - Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), str => str.Split(',').Select(s => new CultureInfo(s)).ToArray()); + Map<Uri>(uri => uri.AbsoluteUri, uri => uri.OriginalString, safeUri); + Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), null, str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc)); + Map<byte[]>(safeFromByteArray, null, safeToByteArray); + Map<Realm>(realm => realm.ToString(), realm => realm.OriginalString, safeRealm); + Map<Identifier>(id => id.SerializedString, id => id.OriginalString, safeIdentifier); + Map<bool>(value => value.ToString().ToLowerInvariant(), null, safeBool); + Map<CultureInfo>(c => c.Name, null, str => new CultureInfo(str)); + Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), null, str => str.Split(',').Select(s => new CultureInfo(s)).ToArray()); } /// <summary> @@ -129,9 +130,28 @@ namespace DotNetOpenAuth.Messaging.Reflection { Contract.Assume(this.memberDeclaredType != null); // CC missing PropertyInfo.PropertyType ensures result != null 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); + if (this.memberDeclaredType.IsGenericType && + this.memberDeclaredType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + // It's a nullable type. Try again to look up an appropriate converter for the underlying type. + Type underlyingType = Nullable.GetUnderlyingType(this.memberDeclaredType); + ValueMapping underlyingMapping; + if (converters.TryGetValue(underlyingType, out underlyingMapping)) { + this.converter = new ValueMapping( + underlyingMapping.ValueToString, + null, + str => str != null ? underlyingMapping.StringToValue(str) : null); + } else { + this.converter = new ValueMapping( + obj => obj != null ? obj.ToString() : null, + null, + str => str != null ? Convert.ChangeType(str, underlyingType, CultureInfo.InvariantCulture) : null); + } + } else { + this.converter = new ValueMapping( + obj => obj != null ? obj.ToString() : null, + null, + str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null); + } } } else { this.converter = new ValueMapping(GetEncoder(attribute.Encoder)); @@ -189,7 +209,8 @@ namespace DotNetOpenAuth.Messaging.Reflection { try { if (this.IsConstantValue) { string constantValue = this.GetValue(message); - if (!string.Equals(constantValue, value)) { + var caseSensitivity = DotNetOpenAuthSection.Configuration.Messaging.Strict ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + if (!string.Equals(constantValue, value, caseSensitivity)) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, MessagingStrings.UnexpectedMessagePartValueForConstant, @@ -211,7 +232,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { } /// <summary> - /// Gets the value of a member of a given message. + /// Gets the normalized form of a value of a member of a given message. /// Used in serialization. /// </summary> /// <param name="message">The message instance to read the value from.</param> @@ -219,7 +240,23 @@ namespace DotNetOpenAuth.Messaging.Reflection { internal string GetValue(IMessage message) { try { object value = this.GetValueAsObject(message); - return this.ToString(value); + return this.ToString(value, false); + } catch (FormatException ex) { + throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name); + } + } + + /// <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> + /// <param name="originalValue">A value indicating whether the original value should be retrieved (as opposed to a normalized form of it).</param> + /// <returns>The string representation of the member's value.</returns> + internal string GetValue(IMessage message, bool originalValue) { + try { + object value = this.GetValueAsObject(message); + return this.ToString(value, originalValue); } catch (FormatException ex) { throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name); } @@ -252,15 +289,24 @@ namespace DotNetOpenAuth.Messaging.Reflection { } /// <summary> - /// Adds a pair of type conversion functions to the static converstion map. + /// Adds a pair of type conversion functions to the static conversion 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="toOriginalString">The mapping function that converts some custom value to its original (non-normalized) string. May be null if the same as the <paramref name="toString"/> function.</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) { + private static void Map<T>(Func<T, string> toString, Func<T, string> toOriginalString, Func<string, T> toValue) { + Contract.Requires<ArgumentNullException>(toString != null, "toString"); + Contract.Requires<ArgumentNullException>(toValue != null, "toValue"); + + if (toOriginalString == null) { + toOriginalString = toString; + } + Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null; + Func<object, string> safeToOriginalString = obj => obj != null ? toOriginalString((T)obj) : null; Func<string, object> safeToT = str => str != null ? toValue(str) : default(T); - converters.Add(typeof(T), new ValueMapping(safeToString, safeToT)); + converters.Add(typeof(T), new ValueMapping(safeToString, safeToOriginalString, safeToT)); } /// <summary> @@ -312,11 +358,12 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// Converts the member's value to its string representation. /// </summary> /// <param name="value">The value of the member.</param> + /// <param name="originalString">A value indicating whether a string matching the originally decoded string should be returned (as opposed to a normalized string).</param> /// <returns> /// The string representation of the member's value. /// </returns> - private string ToString(object value) { - return this.converter.ValueToString(value); + private string ToString(object value, bool originalString) { + return originalString ? this.converter.ValueToOriginalString(value) : this.converter.ValueToString(value); } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs index 1c7631e..b0b8b47 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs @@ -19,6 +19,12 @@ namespace DotNetOpenAuth.Messaging.Reflection { internal readonly Func<object, string> ValueToString; /// <summary> + /// The mapping function that converts some custom type to the original string + /// (possibly non-normalized) that represents it. + /// </summary> + internal readonly Func<object, string> ValueToOriginalString; + + /// <summary> /// The mapping function that converts a string to some custom type. /// </summary> internal readonly Func<string, object> StringToValue; @@ -26,13 +32,15 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <summary> /// Initializes a new instance of the <see cref="ValueMapping"/> struct. /// </summary> - /// <param name="toString">The mapping function that converts some custom type to a string.</param> - /// <param name="toValue">The mapping function that converts a string to some custom type.</param> - internal ValueMapping(Func<object, string> toString, Func<string, object> toValue) { + /// <param name="toString">The mapping function that converts some custom value to a string.</param> + /// <param name="toOriginalString">The mapping function that converts some custom value to its original (non-normalized) string. May be null if the same as the <paramref name="toString"/> function.</param> + /// <param name="toValue">The mapping function that converts a string to some custom value.</param> + internal ValueMapping(Func<object, string> toString, Func<object, string> toOriginalString, Func<string, object> toValue) { Contract.Requires<ArgumentNullException>(toString != null); Contract.Requires<ArgumentNullException>(toValue != null); this.ValueToString = toString; + this.ValueToOriginalString = toOriginalString ?? toString; this.StringToValue = toValue; } @@ -45,8 +53,15 @@ namespace DotNetOpenAuth.Messaging.Reflection { var nullEncoder = encoder as IMessagePartNullEncoder; string nullString = nullEncoder != null ? nullEncoder.EncodedNullValue : null; + var originalStringEncoder = encoder as IMessagePartOriginalEncoder; + Func<object, string> originalStringEncode = encoder.Encode; + if (originalStringEncoder != null) { + originalStringEncode = originalStringEncoder.EncodeAsOriginalString; + } + this.ValueToString = obj => (obj != null) ? encoder.Encode(obj) : nullString; this.StringToValue = str => (str != null) ? encoder.Decode(str) : null; + this.ValueToOriginalString = obj => (obj != null) ? originalStringEncode(obj) : nullString; } } } diff --git a/src/DotNetOpenAuth/Migrated rules for DotNetOpenAuth.ruleset b/src/DotNetOpenAuth/Migrated rules for DotNetOpenAuth.ruleset new file mode 100644 index 0000000..db238b6 --- /dev/null +++ b/src/DotNetOpenAuth/Migrated rules for DotNetOpenAuth.ruleset @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<RuleSet Name="Migrated rules for DotNetOpenAuth.ruleset" Description="This rule set was created from the CodeAnalysisRules property for the "Debug (Any CPU)" configuration in project "C:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\DotNetOpenAuth.csproj"." ToolsVersion="10.0"> + <IncludeAll Action="Warning" /> + <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed"> + <Rule Id="CA1054" Action="None" /> + <Rule Id="CA1055" Action="None" /> + <Rule Id="CA1056" Action="None" /> + <Rule Id="CA2104" Action="None" /> + </Rules> +</RuleSet>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs b/src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs new file mode 100644 index 0000000..4b88d04 --- /dev/null +++ b/src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------- +// <copyright file="OpenIdAjaxOptions.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Mvc { + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// A set of customizations available for the scripts sent to the browser in AJAX OpenID scenarios. + /// </summary> + public class OpenIdAjaxOptions { + /// <summary> + /// Initializes a new instance of the <see cref="OpenIdAjaxOptions"/> class. + /// </summary> + public OpenIdAjaxOptions() { + this.AssertionHiddenFieldId = "openid_openidAuthData"; + this.ReturnUrlHiddenFieldId = "ReturnUrl"; + } + + /// <summary> + /// Gets or sets the ID of the hidden field that should carry the positive assertion + /// until it is posted to the RP. + /// </summary> + public string AssertionHiddenFieldId { get; set; } + + /// <summary> + /// Gets or sets the ID of the hidden field that should be set with the parent window/frame's URL + /// prior to posting the form with the positive assertion. Useful for jQuery popup dialogs. + /// </summary> + public string ReturnUrlHiddenFieldId { get; set; } + + /// <summary> + /// Gets or sets the index of the form in the document.forms array on the browser that should + /// be submitted when the user is ready to send the positive assertion to the RP. + /// </summary> + public int FormIndex { get; set; } + + /// <summary> + /// Gets or sets the id of the form in the document.forms array on the browser that should + /// be submitted when the user is ready to send the positive assertion to the RP. A value + /// in this property takes precedence over any value in the <see cref="FormIndex"/> property. + /// </summary> + /// <value>The form id.</value> + public string FormId { get; set; } + + /// <summary> + /// Gets or sets the preloaded discovery results. + /// </summary> + public string PreloadedDiscoveryResults { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether to print diagnostic trace messages in the browser. + /// </summary> + public bool ShowDiagnosticTrace { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether to show all the "hidden" iframes that facilitate + /// asynchronous authentication of the user for diagnostic purposes. + /// </summary> + public bool ShowDiagnosticIFrame { get; set; } + + /// <summary> + /// Gets the form key to use when accessing the relevant form. + /// </summary> + internal string FormKey { + get { return string.IsNullOrEmpty(this.FormId) ? this.FormIndex.ToString(CultureInfo.InvariantCulture) : MessagingUtilities.GetSafeJavascriptValue(this.FormId); } + } + } +} diff --git a/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs new file mode 100644 index 0000000..193e445 --- /dev/null +++ b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs @@ -0,0 +1,432 @@ +//----------------------------------------------------------------------- +// <copyright file="OpenIdHelper.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Mvc { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Text; + using System.Web; + using System.Web.Mvc; + using System.Web.Routing; + using System.Web.UI; + using DotNetOpenAuth.Configuration; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.RelyingParty; + + /// <summary> + /// Methods that generate HTML or Javascript for hosting AJAX OpenID "controls" on + /// ASP.NET MVC web sites. + /// </summary> + public static class OpenIdHelper { + /// <summary> + /// Emits a series of stylesheet import tags to support the AJAX OpenID Selector. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <param name="page">The page being rendered.</param> + /// <returns>HTML that should be sent directly to the browser.</returns> + public static string OpenIdSelectorStyles(this HtmlHelper html, Page page) { + Contract.Requires<ArgumentNullException>(html != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Ensures(Contract.Result<string>() != null); + + StringWriter result = new StringWriter(); + result.WriteStylesheetLink(page, OpenId.RelyingParty.OpenIdSelector.EmbeddedStylesheetResourceName); + result.WriteStylesheetLink(page, OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedStylesheetResourceName); + return result.ToString(); + } + + /// <summary> + /// Emits a series of script import tags and some inline script to support the AJAX OpenID Selector. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <param name="page">The page being rendered.</param> + /// <returns>HTML that should be sent directly to the browser.</returns> + public static string OpenIdSelectorScripts(this HtmlHelper html, Page page) { + return OpenIdSelectorScripts(html, page, null, null); + } + + /// <summary> + /// Emits a series of script import tags and some inline script to support the AJAX OpenID Selector. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="selectorOptions">An optional instance of an <see cref="OpenIdSelector"/> control, whose properties have been customized to express how this MVC control should be rendered.</param> + /// <param name="additionalOptions">An optional set of additional script customizations.</param> + /// <returns> + /// HTML that should be sent directly to the browser. + /// </returns> + public static string OpenIdSelectorScripts(this HtmlHelper html, Page page, OpenIdSelector selectorOptions, OpenIdAjaxOptions additionalOptions) { + Contract.Requires<ArgumentNullException>(html != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Ensures(Contract.Result<string>() != null); + + if (selectorOptions == null) { + selectorOptions = new OpenId.RelyingParty.OpenIdSelector(); + } + + if (additionalOptions == null) { + additionalOptions = new OpenIdAjaxOptions(); + } + + StringWriter result = new StringWriter(); + + if (additionalOptions.ShowDiagnosticIFrame || additionalOptions.ShowDiagnosticTrace) { + string scriptFormat = @"window.openid_visible_iframe = {0}; // causes the hidden iframe to show up +window.openid_trace = {1}; // causes lots of messages"; + result.WriteScriptBlock(string.Format( + CultureInfo.InvariantCulture, + scriptFormat, + additionalOptions.ShowDiagnosticIFrame ? "true" : "false", + additionalOptions.ShowDiagnosticTrace ? "true" : "false")); + } + var scriptResources = new[] { + OpenIdRelyingPartyControlBase.EmbeddedJavascriptResource, + OpenIdRelyingPartyAjaxControlBase.EmbeddedAjaxJavascriptResource, + OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedScriptResourceName, + }; + result.WriteScriptTags(page, scriptResources); + + if (selectorOptions.DownloadYahooUILibrary) { + result.WriteScriptTags(new[] { "https://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js" }); + } + + var blockBuilder = new StringWriter(); + if (selectorOptions.DownloadYahooUILibrary) { + blockBuilder.WriteLine(@" try { + if (YAHOO) { + var loader = new YAHOO.util.YUILoader({ + require: ['button', 'menu'], + loadOptional: false, + combine: true + }); + + loader.insert(); + } + } catch (e) { }"); + } + + blockBuilder.WriteLine("window.aspnetapppath = '{0}';", VirtualPathUtility.AppendTrailingSlash(HttpContext.Current.Request.ApplicationPath)); + + // Positive assertions can last no longer than this library is willing to consider them valid, + // and when they come with OP private associations they last no longer than the OP is willing + // to consider them valid. We assume the OP will hold them valid for at least five minutes. + double assertionLifetimeInMilliseconds = Math.Min(TimeSpan.FromMinutes(5).TotalMilliseconds, Math.Min(DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime.TotalMilliseconds, DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime.TotalMilliseconds)); + blockBuilder.WriteLine( + "{0} = {1};", + OpenIdRelyingPartyAjaxControlBase.MaxPositiveAssertionLifetimeJsName, + assertionLifetimeInMilliseconds.ToString(CultureInfo.InvariantCulture)); + + if (additionalOptions.PreloadedDiscoveryResults != null) { + blockBuilder.WriteLine(additionalOptions.PreloadedDiscoveryResults); + } + + string discoverUrl = VirtualPathUtility.AppendTrailingSlash(HttpContext.Current.Request.ApplicationPath) + html.RouteCollection["OpenIdDiscover"].GetVirtualPath(html.ViewContext.RequestContext, new RouteValueDictionary(new { identifier = "xxx" })).VirtualPath; + string blockFormat = @" {0} = function (argument, resultFunction, errorCallback) {{ + jQuery.ajax({{ + async: true, + dataType: 'text', + error: function (request, status, error) {{ errorCallback(status, argument); }}, + success: function (result) {{ resultFunction(result, argument); }}, + url: '{1}'.replace('xxx', encodeURIComponent(argument)) + }}); + }};"; + blockBuilder.WriteLine(blockFormat, OpenIdRelyingPartyAjaxControlBase.CallbackJSFunctionAsync, discoverUrl); + + blockFormat = @" window.postLoginAssertion = function (positiveAssertion) {{ + $('#{0}')[0].setAttribute('value', positiveAssertion); + if ($('#{1}')[0] && !$('#{1}')[0].value) {{ // popups have no ReturnUrl predefined, but full page LogOn does. + $('#{1}')[0].setAttribute('value', window.parent.location.href); + }} + document.forms[{2}].submit(); + }};"; + blockBuilder.WriteLine( + blockFormat, + additionalOptions.AssertionHiddenFieldId, + additionalOptions.ReturnUrlHiddenFieldId, + additionalOptions.FormKey); + + blockFormat = @" $(function () {{ + var box = document.getElementsByName('openid_identifier')[0]; + initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, + null, // js function to invoke on receiving a positive assertion + {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, + false, // auto postback + null); // PostBackEventReference (unused in MVC) + }});"; + blockBuilder.WriteLine( + blockFormat, + MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenIdTextBox.EmbeddedLogoResourceName)), + MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName)), + MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)), + MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName)), + selectorOptions.Throttle, + selectorOptions.Timeout.TotalMilliseconds, + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnText), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnToolTip), + selectorOptions.TextBox.ShowLogOnPostBackButton ? "true" : "false", + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnPostBackToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.RetryText), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.RetryToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.BusyToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.IdentifierRequiredMessage), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnInProgressMessage), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticationSucceededToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticatedAsToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticationFailedToolTip)); + + result.WriteScriptBlock(blockBuilder.ToString()); + result.WriteScriptTags(page, OpenId.RelyingParty.OpenIdSelector.EmbeddedScriptResourceName); + + Reporting.RecordFeatureUse("MVC " + typeof(OpenIdSelector).Name); + return result.ToString(); + } + + /// <summary> + /// Emits the HTML to render an OpenID Provider button as a part of the overall OpenID Selector UI. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="providerIdentifier">The OP Identifier.</param> + /// <param name="imageUrl">The URL of the image to display on the button.</param> + /// <returns> + /// HTML that should be sent directly to the browser. + /// </returns> + public static string OpenIdSelectorOPButton(this HtmlHelper html, Page page, Identifier providerIdentifier, string imageUrl) { + Contract.Requires<ArgumentNullException>(html != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Requires<ArgumentNullException>(providerIdentifier != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); + Contract.Ensures(Contract.Result<string>() != null); + + return OpenIdSelectorButton(html, page, providerIdentifier, "OPButton", imageUrl); + } + + /// <summary> + /// Emits the HTML to render a generic OpenID button as a part of the overall OpenID Selector UI, + /// allowing the user to enter their own OpenID. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="imageUrl">The URL of the image to display on the button.</param> + /// <returns> + /// HTML that should be sent directly to the browser. + /// </returns> + public static string OpenIdSelectorOpenIdButton(this HtmlHelper html, Page page, string imageUrl) { + Contract.Requires<ArgumentNullException>(html != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); + Contract.Ensures(Contract.Result<string>() != null); + + return OpenIdSelectorButton(html, page, "OpenIDButton", "OpenIDButton", imageUrl); + } + + /// <summary> + /// Emits the HTML to render the entire OpenID Selector UI. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="buttons">The buttons to include on the selector.</param> + /// <returns> + /// HTML that should be sent directly to the browser. + /// </returns> + public static string OpenIdSelector(this HtmlHelper html, Page page, params SelectorButton[] buttons) { + Contract.Requires<ArgumentNullException>(html != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Requires<ArgumentNullException>(buttons != null); + Contract.Ensures(Contract.Result<string>() != null); + + var writer = new StringWriter(); + var h = new HtmlTextWriter(writer); + + h.AddAttribute(HtmlTextWriterAttribute.Class, "OpenIdProviders"); + h.RenderBeginTag(HtmlTextWriterTag.Ul); + + foreach (SelectorButton button in buttons) { + var op = button as SelectorProviderButton; + if (op != null) { + h.Write(OpenIdSelectorOPButton(html, page, op.OPIdentifier, op.Image)); + continue; + } + + var openid = button as SelectorOpenIdButton; + if (openid != null) { + h.Write(OpenIdSelectorOpenIdButton(html, page, openid.Image)); + continue; + } + + ErrorUtilities.VerifySupported(false, "The {0} button is not yet supported for MVC.", button.GetType().Name); + } + + h.RenderEndTag(); // ul + + if (buttons.OfType<SelectorOpenIdButton>().Any()) { + h.Write(OpenIdAjaxTextBox(html)); + } + + return writer.ToString(); + } + + /// <summary> + /// Emits the HTML to render the <see cref="OpenIdAjaxTextBox"/> control as a part of the overall + /// OpenID Selector UI. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <returns> + /// HTML that should be sent directly to the browser. + /// </returns> + public static string OpenIdAjaxTextBox(this HtmlHelper html) { + return @"<div style='display: none' id='OpenIDForm'> + <span class='OpenIdAjaxTextBox' style='display: inline-block; position: relative; font-size: 16px'> + <input name='openid_identifier' id='openid_identifier' size='40' style='padding-left: 18px; border-style: solid; border-width: 1px; border-color: lightgray' /> + </span> + </div>"; + } + + /// <summary> + /// Emits the HTML to render a button as a part of the overall OpenID Selector UI. + /// </summary> + /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="id">The value to assign to the HTML id attribute.</param> + /// <param name="cssClass">The value to assign to the HTML class attribute.</param> + /// <param name="imageUrl">The URL of the image to draw on the button.</param> + /// <returns> + /// HTML that should be sent directly to the browser. + /// </returns> + private static string OpenIdSelectorButton(this HtmlHelper html, Page page, string id, string cssClass, string imageUrl) { + Contract.Requires<ArgumentNullException>(html != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Requires<ArgumentNullException>(id != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); + Contract.Ensures(Contract.Result<string>() != null); + + var writer = new StringWriter(); + var h = new HtmlTextWriter(writer); + + h.AddAttribute(HtmlTextWriterAttribute.Id, id); + if (!string.IsNullOrEmpty(cssClass)) { + h.AddAttribute(HtmlTextWriterAttribute.Class, cssClass); + } + h.RenderBeginTag(HtmlTextWriterTag.Li); + + h.AddAttribute(HtmlTextWriterAttribute.Href, "#"); + h.RenderBeginTag(HtmlTextWriterTag.A); + + h.RenderBeginTag(HtmlTextWriterTag.Div); + h.RenderBeginTag(HtmlTextWriterTag.Div); + + h.AddAttribute(HtmlTextWriterAttribute.Src, imageUrl); + h.RenderBeginTag(HtmlTextWriterTag.Img); + h.RenderEndTag(); + + h.AddAttribute(HtmlTextWriterAttribute.Src, page.ClientScript.GetWebResourceUrl(typeof(OpenIdSelector), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)); + h.AddAttribute(HtmlTextWriterAttribute.Class, "loginSuccess"); + h.AddAttribute(HtmlTextWriterAttribute.Title, "Authenticated as {0}"); + h.RenderBeginTag(HtmlTextWriterTag.Img); + h.RenderEndTag(); + + h.RenderEndTag(); // div + + h.AddAttribute(HtmlTextWriterAttribute.Class, "ui-widget-overlay"); + h.RenderBeginTag(HtmlTextWriterTag.Div); + h.RenderEndTag(); // div + + h.RenderEndTag(); // div + h.RenderEndTag(); // a + h.RenderEndTag(); // li + + return writer.ToString(); + } + + /// <summary> + /// Emits <script> tags that import a given set of scripts given their URLs. + /// </summary> + /// <param name="writer">The writer to emit the tags to.</param> + /// <param name="scriptUrls">The locations of the scripts to import.</param> + private static void WriteScriptTags(this TextWriter writer, IEnumerable<string> scriptUrls) { + Contract.Requires<ArgumentNullException>(writer != null); + Contract.Requires<ArgumentNullException>(scriptUrls != null); + + foreach (string script in scriptUrls) { + writer.WriteLine("<script type='text/javascript' src='{0}'></script>", script); + } + } + + /// <summary> + /// Writes out script tags that import a script from resources embedded in this assembly. + /// </summary> + /// <param name="writer">The writer to emit the tags to.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="resourceName">Name of the resource.</param> + private static void WriteScriptTags(this TextWriter writer, Page page, string resourceName) { + Contract.Requires<ArgumentNullException>(writer != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(resourceName)); + + WriteScriptTags(writer, page, new[] { resourceName }); + } + + /// <summary> + /// Writes out script tags that import scripts from resources embedded in this assembly. + /// </summary> + /// <param name="writer">The writer to emit the tags to.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="resourceNames">The resource names.</param> + private static void WriteScriptTags(this TextWriter writer, Page page, IEnumerable<string> resourceNames) { + Contract.Requires<ArgumentNullException>(writer != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Requires<ArgumentNullException>(resourceNames != null); + + writer.WriteScriptTags(resourceNames.Select(r => page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), r))); + } + + /// <summary> + /// Writes a given script block, surrounding it with <script> and CDATA tags. + /// </summary> + /// <param name="writer">The writer to emit the tags to.</param> + /// <param name="script">The script to inline on the page.</param> + private static void WriteScriptBlock(this TextWriter writer, string script) { + Contract.Requires<ArgumentNullException>(writer != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(script)); + + writer.WriteLine("<script type='text/javascript' language='javascript'><!--"); + writer.WriteLine("//<![CDATA["); + writer.WriteLine(script); + writer.WriteLine("//]]>--></script>"); + } + + /// <summary> + /// Writes a given CSS link. + /// </summary> + /// <param name="writer">The writer to emit the tags to.</param> + /// <param name="page">The page being rendered.</param> + /// <param name="resourceName">Name of the resource containing the CSS content.</param> + private static void WriteStylesheetLink(this TextWriter writer, Page page, string resourceName) { + Contract.Requires<ArgumentNullException>(writer != null); + Contract.Requires<ArgumentNullException>(page != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(resourceName)); + + WriteStylesheetLink(writer, page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyAjaxControlBase), resourceName)); + } + + /// <summary> + /// Writes a given CSS link. + /// </summary> + /// <param name="writer">The writer to emit the tags to.</param> + /// <param name="stylesheet">The stylesheet to link in.</param> + private static void WriteStylesheetLink(this TextWriter writer, string stylesheet) { + Contract.Requires<ArgumentNullException>(writer != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(stylesheet)); + + writer.WriteLine("<link rel='stylesheet' type='text/css' href='{0}' />", stylesheet); + } + } +} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index 036c19a..dc59b56 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System.Diagnostics.Contracts; using System.Globalization; using System.IO; + using System.Linq; using System.Net; using System.Net.Mime; using System.Text; @@ -88,7 +89,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <param name="message">The message with data to encode.</param> /// <returns>A dictionary of name-value pairs with their strings encoded.</returns> - internal static IDictionary<string, string> GetUriEscapedParameters(MessageDictionary message) { + internal static IDictionary<string, string> GetUriEscapedParameters(IEnumerable<KeyValuePair<string, string>> message) { var encodedDictionary = new Dictionary<string, string>(); UriEscapeParameters(message, encodedDictionary); return encodedDictionary; @@ -210,6 +211,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { if ((transmissionMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) { httpRequest = this.InitializeRequestAsAuthHeader(request); } else if ((transmissionMethod & HttpDeliveryMethods.PostRequest) != 0) { + var requestMessageWithBinaryData = request as IMessageWithBinaryData; + ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || !requestMessageWithBinaryData.SendAsMultipart, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader); httpRequest = this.InitializeRequestAsPost(request); } else if ((transmissionMethod & HttpDeliveryMethods.GetRequest) != 0) { httpRequest = InitializeRequestAsGet(request); @@ -282,7 +285,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <param name="source">The dictionary with names and values to encode.</param> /// <param name="destination">The dictionary to add the encoded pairs to.</param> - private static void UriEscapeParameters(IDictionary<string, string> source, IDictionary<string, string> destination) { + private static void UriEscapeParameters(IEnumerable<KeyValuePair<string, string>> source, IDictionary<string, string> destination) { Contract.Requires<ArgumentNullException>(source != null); Contract.Requires<ArgumentNullException>(destination != null); @@ -359,12 +362,22 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { if (hasEntity) { // WARNING: We only set up the request stream for the caller if there is // extra data. If there isn't any extra data, the caller must do this themselves. - if (requestMessage.ExtraData.Count > 0) { - SendParametersInEntity(httpRequest, requestMessage.ExtraData); + var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData; + if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { + // Include the binary data in the multipart entity, and any standard text extra message data. + // The standard declared message parts are included in the authorization header. + var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData); + multiPartFields.AddRange(requestMessage.ExtraData.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); + this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields); } else { - // We'll assume the content length is zero since the caller may not have - // anything. They're responsible to change it when the add the payload if they have one. - httpRequest.ContentLength = 0; + ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart); + if (requestMessage.ExtraData.Count > 0) { + this.SendParametersInEntity(httpRequest, requestMessage.ExtraData); + } else { + // We'll assume the content length is zero since the caller may not have + // anything. They're responsible to change it when the add the payload if they have one. + httpRequest.ContentLength = 0; + } } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs index 004e7d5..cf09036 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs @@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System.Collections.Specialized; using System.Diagnostics.Contracts; using System.Globalization; + using System.Linq; using System.Text; using System.Web; using DotNetOpenAuth.Messaging; @@ -157,7 +158,26 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { signatureBaseStringElements.Add(message.HttpMethod.ToUpperInvariant()); - var encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary); + // For multipart POST messages, only include the message parts that are NOT + // in the POST entity (those parts that may appear in an OAuth authorization header). + var encodedDictionary = new Dictionary<string, string>(); + IEnumerable<KeyValuePair<string, string>> partsToInclude = Enumerable.Empty<KeyValuePair<string, string>>(); + var binaryMessage = message as IMessageWithBinaryData; + if (binaryMessage != null && binaryMessage.SendAsMultipart) { + HttpDeliveryMethods authHeaderInUseFlags = HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest; + ErrorUtilities.VerifyProtocol((binaryMessage.HttpMethods & authHeaderInUseFlags) == authHeaderInUseFlags, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader); + + // Include the declared keys in the signature as those will be signable. + // Cache in local variable to avoid recalculating DeclaredKeys in the delegate. + ICollection<string> declaredKeys = messageDictionary.DeclaredKeys; + partsToInclude = messageDictionary.Where(pair => declaredKeys.Contains(pair.Key)); + } else { + partsToInclude = messageDictionary; + } + + foreach (var pair in OAuthChannel.GetUriEscapedParameters(partsToInclude)) { + encodedDictionary[pair.Key] = pair.Value; + } // An incoming message will already have included the query and form parameters // in the message dictionary, but an outgoing message COULD have SOME parameters @@ -238,6 +258,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <c>true</c> if the signature on the message is valid; otherwise, <c>false</c>. /// </returns> protected virtual bool IsSignatureValid(ITamperResistantOAuthMessage message) { + Contract.Requires<ArgumentNullException>(message != null); + string signature = this.GetSignature(message); return message.Signature == signature; } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs index 7b369c3..4ff52fd 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs @@ -15,8 +15,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { [ContractClassFor(typeof(SigningBindingElementBase))] internal abstract class SigningBindingElementBaseContract : SigningBindingElementBase { /// <summary> - /// Prevents a default instance of the SigningBindingElementBaseContract - /// class from being created. + /// Prevents a default instance of the SigningBindingElementBaseContract class from being created. /// </summary> private SigningBindingElementBaseContract() : base(string.Empty) { diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs index a189dcf..dddbe9e 100644 --- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs +++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuth { using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; + using System.Linq; using System.Net; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; @@ -111,6 +112,27 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Prepares an authorized request that carries an HTTP multi-part POST, allowing for binary data. + /// </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> + /// <param name="binaryData">Extra parameters to include in the message. Must not be null, but may be empty.</param> + /// <returns>The initialized WebRequest object.</returns> + public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken, IEnumerable<MultipartPostPart> binaryData) { + Contract.Requires<ArgumentNullException>(endpoint != null); + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(accessToken)); + Contract.Requires<ArgumentNullException>(binaryData != null); + + AccessProtectedResourceRequest message = this.CreateAuthorizingMessage(endpoint, accessToken); + foreach (MultipartPostPart part in binaryData) { + message.BinaryData.Add(part); + } + + HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message); + return wr; + } + + /// <summary> /// Prepares an HTTP request that has OAuth authorization already attached to it. /// </summary> /// <param name="message">The OAuth authorization message to attach to the HTTP request.</param> diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs index b60fda4..f3231f0 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OAuth.Messages { using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using DotNetOpenAuth.Messaging; @@ -13,7 +14,12 @@ namespace DotNetOpenAuth.OAuth.Messages { /// A message attached to a request for protected resources that provides the necessary /// credentials to be granted access to those resources. /// </summary> - public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage { + public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage, IMessageWithBinaryData { + /// <summary> + /// A store for the binary data that is carried in the message. + /// </summary> + private List<MultipartPostPart> binaryData = new List<MultipartPostPart>(); + /// <summary> /// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class. /// </summary> @@ -43,5 +49,24 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </remarks> [MessagePart("oauth_token", IsRequired = true)] public string AccessToken { get; set; } + + #region IMessageWithBinaryData Members + + /// <summary> + /// Gets the parts of the message that carry binary data. + /// </summary> + /// <value>A list of parts. Never null.</value> + public IList<MultipartPostPart> BinaryData { + get { return this.binaryData; } + } + + /// <summary> + /// Gets a value indicating whether this message should be sent as multi-part POST. + /// </summary> + public bool SendAsMultipart { + get { return this.HttpMethod == "POST" && this.BinaryData.Count > 0; } + } + + #endregion } } diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs index 3593446..fb4c9a1 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4918 +// Runtime Version:4.0.30104.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.OAuth { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class OAuthStrings { @@ -115,29 +115,38 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> - /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.. + /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.. /// </summary> - internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface { + internal static string MinimumConsumerVersionRequirementNotMet { get { - return ResourceManager.GetString("OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface", resourceCulture); + return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture); } } /// <summary> - /// Looks up a localized string similar to The OpenID Relying Party's realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given.. + /// Looks up a localized string similar to Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.. /// </summary> - internal static string OpenIdOAuthRealmConsumerKeyDoNotMatch { + internal static string MultipartPostMustBeUsedWithAuthHeader { get { - return ResourceManager.GetString("OpenIdOAuthRealmConsumerKeyDoNotMatch", resourceCulture); + return ResourceManager.GetString("MultipartPostMustBeUsedWithAuthHeader", resourceCulture); } } /// <summary> - /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.. + /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.. /// </summary> - internal static string MinimumConsumerVersionRequirementNotMet { + internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface { get { - return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture); + return ResourceManager.GetString("OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The OpenID Relying Party's realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given.. + /// </summary> + internal static string OpenIdOAuthRealmConsumerKeyDoNotMatch { + get { + return ResourceManager.GetString("OpenIdOAuthRealmConsumerKeyDoNotMatch", resourceCulture); } } diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx index bbeeda9..34b314b 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx @@ -138,6 +138,9 @@ <data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve"> <value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value> </data> + <data name="MultipartPostMustBeUsedWithAuthHeader" xml:space="preserve"> + <value>Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.</value> + </data> <data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve"> <value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value> </data> diff --git a/src/DotNetOpenAuth/OpenId/Association.cs b/src/DotNetOpenAuth/OpenId/Association.cs index 62e91ec..3c7e89f 100644 --- a/src/DotNetOpenAuth/OpenId/Association.cs +++ b/src/DotNetOpenAuth/OpenId/Association.cs @@ -238,24 +238,28 @@ namespace DotNetOpenAuth.OpenId { /// </returns> public override int GetHashCode() { HMACSHA1 hmac = new HMACSHA1(this.SecretKey); - CryptoStream cs = new CryptoStream(Stream.Null, hmac, CryptoStreamMode.Write); + try { + CryptoStream cs = new CryptoStream(Stream.Null, hmac, CryptoStreamMode.Write); - byte[] hbytes = ASCIIEncoding.ASCII.GetBytes(this.Handle); + byte[] hbytes = ASCIIEncoding.ASCII.GetBytes(this.Handle); - cs.Write(hbytes, 0, hbytes.Length); - cs.Close(); + cs.Write(hbytes, 0, hbytes.Length); + cs.Close(); - byte[] hash = hmac.Hash; - hmac.Clear(); + byte[] hash = hmac.Hash; + hmac.Clear(); - long val = 0; - for (int i = 0; i < hash.Length; i++) { - val = val ^ (long)hash[i]; - } + long val = 0; + for (int i = 0; i < hash.Length; i++) { + val = val ^ (long)hash[i]; + } - val = val ^ this.Expires.ToFileTimeUtc(); + val = val ^ this.Expires.ToFileTimeUtc(); - return (int)val; + return (int)val; + } finally { + ((IDisposable)hmac).Dispose(); + } } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs index 937ecaf..8c952ab 100644 --- a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4918 +// Runtime Version:4.0.30104.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.OpenId.Behaviors { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class BehaviorStrings { diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs index 43d6c03..370192a 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs @@ -209,7 +209,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// or if unsolicited assertions should be rejected at the RP; otherwise <c>false</c>. /// </returns> private bool UseRequestNonce(IMessage message) { - return message != null && (message.Version.Major < 2 || this.securitySettings.RejectUnsolicitedAssertions); + return message != null && (this.securitySettings.RejectUnsolicitedAssertions || + (message.Version.Major < 2 && this.securitySettings.ProtectDownlevelReplayAttacks)); } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs index d27619f..1b58c2f 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs @@ -51,7 +51,7 @@ namespace DotNetOpenAuth.OpenId.Extensions { return; } - if (req.Provider.IsExtensionSupported<ClaimsRequest>()) { + if (req.DiscoveryResult.IsExtensionSupported<ClaimsRequest>()) { Logger.OpenId.Debug("Skipping generation of AX request because the Identifier advertises the Provider supports the Sreg extension."); return; } @@ -277,8 +277,7 @@ namespace DotNetOpenAuth.OpenId.Extensions { /// <returns>The AX format(s) to use based on the Provider's advertised AX support.</returns> private static bool TryDetectOPAttributeFormat(RelyingParty.IAuthenticationRequest request, out AXAttributeFormats attributeFormat) { Contract.Requires<ArgumentNullException>(request != null); - var provider = (RelyingParty.ServiceEndpoint)request.Provider; - attributeFormat = DetectAXFormat(provider.ProviderDescription.Capabilities); + attributeFormat = DetectAXFormat(request.DiscoveryResult.Capabilities); return attributeFormat != AXAttributeFormats.None; } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs index ae483a6..55c2dc5 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs @@ -28,8 +28,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI { /// <see cref="UIModes.Popup"/>. </para> /// <para>An RP may determine whether an arbitrary OP supports this extension (and thereby determine /// whether to use a standard full window redirect or a popup) via the - /// <see cref="IProviderEndpoint.IsExtensionSupported"/> method on the <see cref="DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationRequest.Provider"/> - /// object.</para> + /// <see cref="IdentifierDiscoveryResult.IsExtensionSupported<T>()"/> method.</para> /// </remarks> [Serializable] public sealed class UIRequest : IOpenIdMessageExtension, IMessageWithEvents { @@ -63,6 +62,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI { /// </summary> public UIRequest() { this.LanguagePreference = new[] { CultureInfo.CurrentUICulture }; + this.Mode = UIModes.Popup; } /// <summary> @@ -77,12 +77,11 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI { public CultureInfo[] LanguagePreference { get; set; } /// <summary> - /// Gets the style of UI that the RP is hosting the OP's authentication page in. + /// Gets or sets the style of UI that the RP is hosting the OP's authentication page in. /// </summary> /// <value>Some value from the <see cref="UIModes"/> class. Defaults to <see cref="UIModes.Popup"/>.</value> - [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Design is to allow this later to be changable when more than one value exists.")] [MessagePart("mode", AllowEmpty = false, IsRequired = true)] - public string Mode { get { return UIModes.Popup; } } + public string Mode { get; set; } /// <summary> /// Gets or sets a value indicating whether the Relying Party has an icon diff --git a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs new file mode 100644 index 0000000..ba9852e --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs @@ -0,0 +1,515 @@ +//----------------------------------------------------------------------- +// <copyright file="HostMetaDiscoveryService.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Net; + using System.Security; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + using System.Security.Permissions; + using System.Text; + using System.Text.RegularExpressions; + using System.Xml; + using System.Xml.XPath; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.RelyingParty; + using DotNetOpenAuth.Xrds; + using DotNetOpenAuth.Yadis; + + /// <summary> + /// The discovery service to support host-meta based discovery, such as Google Apps for Domains. + /// </summary> + /// <remarks> + /// The spec for this discovery mechanism can be found at: + /// http://groups.google.com/group/google-federated-login-api/web/openid-discovery-for-hosted-domains + /// and the XMLDSig spec referenced in that spec can be found at: + /// http://wiki.oasis-open.org/xri/XrdOne/XmlDsigProfile + /// </remarks> + public class HostMetaDiscoveryService : IIdentifierDiscoveryService { + /// <summary> + /// The URI template for discovery host-meta on domains hosted by + /// Google Apps for Domains. + /// </summary> + private static readonly HostMetaProxy GoogleHostedHostMeta = new HostMetaProxy("https://www.google.com/accounts/o8/.well-known/host-meta?hd={0}", "hosted-id.google.com"); + + /// <summary> + /// Path to the well-known location of the host-meta document at a domain. + /// </summary> + private const string LocalHostMetaPath = "/.well-known/host-meta"; + + /// <summary> + /// The pattern within a host-meta file to look for to obtain the URI to the XRDS document. + /// </summary> + private static readonly Regex HostMetaLink = new Regex(@"^Link: <(?<location>.+?)>; rel=""describedby http://reltype.google.com/openid/xrd-op""; type=""application/xrds\+xml""$"); + + /// <summary> + /// Initializes a new instance of the <see cref="HostMetaDiscoveryService"/> class. + /// </summary> + public HostMetaDiscoveryService() { + this.TrustedHostMetaProxies = new List<HostMetaProxy>(); + } + + /// <summary> + /// Gets the set of URI templates to use to contact host-meta hosting proxies + /// for domain discovery. + /// </summary> + public IList<HostMetaProxy> TrustedHostMetaProxies { get; private set; } + + /// <summary> + /// Gets or sets a value indicating whether to trust Google to host domains' host-meta documents. + /// </summary> + /// <remarks> + /// This property is just a convenient mechanism for checking or changing the set of + /// trusted host-meta proxies in the <see cref="TrustedHostMetaProxies"/> property. + /// </remarks> + public bool UseGoogleHostedHostMeta { + get { + return this.TrustedHostMetaProxies.Contains(GoogleHostedHostMeta); + } + + set { + if (value != this.UseGoogleHostedHostMeta) { + if (value) { + this.TrustedHostMetaProxies.Add(GoogleHostedHostMeta); + } else { + this.TrustedHostMetaProxies.Remove(GoogleHostedHostMeta); + } + } + } + } + + #region IIdentifierDiscoveryService Members + + /// <summary> + /// Performs discovery on the specified identifier. + /// </summary> + /// <param name="identifier">The identifier to perform discovery on.</param> + /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> + /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> + /// <returns> + /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. + /// </returns> + public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { + abortDiscoveryChain = false; + + // Google Apps are always URIs -- not XRIs. + var uriIdentifier = identifier as UriIdentifier; + if (uriIdentifier == null) { + return Enumerable.Empty<IdentifierDiscoveryResult>(); + } + + var results = new List<IdentifierDiscoveryResult>(); + string signingHost; + using (var response = GetXrdsResponse(uriIdentifier, requestHandler, out signingHost)) { + if (response != null) { + try { + var document = new XrdsDocument(XmlReader.Create(response.ResponseStream)); + ValidateXmlDSig(document, uriIdentifier, response, signingHost); + var xrds = GetXrdElements(document, uriIdentifier.Uri.Host); + + // Look for claimed identifier template URIs for an additional XRDS document. + results.AddRange(GetExternalServices(xrds, uriIdentifier, requestHandler)); + + // If we couldn't find any claimed identifiers, look for OP identifiers. + // Normally this would be the opposite (OP Identifiers take precedence over + // claimed identifiers, but for Google Apps, XRDS' always have OP Identifiers + // mixed in, which the OpenID spec mandate should eclipse Claimed Identifiers, + // which would break positive assertion checks). + if (results.Count == 0) { + results.AddRange(xrds.CreateServiceEndpoints(uriIdentifier, uriIdentifier)); + } + + abortDiscoveryChain = true; + } catch (XmlException ex) { + Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.FinalUri, ex); + } + } + } + + return results; + } + + #endregion + + /// <summary> + /// Gets the XRD elements that have a given CanonicalID. + /// </summary> + /// <param name="document">The XRDS document.</param> + /// <param name="canonicalId">The CanonicalID to match on.</param> + /// <returns>A sequence of XRD elements.</returns> + private static IEnumerable<XrdElement> GetXrdElements(XrdsDocument document, string canonicalId) { + // filter to include only those XRD elements describing the host whose host-meta pointed us to this document. + return document.XrdElements.Where(xrd => string.Equals(xrd.CanonicalID, canonicalId, StringComparison.Ordinal)); + } + + /// <summary> + /// Gets the described-by services in XRD elements. + /// </summary> + /// <param name="xrds">The XRDs to search.</param> + /// <returns>A sequence of services.</returns> + private static IEnumerable<ServiceElement> GetDescribedByServices(IEnumerable<XrdElement> xrds) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null); + + var describedBy = from xrd in xrds + from service in xrd.SearchForServiceTypeUris(p => "http://www.iana.org/assignments/relation/describedby") + select service; + return describedBy; + } + + /// <summary> + /// Gets the services for an identifier that are described by an external XRDS document. + /// </summary> + /// <param name="xrds">The XRD elements to search for described-by services.</param> + /// <param name="identifier">The identifier under discovery.</param> + /// <param name="requestHandler">The request handler.</param> + /// <returns>The discovered services.</returns> + private static IEnumerable<IdentifierDiscoveryResult> GetExternalServices(IEnumerable<XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); + + var results = new List<IdentifierDiscoveryResult>(); + foreach (var serviceElement in GetDescribedByServices(xrds)) { + var templateNode = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver); + var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver); + if (templateNode != null) { + Uri externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri))); + string nextAuthority = nextAuthorityNode != null ? nextAuthorityNode.Value.Trim() : identifier.Uri.Host; + try { + using (var externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation)) { + XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream)); + ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority); + results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier)); + } + } catch (ProtocolException ex) { + Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex); + } catch (XmlException ex) { + Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex); + } + } + } + + return results; + } + + /// <summary> + /// Validates the XML digital signature on an XRDS document. + /// </summary> + /// <param name="document">The XRDS document whose signature should be validated.</param> + /// <param name="identifier">The identifier under discovery.</param> + /// <param name="response">The response.</param> + /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> + /// <exception cref="ProtocolException">Thrown if the XRDS document has an invalid or a missing signature.</exception> + private static void ValidateXmlDSig(XrdsDocument document, UriIdentifier identifier, IncomingWebResponse response, string signingHost) { + Contract.Requires<ArgumentNullException>(document != null); + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(response != null); + + var signatureNode = document.Node.SelectSingleNode("/xrds:XRDS/ds:Signature", document.XmlNamespaceResolver); + ErrorUtilities.VerifyProtocol(signatureNode != null, OpenIdStrings.MissingElement, "Signature"); + var signedInfoNode = signatureNode.SelectSingleNode("ds:SignedInfo", document.XmlNamespaceResolver); + ErrorUtilities.VerifyProtocol(signedInfoNode != null, OpenIdStrings.MissingElement, "SignedInfo"); + ErrorUtilities.VerifyProtocol( + signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null, + "Unrecognized or missing canonicalization method."); + ErrorUtilities.VerifyProtocol( + signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null, + "Unrecognized or missing signature method."); + var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver); + ErrorUtilities.VerifyProtocol(certNodes.Count > 0, OpenIdStrings.MissingElement, "X509Certificate"); + var certs = certNodes.Cast<XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList(); + + // Verify that we trust the signer of the certificates. + // Start by trying to validate just the certificate used to sign the XRDS document, + // since we can do that with partial trust. + Logger.OpenId.Debug("Verifying that we trust the certificate used to sign the discovery document."); + if (!certs[0].Verify()) { + // We couldn't verify just the signing certificate, so try to verify the whole certificate chain. + try { + Logger.OpenId.Debug("Verifying the whole certificate chain."); + VerifyCertChain(certs); + Logger.OpenId.Debug("Certificate chain verified."); + } catch (SecurityException) { + Logger.Yadis.Warn("Signing certificate verification failed and we have insufficient code access security permissions to perform certificate chain validation."); + ErrorUtilities.ThrowProtocol(OpenIdStrings.X509CertificateNotTrusted); + } + } + + // Verify that the certificate is issued to the host on whom we are performing discovery. + string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false); + ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), "X.509 signing certificate issued to {0}, but a certificate for {1} was expected.", hostName, signingHost); + + // Verify the signature itself + byte[] signature = Convert.FromBase64String(response.Headers["Signature"]); + var provider = (RSACryptoServiceProvider)certs.First().PublicKey.Key; + byte[] data = new byte[response.ResponseStream.Length]; + response.ResponseStream.Seek(0, SeekOrigin.Begin); + response.ResponseStream.Read(data, 0, data.Length); + ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), "Invalid XmlDSig signature on XRDS document."); + } + + /// <summary> + /// Verifies the cert chain. + /// </summary> + /// <param name="certs">The certs.</param> + /// <remarks> + /// This must be in a method of its own because there is a LinkDemand on the <see cref="X509Chain.Build"/> + /// method. By being in a method of its own, the caller of this method may catch a + /// <see cref="SecurityException"/> that is thrown if we're not running with full trust and execute + /// an alternative plan. + /// </remarks> + /// <exception cref="ProtocolException">Thrown if the certificate chain is invalid or unverifiable.</exception> + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "By design")] + private static void VerifyCertChain(List<X509Certificate2> certs) { + var chain = new X509Chain(); + foreach (var cert in certs) { + chain.Build(cert); + } + + if (chain.ChainStatus.Length > 0) { + ErrorUtilities.ThrowProtocol( + string.Format( + CultureInfo.CurrentCulture, + OpenIdStrings.X509CertificateNotTrusted + " {0}", + string.Join(", ", chain.ChainStatus.Select(status => status.StatusInformation).ToArray()))); + } + } + + /// <summary> + /// Gets the XRDS HTTP response for a given identifier. + /// </summary> + /// <param name="identifier">The identifier.</param> + /// <param name="requestHandler">The request handler.</param> + /// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param> + /// <returns> + /// A HTTP response carrying an XRDS document. + /// </returns> + /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> + private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + Contract.Requires<ArgumentNullException>(xrdsLocation != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>() != null); + + var request = (HttpWebRequest)WebRequest.Create(xrdsLocation); + request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; + request.Accept = ContentTypes.Xrds; + var options = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None; + var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); + if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) { + Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType); + } + + return response; + } + + /// <summary> + /// Gets the XRDS HTTP response for a given identifier. + /// </summary> + /// <param name="identifier">The identifier.</param> + /// <param name="requestHandler">The request handler.</param> + /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> + /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns> + /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> + private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost); + if (xrdsLocation == null) { + return null; + } + + var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation); + + return response; + } + + /// <summary> + /// Gets the location of the XRDS document that describes a given identifier. + /// </summary> + /// <param name="identifier">The identifier under discovery.</param> + /// <param name="requestHandler">The request handler.</param> + /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> + /// <returns>An absolute URI, or <c>null</c> if one could not be determined.</returns> + private Uri GetXrdsLocation(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + using (var hostMetaResponse = this.GetHostMeta(identifier, requestHandler, out signingHost)) { + if (hostMetaResponse == null) { + return null; + } + + using (var sr = hostMetaResponse.GetResponseReader()) { + string line = sr.ReadLine(); + Match m = HostMetaLink.Match(line); + if (m.Success) { + Uri location = new Uri(m.Groups["location"].Value); + Logger.Yadis.InfoFormat("Found link to XRDS at {0} in host-meta document {1}.", location, hostMetaResponse.FinalUri); + return location; + } + } + + Logger.Yadis.WarnFormat("Could not find link to XRDS in host-meta document: {0}", hostMetaResponse.FinalUri); + return null; + } + } + + /// <summary> + /// Gets the host-meta for a given identifier. + /// </summary> + /// <param name="identifier">The identifier.</param> + /// <param name="requestHandler">The request handler.</param> + /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> + /// <returns> + /// The host-meta response, or <c>null</c> if no host-meta document could be obtained. + /// </returns> + private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) { + var hostMetaLocation = hostMetaProxy.GetProxy(identifier); + var request = (HttpWebRequest)WebRequest.Create(hostMetaLocation); + request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; + var options = DirectWebRequestOptions.AcceptAllHttpResponses; + if (identifier.IsDiscoverySecureEndToEnd) { + options |= DirectWebRequestOptions.RequireSsl; + } + var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); + try { + if (response.Status == HttpStatusCode.OK) { + Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation); + signingHost = hostMetaProxy.GetSigningHost(identifier); + return response; + } else { + Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation); + response.Dispose(); + } + } catch { + response.Dispose(); + throw; + } + } + + signingHost = null; + return null; + } + + /// <summary> + /// Gets the URIs authorized to host host-meta documents on behalf of a given domain. + /// </summary> + /// <param name="identifier">The identifier.</param> + /// <returns>A sequence of URIs that MAY provide the host-meta for a given identifier.</returns> + private IEnumerable<HostMetaProxy> GetHostMetaLocations(UriIdentifier identifier) { + Contract.Requires<ArgumentNullException>(identifier != null); + + // First try the proxies, as they are considered more "secure" than the local + // host-meta for a domain since the domain may be defaced. + IEnumerable<HostMetaProxy> result = this.TrustedHostMetaProxies; + + // Finally, look for the local host-meta. + UriBuilder localHostMetaBuilder = new UriBuilder(); + localHostMetaBuilder.Scheme = identifier.IsDiscoverySecureEndToEnd || identifier.Uri.IsTransportSecure() ? Uri.UriSchemeHttps : Uri.UriSchemeHttp; + localHostMetaBuilder.Host = identifier.Uri.Host; + localHostMetaBuilder.Path = LocalHostMetaPath; + result = result.Concat(new[] { new HostMetaProxy(localHostMetaBuilder.Uri.AbsoluteUri, identifier.Uri.Host) }); + + return result; + } + + /// <summary> + /// A description of a web server that hosts host-meta documents. + /// </summary> + [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "By design")] + public class HostMetaProxy { + /// <summary> + /// Initializes a new instance of the <see cref="HostMetaProxy"/> class. + /// </summary> + /// <param name="proxyFormat">The proxy formatting string.</param> + /// <param name="signingHostFormat">The signing host formatting string.</param> + public HostMetaProxy(string proxyFormat, string signingHostFormat) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(proxyFormat)); + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(signingHostFormat)); + this.ProxyFormat = proxyFormat; + this.SigningHostFormat = signingHostFormat; + } + + /// <summary> + /// Gets the URL of the host-meta proxy. + /// </summary> + /// <value>The absolute proxy URL, which may include {0} to be replaced with the host of the identifier to be discovered.</value> + public string ProxyFormat { get; private set; } + + /// <summary> + /// Gets the formatting string to determine the expected host name on the certificate + /// that is expected to be used to sign the XRDS document. + /// </summary> + /// <value> + /// Either a string literal, or a formatting string where these placeholders may exist: + /// {0} the host on the identifier discovery was originally performed on; + /// {1} the host on this proxy. + /// </value> + public string SigningHostFormat { get; private set; } + + /// <summary> + /// Gets the absolute proxy URI. + /// </summary> + /// <param name="identifier">The identifier being discovered.</param> + /// <returns>The an absolute URI.</returns> + public virtual Uri GetProxy(UriIdentifier identifier) { + Contract.Requires<ArgumentNullException>(identifier != null); + return new Uri(string.Format(CultureInfo.InvariantCulture, this.ProxyFormat, Uri.EscapeDataString(identifier.Uri.Host))); + } + + /// <summary> + /// Gets the signing host URI. + /// </summary> + /// <param name="identifier">The identifier being discovered.</param> + /// <returns>A host name.</returns> + public virtual string GetSigningHost(UriIdentifier identifier) { + Contract.Requires<ArgumentNullException>(identifier != null); + return string.Format(CultureInfo.InvariantCulture, this.SigningHostFormat, identifier.Uri.Host, this.GetProxy(identifier).Host); + } + + /// <summary> + /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>. + /// </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) { + var other = obj as HostMetaProxy; + if (other == null) { + return false; + } + + return this.ProxyFormat == other.ProxyFormat && this.SigningHostFormat == other.SigningHostFormat; + } + + /// <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.ProxyFormat.GetHashCode(); + } + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs new file mode 100644 index 0000000..eb2bf98 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------- +// <copyright file="IIdentifierDiscoveryService.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.RelyingParty; + + /// <summary> + /// A module that provides discovery services for OpenID identifiers. + /// </summary> + [ContractClass(typeof(IIdentifierDiscoveryServiceContract))] + public interface IIdentifierDiscoveryService { + /// <summary> + /// Performs discovery on the specified identifier. + /// </summary> + /// <param name="identifier">The identifier to perform discovery on.</param> + /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> + /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> + /// <returns> + /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By design")] + [Pure] + IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain); + } + + /// <summary> + /// Code contract for the <see cref="IIdentifierDiscoveryService"/> interface. + /// </summary> + [ContractClassFor(typeof(IIdentifierDiscoveryService))] + internal class IIdentifierDiscoveryServiceContract : IIdentifierDiscoveryService { + #region IDiscoveryService Members + + /// <summary> + /// Performs discovery on the specified identifier. + /// </summary> + /// <param name="identifier">The identifier to perform discovery on.</param> + /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> + /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> + /// <returns> + /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. + /// </returns> + IEnumerable<IdentifierDiscoveryResult> IIdentifierDiscoveryService.Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/OpenId/Identifier.cs b/src/DotNetOpenAuth/OpenId/Identifier.cs index 831dfa9..36ec784 100644 --- a/src/DotNetOpenAuth/OpenId/Identifier.cs +++ b/src/DotNetOpenAuth/OpenId/Identifier.cs @@ -39,6 +39,18 @@ namespace DotNetOpenAuth.OpenId { public string OriginalString { get; private set; } /// <summary> + /// Gets the Identifier in the form in which it should be serialized. + /// </summary> + /// <value> + /// For Identifiers that were originally deserialized, this is the exact same + /// string that was deserialized. For Identifiers instantiated in some other way, this is + /// the normalized form of the string used to instantiate the identifier. + /// </value> + internal virtual string SerializedString { + get { return this.IsDeserializedInstance ? this.OriginalString : this.ToString(); } + } + + /// <summary> /// Gets or sets a value indicating whether <see cref="Identifier"/> instances are considered equal /// based solely on their string reprsentations. /// </summary> @@ -59,6 +71,18 @@ namespace DotNetOpenAuth.OpenId { protected internal bool IsDiscoverySecureEndToEnd { get; private set; } /// <summary> + /// Gets a value indicating whether this instance was initialized from + /// deserializing a message. + /// </summary> + /// <remarks> + /// This is interesting because when an Identifier comes from the network, + /// we can't normalize it and then expect signatures to still verify. + /// But if the Identifier is initialized locally, we can and should normalize it + /// before serializing it. + /// </remarks> + protected bool IsDeserializedInstance { get; private set; } + + /// <summary> /// Converts the string representation of an Identifier to its strong type. /// </summary> /// <param name="identifier">The identifier.</param> @@ -118,11 +142,32 @@ namespace DotNetOpenAuth.OpenId { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(identifier)); Contract.Ensures(Contract.Result<Identifier>() != null); + return Parse(identifier, false); + } + + /// <summary> + /// Parses an identifier string and automatically determines + /// whether it is an XRI or URI. + /// </summary> + /// <param name="identifier">Either a URI or XRI identifier.</param> + /// <param name="serializeExactValue">if set to <c>true</c> this Identifier will serialize exactly as given rather than in its normalized form.</param> + /// <returns> + /// An <see cref="Identifier"/> instance for the given value. + /// </returns> + [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Some of these identifiers are not properly formatted to be Uris at this stage.")] + public static Identifier Parse(string identifier, bool serializeExactValue) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(identifier)); + Contract.Ensures(Contract.Result<Identifier>() != null); + + Identifier id; if (XriIdentifier.IsValidXri(identifier)) { - return new XriIdentifier(identifier); + id = new XriIdentifier(identifier); } else { - return new UriIdentifier(identifier); + id = new UriIdentifier(identifier); } + + id.IsDeserializedInstance = serializeExactValue; + return id; } /// <summary> @@ -212,14 +257,17 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Performs discovery on the Identifier. + /// Reparses the specified identifier in order to be assured that the concrete type that + /// implements the identifier is one of the well-known ones. /// </summary> - /// <param name="requestHandler">The web request handler to use for discovery.</param> - /// <returns> - /// An initialized structure containing the discovered provider endpoint information. - /// </returns> - [Pure] - internal abstract IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler); + /// <param name="identifier">The identifier.</param> + /// <returns>Either <see cref="XriIdentifier"/> or <see cref="UriIdentifier"/>.</returns> + internal static Identifier Reparse(Identifier identifier) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Ensures(Contract.Result<Identifier>() != null); + + return Parse(identifier, identifier.IsDeserializedInstance); + } /// <summary> /// Returns an <see cref="Identifier"/> that has no URI fragment. diff --git a/src/DotNetOpenAuth/OpenId/IdentifierContract.cs b/src/DotNetOpenAuth/OpenId/IdentifierContract.cs index 498ae45..4af18e1 100644 --- a/src/DotNetOpenAuth/OpenId/IdentifierContract.cs +++ b/src/DotNetOpenAuth/OpenId/IdentifierContract.cs @@ -24,19 +24,6 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Performs discovery on the Identifier. - /// </summary> - /// <param name="requestHandler">The web request handler to use for discovery.</param> - /// <returns> - /// An initialized structure containing the discovered provider endpoint information. - /// </returns> - internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { - Contract.Requires<ArgumentNullException>(requestHandler != null); - Contract.Ensures(Contract.Result<IEnumerable<ServiceEndpoint>>() != null); - throw new NotImplementedException(); - } - - /// <summary> /// Returns an <see cref="Identifier"/> that has no URI fragment. /// Quietly returns the original <see cref="Identifier"/> if it is not /// a <see cref="UriIdentifier"/> or no fragment exists. diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs b/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs index f8744d0..c851f24 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs +++ b/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs @@ -1,13 +1,15 @@ //----------------------------------------------------------------------- -// <copyright file="ServiceEndpoint.cs" company="Andrew Arnott"> +// <copyright file="IdentifierDiscoveryResult.cs" company="Andrew Arnott"> // Copyright (c) Andrew Arnott. All rights reserved. // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.OpenId.RelyingParty { +namespace DotNetOpenAuth.OpenId { using System; + using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; @@ -15,17 +17,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.RelyingParty; /// <summary> /// Represents a single OP endpoint from discovery on some OpenID Identifier. /// </summary> [DebuggerDisplay("ClaimedIdentifier: {ClaimedIdentifier}, ProviderEndpoint: {ProviderEndpoint}, OpenId: {Protocol.Version}")] - internal class ServiceEndpoint : IXrdsProviderEndpoint { + public sealed class IdentifierDiscoveryResult : IProviderEndpoint { /// <summary> - /// The i-name identifier the user actually typed in - /// or the url identifier with the scheme stripped off. + /// Backing field for the <see cref="Protocol"/> property. /// </summary> - private string friendlyIdentifierForDisplay; + private Protocol protocol; /// <summary> /// Backing field for the <see cref="ClaimedIdentifier"/> property. @@ -33,23 +35,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private Identifier claimedIdentifier; /// <summary> - /// The OpenID protocol version used at the identity Provider. - /// </summary> - private Protocol protocol; - - /// <summary> - /// The @priority given in the XRDS document for this specific OP endpoint. - /// </summary> - private int? uriPriority; - - /// <summary> - /// The @priority given in the XRDS document for this service - /// (which may consist of several endpoints). + /// Backing field for the <see cref="FriendlyIdentifierForDisplay"/> property. /// </summary> - private int? servicePriority; + private string friendlyIdentifierForDisplay; /// <summary> - /// Initializes a new instance of the <see cref="ServiceEndpoint"/> class. + /// Initializes a new instance of the <see cref="IdentifierDiscoveryResult"/> class. /// </summary> /// <param name="providerEndpoint">The provider endpoint.</param> /// <param name="claimedIdentifier">The Claimed Identifier.</param> @@ -57,47 +48,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <param name="providerLocalIdentifier">The Provider Local Identifier.</param> /// <param name="servicePriority">The service priority.</param> /// <param name="uriPriority">The URI priority.</param> - private ServiceEndpoint(ProviderEndpointDescription providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, int? servicePriority, int? uriPriority) { - Contract.Requires<ArgumentNullException>(claimedIdentifier != null); - Contract.Requires<ArgumentNullException>(providerEndpoint != null); - this.ProviderDescription = providerEndpoint; - this.ClaimedIdentifier = claimedIdentifier; - this.UserSuppliedIdentifier = userSuppliedIdentifier; - this.ProviderLocalIdentifier = providerLocalIdentifier ?? claimedIdentifier; - this.servicePriority = servicePriority; - this.uriPriority = uriPriority; - } - - /// <summary> - /// Initializes a new instance of the <see cref="ServiceEndpoint"/> class. - /// </summary> - /// <param name="providerEndpoint">The provider endpoint.</param> - /// <param name="claimedIdentifier">The Claimed Identifier.</param> - /// <param name="userSuppliedIdentifier">The User-supplied Identifier.</param> - /// <param name="providerLocalIdentifier">The Provider Local Identifier.</param> - /// <param name="protocol">The protocol.</param> - /// <remarks> - /// Used for deserializing <see cref="ServiceEndpoint"/> from authentication responses. - /// </remarks> - private ServiceEndpoint(Uri providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, Protocol protocol) { + private IdentifierDiscoveryResult(ProviderEndpointDescription providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, int? servicePriority, int? uriPriority) { Contract.Requires<ArgumentNullException>(providerEndpoint != null); Contract.Requires<ArgumentNullException>(claimedIdentifier != null); - Contract.Requires<ArgumentNullException>(providerLocalIdentifier != null); - Contract.Requires<ArgumentNullException>(protocol != null); - + this.ProviderEndpoint = providerEndpoint.Uri; + this.Capabilities = new ReadOnlyCollection<string>(providerEndpoint.Capabilities); + this.Version = providerEndpoint.Version; this.ClaimedIdentifier = claimedIdentifier; - this.UserSuppliedIdentifier = userSuppliedIdentifier; - this.ProviderDescription = new ProviderEndpointDescription(providerEndpoint, protocol.Version); this.ProviderLocalIdentifier = providerLocalIdentifier ?? claimedIdentifier; - this.protocol = protocol; + this.UserSuppliedIdentifier = userSuppliedIdentifier; + this.ServicePriority = servicePriority; + this.ProviderEndpointPriority = uriPriority; } /// <summary> - /// Gets the URL that the OpenID Provider receives authentication requests at. + /// Gets the detected version of OpenID implemented by the Provider. /// </summary> - Uri IProviderEndpoint.Uri { - get { return this.ProviderDescription.Endpoint; } - } + public Version Version { get; private set; } /// <summary> /// Gets the Identifier that was presented by the end user to the Relying Party, @@ -110,19 +77,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public Identifier UserSuppliedIdentifier { get; private set; } /// <summary> - /// Gets or sets the Identifier that the end user claims to own. + /// Gets the Identifier that the end user claims to control. /// </summary> public Identifier ClaimedIdentifier { get { return this.claimedIdentifier; } - set { + internal set { // Take care to reparse the incoming identifier to make sure it's // not a derived type that will override expected behavior. // Elsewhere in this class, we count on the fact that this property // is either UriIdentifier or XriIdentifier. MockIdentifier messes it up. - this.claimedIdentifier = value != null ? Identifier.Parse(value) : null; + this.claimedIdentifier = value != null ? Identifier.Reparse(value) : null; } } @@ -134,8 +101,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public Identifier ProviderLocalIdentifier { get; private set; } /// <summary> - /// Gets the value for the <see cref="IAuthenticationResponse.FriendlyIdentifierForDisplay"/> property. + /// Gets a more user-friendly (but NON-secure!) string to display to the user as his identifier. /// </summary> + /// <returns>A human-readable, abbreviated (but not secure) identifier the user MAY recognize as his own.</returns> public string FriendlyIdentifierForDisplay { get { if (this.friendlyIdentifierForDisplay == null) { @@ -149,8 +117,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } } else if (uri != null) { if (uri != this.Protocol.ClaimedIdentifierForOPIdentifier) { - string displayUri = uri.Uri.Host + uri.Uri.AbsolutePath; - displayUri = displayUri.TrimEnd('/'); + string displayUri = uri.Uri.Host; + + // We typically want to display the path, because that will often have the username in it. + // As Google Apps for Domains and the like become more popular, a standard /openid path + // will often appear, which is not helpful to identifying the user so we'll avoid including + // that path if it's present. + if (!string.Equals(uri.Uri.AbsolutePath, "/openid", StringComparison.OrdinalIgnoreCase)) { + displayUri += uri.Uri.AbsolutePath.TrimEnd('/'); + } // Multi-byte unicode characters get encoded by the Uri class for transit. // Since this is for display purposes, we want to reverse this and display a readable @@ -162,57 +137,45 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.friendlyIdentifierForDisplay = this.ClaimedIdentifier; } } + return this.friendlyIdentifierForDisplay; } } /// <summary> - /// Gets the list of services available at this OP Endpoint for the - /// claimed Identifier. May be null. + /// Gets the provider endpoint. /// </summary> - public ReadOnlyCollection<string> ProviderSupportedServiceTypeUris { - get { return this.ProviderDescription.Capabilities; } - } + public Uri ProviderEndpoint { get; private set; } /// <summary> - /// Gets the OpenID protocol used by the Provider. + /// Gets the @priority given in the XRDS document for this specific OP endpoint. /// </summary> - public Protocol Protocol { - get { - if (this.protocol == null) { - this.protocol = Protocol.Lookup(this.ProviderDescription.ProtocolVersion); - } - - return this.protocol; - } - } - - #region IXrdsProviderEndpoint Members + public int? ProviderEndpointPriority { get; private set; } /// <summary> - /// Gets the priority associated with this service that may have been given - /// in the XRDS document. + /// Gets the @priority given in the XRDS document for this service + /// (which may consist of several endpoints). /// </summary> - int? IXrdsProviderEndpoint.ServicePriority { - get { return this.servicePriority; } - } + public int? ServicePriority { get; private set; } /// <summary> - /// Gets the priority associated with the service endpoint URL. + /// Gets the collection of service type URIs found in the XRDS document describing this Provider. /// </summary> - int? IXrdsProviderEndpoint.UriPriority { - get { return this.uriPriority; } - } + /// <value>Should never be null, but may be empty.</value> + public ReadOnlyCollection<string> Capabilities { get; private set; } - #endregion + #region IProviderEndpoint Members /// <summary> - /// Gets the detected version of OpenID implemented by the Provider. + /// Gets the URL that the OpenID Provider receives authentication requests at. /// </summary> - Version IProviderEndpoint.Version { - get { return this.ProviderDescription.ProtocolVersion; } + /// <value>This value MUST be an absolute HTTP or HTTPS URL.</value> + Uri IProviderEndpoint.Uri { + get { return this.ProviderEndpoint; } } + #endregion + /// <summary> /// Gets an XRDS sorting routine that uses the XRDS Service/@Priority /// attribute to determine order. @@ -220,7 +183,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// Endpoints lacking any priority value are sorted to the end of the list. /// </remarks> - internal static Comparison<IXrdsProviderEndpoint> EndpointOrder { + internal static Comparison<IdentifierDiscoveryResult> EndpointOrder { get { // Sort first by service type (OpenID 2.0, 1.1, 1.0), // then by Service/@priority, then by Service/Uri/@priority @@ -234,11 +197,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { if (result != 0) { return result; } - if (se1.UriPriority.HasValue && se2.UriPriority.HasValue) { - return se1.UriPriority.Value.CompareTo(se2.UriPriority.Value); - } else if (se1.UriPriority.HasValue) { + if (se1.ProviderEndpointPriority.HasValue && se2.ProviderEndpointPriority.HasValue) { + return se1.ProviderEndpointPriority.Value.CompareTo(se2.ProviderEndpointPriority.Value); + } else if (se1.ProviderEndpointPriority.HasValue) { return -1; - } else if (se2.UriPriority.HasValue) { + } else if (se2.ProviderEndpointPriority.HasValue) { return 1; } else { return 0; @@ -250,11 +213,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return 1; } else { // neither service defines a priority, so base ordering by uri priority. - if (se1.UriPriority.HasValue && se2.UriPriority.HasValue) { - return se1.UriPriority.Value.CompareTo(se2.UriPriority.Value); - } else if (se1.UriPriority.HasValue) { + if (se1.ProviderEndpointPriority.HasValue && se2.ProviderEndpointPriority.HasValue) { + return se1.ProviderEndpointPriority.Value.CompareTo(se2.ProviderEndpointPriority.Value); + } else if (se1.ProviderEndpointPriority.HasValue) { return -1; - } else if (se2.UriPriority.HasValue) { + } else if (se2.ProviderEndpointPriority.HasValue) { return 1; } else { return 0; @@ -266,35 +229,25 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Gets the URL which accepts OpenID Authentication protocol messages. + /// Gets the protocol used by the OpenID Provider. /// </summary> - /// <remarks> - /// Obtained by performing discovery on the User-Supplied Identifier. - /// This value MUST be an absolute HTTP or HTTPS URL. - /// </remarks> - internal Uri ProviderEndpoint { - get { return this.ProviderDescription.Endpoint; } - } + internal Protocol Protocol { + get { + if (this.protocol == null) { + this.protocol = Protocol.Lookup(this.Version); + } - /// <summary> - /// Gets a value indicating whether the <see cref="ProviderEndpoint"/> is using an encrypted channel. - /// </summary> - internal bool IsSecure { - get { return string.Equals(this.ProviderEndpoint.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); } + return this.protocol; + } } /// <summary> - /// Gets the provider description. - /// </summary> - internal ProviderEndpointDescription ProviderDescription { get; private set; } - - /// <summary> /// Implements the operator ==. /// </summary> /// <param name="se1">The first service endpoint.</param> /// <param name="se2">The second service endpoint.</param> /// <returns>The result of the operator.</returns> - public static bool operator ==(ServiceEndpoint se1, ServiceEndpoint se2) { + public static bool operator ==(IdentifierDiscoveryResult se1, IdentifierDiscoveryResult se2) { return se1.EqualsNullSafe(se2); } @@ -304,44 +257,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <param name="se1">The first service endpoint.</param> /// <param name="se2">The second service endpoint.</param> /// <returns>The result of the operator.</returns> - public static bool operator !=(ServiceEndpoint se1, ServiceEndpoint se2) { + public static bool operator !=(IdentifierDiscoveryResult se1, IdentifierDiscoveryResult se2) { return !(se1 == se2); } /// <summary> - /// Checks for the presence of a given Type URI in an XRDS service. - /// </summary> - /// <param name="typeUri">The type URI to check for.</param> - /// <returns> - /// <c>true</c> if the service type uri is present; <c>false</c> otherwise. - /// </returns> - public bool IsTypeUriPresent(string typeUri) { - return this.ProviderDescription.IsExtensionSupported(typeUri); - } - - /// <summary> - /// Determines whether a given extension is supported by this endpoint. - /// </summary> - /// <typeparam name="T">The type of extension to check support for on this endpoint.</typeparam> - /// <returns> - /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>. - /// </returns> - public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() { - return this.ProviderDescription.IsExtensionSupported<T>(); - } - - /// <summary> - /// Determines whether a given extension is supported by this endpoint. - /// </summary> - /// <param name="extensionType">The type of extension to check support for on this endpoint.</param> - /// <returns> - /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>. - /// </returns> - public bool IsExtensionSupported(Type extensionType) { - return this.ProviderDescription.IsExtensionSupported(extensionType); - } - - /// <summary> /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>. /// </summary> /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param> @@ -352,7 +272,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The <paramref name="obj"/> parameter is null. /// </exception> public override bool Equals(object obj) { - ServiceEndpoint other = obj as ServiceEndpoint; + var other = obj as IdentifierDiscoveryResult; if (other == null) { return false; } @@ -388,57 +308,95 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { StringBuilder builder = new StringBuilder(); builder.AppendLine("ClaimedIdentifier: " + this.ClaimedIdentifier); builder.AppendLine("ProviderLocalIdentifier: " + this.ProviderLocalIdentifier); - builder.AppendLine("ProviderEndpoint: " + this.ProviderEndpoint.AbsoluteUri); - builder.AppendLine("OpenID version: " + this.Protocol.Version); + builder.AppendLine("ProviderEndpoint: " + this.ProviderEndpoint); + builder.AppendLine("OpenID version: " + this.Version); builder.AppendLine("Service Type URIs:"); - if (this.ProviderSupportedServiceTypeUris != null) { - foreach (string serviceTypeUri in this.ProviderSupportedServiceTypeUris) { - builder.Append("\t"); - builder.AppendLine(serviceTypeUri); - } - } else { - builder.AppendLine("\t(unavailable)"); + foreach (string serviceTypeUri in this.Capabilities) { + builder.Append("\t"); + builder.AppendLine(serviceTypeUri); } builder.Length -= Environment.NewLine.Length; // trim last newline return builder.ToString(); } /// <summary> - /// Reads previously discovered information about an endpoint - /// from a solicited authentication assertion for validation. + /// Checks whether the OpenId Identifier claims support for a given extension. /// </summary> - /// <param name="reader">The reader from which to deserialize the <see cref="ServiceEndpoint"/>.</param> + /// <typeparam name="T">The extension whose support is being queried.</typeparam> /// <returns> - /// A <see cref="ServiceEndpoint"/> object that has everything - /// except the <see cref="ProviderSupportedServiceTypeUris"/> - /// deserialized. + /// True if support for the extension is advertised. False otherwise. /// </returns> - internal static ServiceEndpoint Deserialize(TextReader reader) { - var claimedIdentifier = Identifier.Parse(reader.ReadLine()); - var providerLocalIdentifier = Identifier.Parse(reader.ReadLine()); - string userSuppliedIdentifier = reader.ReadLine(); - if (userSuppliedIdentifier.Length == 0) { - userSuppliedIdentifier = null; + /// <remarks> + /// Note that a true or false return value is no guarantee of a Provider's + /// support for or lack of support for an extension. The return value is + /// determined by how the authenticating user filled out his/her XRDS document only. + /// The only way to be sure of support for a given extension is to include + /// the extension in the request and see if a response comes back for that extension. + /// </remarks> + [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "No parameter at all.")] + public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() { + T extension = new T(); + return this.IsExtensionSupported(extension); + } + + /// <summary> + /// Checks whether the OpenId Identifier claims support for a given extension. + /// </summary> + /// <param name="extensionType">The extension whose support is being queried.</param> + /// <returns> + /// True if support for the extension is advertised. False otherwise. + /// </returns> + /// <remarks> + /// Note that a true or false return value is no guarantee of a Provider's + /// support for or lack of support for an extension. The return value is + /// determined by how the authenticating user filled out his/her XRDS document only. + /// The only way to be sure of support for a given extension is to include + /// the extension in the request and see if a response comes back for that extension. + /// </remarks> + public bool IsExtensionSupported(Type extensionType) { + var extension = (IOpenIdMessageExtension)Activator.CreateInstance(extensionType); + return this.IsExtensionSupported(extension); + } + + /// <summary> + /// Determines whether a given extension is supported by this endpoint. + /// </summary> + /// <param name="extension">An instance of the extension to check support for.</param> + /// <returns> + /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>. + /// </returns> + public bool IsExtensionSupported(IOpenIdMessageExtension extension) { + Contract.Requires<ArgumentNullException>(extension != null); + + // Consider the primary case. + if (this.IsTypeUriPresent(extension.TypeUri)) { + return true; + } + + // Consider the secondary cases. + if (extension.AdditionalSupportedTypeUris != null) { + if (extension.AdditionalSupportedTypeUris.Any(typeUri => this.IsTypeUriPresent(typeUri))) { + return true; + } } - var providerEndpoint = new Uri(reader.ReadLine()); - var protocol = Protocol.FindBestVersion(p => p.Version, new[] { new Version(reader.ReadLine()) }); - return new ServiceEndpoint(providerEndpoint, claimedIdentifier, userSuppliedIdentifier, providerLocalIdentifier, protocol); + + return false; } /// <summary> - /// Creates a <see cref="ServiceEndpoint"/> instance to represent some OP Identifier. + /// Creates a <see cref="IdentifierDiscoveryResult"/> instance to represent some OP Identifier. /// </summary> /// <param name="providerIdentifier">The provider identifier (actually the user-supplied identifier).</param> /// <param name="providerEndpoint">The provider endpoint.</param> /// <param name="servicePriority">The service priority.</param> /// <param name="uriPriority">The URI priority.</param> - /// <returns>The created <see cref="ServiceEndpoint"/> instance</returns> - internal static ServiceEndpoint CreateForProviderIdentifier(Identifier providerIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) { + /// <returns>The created <see cref="IdentifierDiscoveryResult"/> instance</returns> + internal static IdentifierDiscoveryResult CreateForProviderIdentifier(Identifier providerIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) { Contract.Requires<ArgumentNullException>(providerEndpoint != null); - Protocol protocol = Protocol.Detect(providerEndpoint.Capabilities); + Protocol protocol = Protocol.Lookup(providerEndpoint.Version); - return new ServiceEndpoint( + return new IdentifierDiscoveryResult( providerEndpoint, protocol.ClaimedIdentifierForOPIdentifier, providerIdentifier, @@ -448,20 +406,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Creates a <see cref="ServiceEndpoint"/> instance to represent some Claimed Identifier. + /// Creates a <see cref="IdentifierDiscoveryResult"/> instance to represent some Claimed Identifier. /// </summary> /// <param name="claimedIdentifier">The claimed identifier.</param> /// <param name="providerLocalIdentifier">The provider local identifier.</param> /// <param name="providerEndpoint">The provider endpoint.</param> /// <param name="servicePriority">The service priority.</param> /// <param name="uriPriority">The URI priority.</param> - /// <returns>The created <see cref="ServiceEndpoint"/> instance</returns> - internal static ServiceEndpoint CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) { + /// <returns>The created <see cref="IdentifierDiscoveryResult"/> instance</returns> + internal static IdentifierDiscoveryResult CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) { return CreateForClaimedIdentifier(claimedIdentifier, null, providerLocalIdentifier, providerEndpoint, servicePriority, uriPriority); } /// <summary> - /// Creates a <see cref="ServiceEndpoint"/> instance to represent some Claimed Identifier. + /// Creates a <see cref="IdentifierDiscoveryResult"/> instance to represent some Claimed Identifier. /// </summary> /// <param name="claimedIdentifier">The claimed identifier.</param> /// <param name="userSuppliedIdentifier">The user supplied identifier.</param> @@ -469,24 +427,30 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <param name="providerEndpoint">The provider endpoint.</param> /// <param name="servicePriority">The service priority.</param> /// <param name="uriPriority">The URI priority.</param> - /// <returns>The created <see cref="ServiceEndpoint"/> instance</returns> - internal static ServiceEndpoint CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) { - return new ServiceEndpoint(providerEndpoint, claimedIdentifier, userSuppliedIdentifier, providerLocalIdentifier, servicePriority, uriPriority); + /// <returns>The created <see cref="IdentifierDiscoveryResult"/> instance</returns> + internal static IdentifierDiscoveryResult CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) { + return new IdentifierDiscoveryResult(providerEndpoint, claimedIdentifier, userSuppliedIdentifier, providerLocalIdentifier, servicePriority, uriPriority); } /// <summary> - /// Saves the discovered information about this endpoint - /// for later comparison to validate assertions. + /// Determines whether a given type URI is present on the specified provider endpoint. /// </summary> - /// <param name="writer">The writer to use for serializing out the fields.</param> - internal void Serialize(TextWriter writer) { - writer.WriteLine(this.ClaimedIdentifier); - writer.WriteLine(this.ProviderLocalIdentifier); - writer.WriteLine(this.UserSuppliedIdentifier); - writer.WriteLine(this.ProviderEndpoint); - writer.WriteLine(this.Protocol.Version); - - // No reason to serialize priority. We only needed priority to decide whether to use this endpoint. + /// <param name="typeUri">The type URI.</param> + /// <returns> + /// <c>true</c> if the type URI is present on the specified provider endpoint; otherwise, <c>false</c>. + /// </returns> + internal bool IsTypeUriPresent(string typeUri) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri)); + return this.Capabilities.Contains(typeUri); + } + + /// <summary> + /// Sets the Capabilities property (this method is a test hook.) + /// </summary> + /// <param name="value">The value.</param> + /// <remarks>The publicize.exe tool should work for the unit tests, but for some reason it fails on the build server.</remarks> + internal void SetCapabilitiesForTestHook(ReadOnlyCollection<string> value) { + this.Capabilities = value; } /// <summary> @@ -495,22 +459,39 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="endpoint">The endpoint to prioritize.</param> /// <returns>An arbitary integer, which may be used for sorting against other returned values from this method.</returns> - private static double GetEndpointPrecedenceOrderByServiceType(IXrdsProviderEndpoint endpoint) { + private static double GetEndpointPrecedenceOrderByServiceType(IdentifierDiscoveryResult endpoint) { // The numbers returned from this method only need to compare against other numbers // from this method, which makes them arbitrary but relational to only others here. - if (endpoint.IsTypeUriPresent(Protocol.V20.OPIdentifierServiceTypeURI)) { + if (endpoint.Capabilities.Contains(Protocol.V20.OPIdentifierServiceTypeURI)) { return 0; } - if (endpoint.IsTypeUriPresent(Protocol.V20.ClaimedIdentifierServiceTypeURI)) { + if (endpoint.Capabilities.Contains(Protocol.V20.ClaimedIdentifierServiceTypeURI)) { return 1; } - if (endpoint.IsTypeUriPresent(Protocol.V11.ClaimedIdentifierServiceTypeURI)) { + if (endpoint.Capabilities.Contains(Protocol.V11.ClaimedIdentifierServiceTypeURI)) { return 2; } - if (endpoint.IsTypeUriPresent(Protocol.V10.ClaimedIdentifierServiceTypeURI)) { + if (endpoint.Capabilities.Contains(Protocol.V10.ClaimedIdentifierServiceTypeURI)) { return 3; } return 10; } + +#if CONTRACTS_FULL + /// <summary> + /// Verifies conditions that should be true for any valid state of this object. + /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")] + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] + [ContractInvariantMethod] + private void ObjectInvariant() { + Contract.Invariant(this.ProviderEndpoint != null); + Contract.Invariant(this.ClaimedIdentifier != null); + Contract.Invariant(this.ProviderLocalIdentifier != null); + Contract.Invariant(this.Capabilities != null); + Contract.Invariant(this.Version != null); + Contract.Invariant(this.Protocol != null); + } +#endif } } diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs index 5215022..3fd9424 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs @@ -76,16 +76,16 @@ namespace DotNetOpenAuth.OpenId.Messages { /// 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) { + internal static AssociateRequest Create(SecuritySettings securityRequirements, IProviderEndpoint provider) { Contract.Requires<ArgumentNullException>(securityRequirements != null); Contract.Requires<ArgumentNullException>(provider != null); // Apply our knowledge of the endpoint's transport, OpenID version, and // security requirements to decide the best association. - bool unencryptedAllowed = provider.Endpoint.IsTransportSecure(); + bool unencryptedAllowed = provider.Uri.IsTransportSecure(); bool useDiffieHellman = !unencryptedAllowed; string associationType, sessionType; - if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), true, securityRequirements, useDiffieHellman, out associationType, out sessionType)) { + if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.Version), true, securityRequirements, useDiffieHellman, out associationType, out sessionType)) { // There are no associations that meet all requirements. Logger.OpenId.Warn("Security requirements and protocol combination knock out all possible association types. Dumb mode forced."); return null; @@ -106,19 +106,19 @@ namespace DotNetOpenAuth.OpenId.Messages { /// 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, string associationType, string sessionType) { + internal static AssociateRequest Create(SecuritySettings securityRequirements, IProviderEndpoint provider, string associationType, string sessionType) { Contract.Requires<ArgumentNullException>(securityRequirements != null); Contract.Requires<ArgumentNullException>(provider != null); Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(associationType)); Contract.Requires<ArgumentNullException>(sessionType != null); - bool unencryptedAllowed = provider.Endpoint.IsTransportSecure(); + bool unencryptedAllowed = provider.Uri.IsTransportSecure(); if (unencryptedAllowed) { - var associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint); + var associateRequest = new AssociateUnencryptedRequest(provider.Version, provider.Uri); associateRequest.AssociationType = associationType; return associateRequest; } else { - var associateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint); + var associateRequest = new AssociateDiffieHellmanRequest(provider.Version, provider.Uri); associateRequest.AssociationType = associationType; associateRequest.SessionType = sessionType; associateRequest.InitializeRequest(); diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs index 5306c54..db69d3d 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.OpenId.Messages { // Copy all message parts from the id_res message into this one, // except for the openid.mode parameter. - MessageDictionary checkPayload = channel.MessageDescriptions.GetAccessor(message); + MessageDictionary checkPayload = channel.MessageDescriptions.GetAccessor(message, true); MessageDictionary thisPayload = channel.MessageDescriptions.GetAccessor(this); foreach (var pair in checkPayload) { if (!string.Equals(pair.Key, this.Protocol.openid.mode)) { diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs index 61825e8..f1bb5ac 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs @@ -47,7 +47,7 @@ namespace DotNetOpenAuth.OpenId.Messages { // really doesn't exist. OpenID 2.0 section 11.4.2.2. IndirectSignedResponse signedResponse = new IndirectSignedResponse(request, provider.Channel); string invalidateHandle = ((ITamperResistantOpenIdMessage)signedResponse).InvalidateHandle; - if (invalidateHandle != null && provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, invalidateHandle) == null) { + if (!string.IsNullOrEmpty(invalidateHandle) && provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, invalidateHandle) == null) { this.InvalidateHandle = invalidateHandle; } } @@ -70,8 +70,10 @@ namespace DotNetOpenAuth.OpenId.Messages { /// <para>This two-step process for invalidating associations is necessary /// to prevent an attacker from invalidating an association at will by /// adding "invalidate_handle" parameters to an authentication response.</para> + /// <para>For OpenID 1.1, we allow this to be present but empty to put up with poor implementations such as Blogger.</para> /// </remarks> - [MessagePart("invalidate_handle", IsRequired = false, AllowEmpty = false)] + [MessagePart("invalidate_handle", IsRequired = false, AllowEmpty = true, MaxVersion = "1.1")] + [MessagePart("invalidate_handle", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")] internal string InvalidateHandle { get; set; } } } diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs index 2f02974..fff4cf6 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs @@ -207,7 +207,11 @@ namespace DotNetOpenAuth.OpenId.Messages { /// Gets or sets the association handle that the Provider wants the Relying Party to not use any more. /// </summary> /// <value>If the Relying Party sent an invalid association handle with the request, it SHOULD be included here.</value> - [MessagePart("openid.invalidate_handle", IsRequired = false, AllowEmpty = false)] + /// <remarks> + /// For OpenID 1.1, we allow this to be present but empty to put up with poor implementations such as Blogger. + /// </remarks> + [MessagePart("openid.invalidate_handle", IsRequired = false, AllowEmpty = true, MaxVersion = "1.1")] + [MessagePart("openid.invalidate_handle", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")] string ITamperResistantOpenIdMessage.InvalidateHandle { get; set; } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs b/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs index 636df67..1a6e7e9 100644 --- a/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs @@ -70,17 +70,6 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Performs discovery on the Identifier. - /// </summary> - /// <param name="requestHandler">The web request handler to use for discovery.</param> - /// <returns> - /// An initialized structure containing the discovered provider endpoint information. - /// </returns> - internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { - return Enumerable.Empty<ServiceEndpoint>(); - } - - /// <summary> /// Returns an <see cref="Identifier"/> that has no URI fragment. /// Quietly returns the original <see cref="Identifier"/> if it is not /// a <see cref="UriIdentifier"/> or no fragment exists. diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs index 33a16f8..43283ac 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4927 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.OpenId { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class OpenIdStrings { @@ -196,6 +196,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to This OpenID exploits features that this relying party cannot reliably verify. Please try logging in with a human-readable OpenID or from a different OpenID Provider.. + /// </summary> + internal static string ClaimedIdentifierDefiesDotNetNormalization { + get { + return ResourceManager.GetString("ClaimedIdentifierDefiesDotNetNormalization", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The ClaimedIdentifier property must be set first.. /// </summary> internal static string ClaimedIdentifierMustBeSetFirst { @@ -416,6 +425,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to Missing {0} element.. + /// </summary> + internal static string MissingElement { + get { + return ResourceManager.GetString("MissingElement", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to No recognized association type matches the requested length of {0}.. /// </summary> internal static string NoAssociationTypeFoundByLength { @@ -722,6 +740,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to The X.509 certificate used to sign this document is not trusted.. + /// </summary> + internal static string X509CertificateNotTrusted { + get { + return ResourceManager.GetString("X509CertificateNotTrusted", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to XRI support has been disabled at this site.. /// </summary> internal static string XriResolutionDisabled { diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx index c5f506d..fab03a9 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx @@ -346,4 +346,13 @@ Discovered endpoint info: <data name="PropertyNotSet" xml:space="preserve"> <value>The {0} property must be set first.</value> </data> + <data name="X509CertificateNotTrusted" xml:space="preserve"> + <value>The X.509 certificate used to sign this document is not trusted.</value> + </data> + <data name="ClaimedIdentifierDefiesDotNetNormalization" xml:space="preserve"> + <value>This OpenID exploits features that this relying party cannot reliably verify. Please try logging in with a human-readable OpenID or from a different OpenID Provider.</value> + </data> + <data name="MissingElement" xml:space="preserve"> + <value>Missing {0} element.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs index 3e75e61..04eb23c 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs @@ -16,13 +16,14 @@ namespace DotNetOpenAuth.OpenId { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Extensions; + using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; /// <summary> /// A set of utilities especially useful to OpenID. /// </summary> - internal static class OpenIdUtilities { + public static class OpenIdUtilities { /// <summary> /// The prefix to designate this library's proprietary parameters added to the protocol. /// </summary> diff --git a/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs b/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs index 664a127..00468ed 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs @@ -27,6 +27,9 @@ namespace DotNetOpenAuth.OpenId { /// or for Provider's to perform RP discovery/verification as part of authentication. /// </remarks> internal static IEnumerable<RelyingPartyEndpointDescription> FindRelyingPartyReceivingEndpoints(this XrdsDocument xrds) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Ensures(Contract.Result<IEnumerable<RelyingPartyEndpointDescription>>() != null); + return from service in xrds.FindReturnToServices() from uri in service.UriElements select new RelyingPartyEndpointDescription(uri.Uri, service.TypeElementUris); @@ -39,6 +42,9 @@ namespace DotNetOpenAuth.OpenId { /// <param name="xrds">The XrdsDocument to search.</param> /// <returns>A sequence of the icon URLs in preferred order.</returns> internal static IEnumerable<Uri> FindRelyingPartyIcons(this XrdsDocument xrds) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Ensures(Contract.Result<IEnumerable<Uri>>() != null); + return from xrd in xrds.XrdElements from service in xrd.OpenIdRelyingPartyIcons from uri in service.UriElements @@ -55,15 +61,16 @@ namespace DotNetOpenAuth.OpenId { /// <returns> /// A sequence of OpenID Providers that can assert ownership of the <paramref name="claimedIdentifier"/>. /// </returns> - internal static IEnumerable<ServiceEndpoint> CreateServiceEndpoints(this XrdsDocument xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) { - var endpoints = new List<ServiceEndpoint>(); + internal static IEnumerable<IdentifierDiscoveryResult> CreateServiceEndpoints(this IEnumerable<XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Requires<ArgumentNullException>(claimedIdentifier != null); + Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null); + Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); + + var endpoints = new List<IdentifierDiscoveryResult>(); endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier)); + endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier)); - // If any OP Identifier service elements were found, we must not proceed - // to return any Claimed Identifier services. - if (endpoints.Count == 0) { - endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier)); - } Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count); Logger.Yadis.Debug(endpoints.ToStringDeferred(true)); return endpoints; @@ -76,18 +83,14 @@ namespace DotNetOpenAuth.OpenId { /// <param name="xrds">The XrdsDocument instance to use in this process.</param> /// <param name="userSuppliedIdentifier">The user-supplied i-name that was used to discover this XRDS document.</param> /// <returns>A sequence of OpenID Providers that can assert ownership of the canonical ID given in this document.</returns> - internal static IEnumerable<ServiceEndpoint> CreateServiceEndpoints(this XrdsDocument xrds, XriIdentifier userSuppliedIdentifier) { + internal static IEnumerable<IdentifierDiscoveryResult> CreateServiceEndpoints(this IEnumerable<XrdElement> xrds, XriIdentifier userSuppliedIdentifier) { Contract.Requires<ArgumentNullException>(xrds != null); Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null); - Contract.Ensures(Contract.Result<IEnumerable<ServiceEndpoint>>() != null); - var endpoints = new List<ServiceEndpoint>(); - endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier)); + Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); - // If any OP Identifier service elements were found, we must not proceed - // to return any Claimed Identifier services. - if (endpoints.Count == 0) { - endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(userSuppliedIdentifier)); - } + var endpoints = new List<IdentifierDiscoveryResult>(); + endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier)); + endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(userSuppliedIdentifier)); Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count); Logger.Yadis.Debug(endpoints.ToStringDeferred(true)); return endpoints; @@ -99,15 +102,15 @@ namespace DotNetOpenAuth.OpenId { /// <param name="xrds">The XrdsDocument instance to use in this process.</param> /// <param name="opIdentifier">The OP Identifier entered (and resolved) by the user. Essentially the user-supplied identifier.</param> /// <returns>A sequence of the providers that can offer directed identity services.</returns> - private static IEnumerable<ServiceEndpoint> GenerateOPIdentifierServiceEndpoints(this XrdsDocument xrds, Identifier opIdentifier) { + private static IEnumerable<IdentifierDiscoveryResult> GenerateOPIdentifierServiceEndpoints(this IEnumerable<XrdElement> xrds, Identifier opIdentifier) { Contract.Requires<ArgumentNullException>(xrds != null); Contract.Requires<ArgumentNullException>(opIdentifier != null); - Contract.Ensures(Contract.Result<IEnumerable<ServiceEndpoint>>() != null); + Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); return from service in xrds.FindOPIdentifierServices() from uri in service.UriElements let protocol = Protocol.FindBestVersion(p => p.OPIdentifierServiceTypeURI, service.TypeElementUris) let providerDescription = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris) - select ServiceEndpoint.CreateForProviderIdentifier(opIdentifier, providerDescription, service.Priority, uri.Priority); + select IdentifierDiscoveryResult.CreateForProviderIdentifier(opIdentifier, providerDescription, service.Priority, uri.Priority); } /// <summary> @@ -120,12 +123,16 @@ namespace DotNetOpenAuth.OpenId { /// <returns> /// A sequence of the providers that can assert ownership of the given identifier. /// </returns> - private static IEnumerable<ServiceEndpoint> GenerateClaimedIdentifierServiceEndpoints(this XrdsDocument xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) { + private static IEnumerable<IdentifierDiscoveryResult> GenerateClaimedIdentifierServiceEndpoints(this IEnumerable<XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Requires<ArgumentNullException>(claimedIdentifier != null); + Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); + return from service in xrds.FindClaimedIdentifierServices() from uri in service.UriElements where uri.Uri != null let providerEndpoint = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris) - select ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority); + select IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority); } /// <summary> @@ -135,7 +142,12 @@ namespace DotNetOpenAuth.OpenId { /// <param name="xrds">The XrdsDocument instance to use in this process.</param> /// <param name="userSuppliedIdentifier">The i-name supplied by the user.</param> /// <returns>A sequence of the providers that can assert ownership of the given identifier.</returns> - private static IEnumerable<ServiceEndpoint> GenerateClaimedIdentifierServiceEndpoints(this XrdsDocument xrds, XriIdentifier userSuppliedIdentifier) { + private static IEnumerable<IdentifierDiscoveryResult> GenerateClaimedIdentifierServiceEndpoints(this IEnumerable<XrdElement> xrds, XriIdentifier userSuppliedIdentifier) { + // Cannot use code contracts because this method uses yield return. + ////Contract.Requires<ArgumentNullException>(xrds != null); + ////Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); + ErrorUtilities.VerifyArgumentNotNull(xrds, "xrds"); + foreach (var service in xrds.FindClaimedIdentifierServices()) { foreach (var uri in service.UriElements) { // spec section 7.3.2.3 on Claimed Id -> CanonicalID substitution @@ -148,7 +160,7 @@ namespace DotNetOpenAuth.OpenId { // In the case of XRI names, the ClaimedId is actually the CanonicalID. var claimedIdentifier = new XriIdentifier(service.Xrd.CanonicalID); var providerEndpoint = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris); - yield return ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority); + yield return IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority); } } } @@ -158,8 +170,11 @@ namespace DotNetOpenAuth.OpenId { /// </summary> /// <param name="xrds">The XrdsDocument instance to use in this process.</param> /// <returns>A sequence of service elements.</returns> - private static IEnumerable<ServiceElement> FindOPIdentifierServices(this XrdsDocument xrds) { - return from xrd in xrds.XrdElements + private static IEnumerable<ServiceElement> FindOPIdentifierServices(this IEnumerable<XrdElement> xrds) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null); + + return from xrd in xrds from service in xrd.OpenIdProviderIdentifierServices select service; } @@ -170,8 +185,11 @@ namespace DotNetOpenAuth.OpenId { /// </summary> /// <param name="xrds">The XrdsDocument instance to use in this process.</param> /// <returns>A sequence of the services offered.</returns> - private static IEnumerable<ServiceElement> FindClaimedIdentifierServices(this XrdsDocument xrds) { - return from xrd in xrds.XrdElements + private static IEnumerable<ServiceElement> FindClaimedIdentifierServices(this IEnumerable<XrdElement> xrds) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null); + + return from xrd in xrds from service in xrd.OpenIdClaimedIdentifierServices select service; } @@ -183,6 +201,9 @@ namespace DotNetOpenAuth.OpenId { /// <param name="xrds">The XrdsDocument instance to use in this process.</param> /// <returns>A sequence of service elements.</returns> private static IEnumerable<ServiceElement> FindReturnToServices(this XrdsDocument xrds) { + Contract.Requires<ArgumentNullException>(xrds != null); + Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null); + return from xrd in xrds.XrdElements from service in xrd.OpenIdRelyingPartyReturnToServices select service; diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs index 827ca6c..10c69a3 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs @@ -20,10 +20,12 @@ namespace DotNetOpenAuth.OpenId.Provider { using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Messages; + using RP = DotNetOpenAuth.OpenId.RelyingParty; /// <summary> /// Offers services for a web page that is acting as an OpenID identity server. /// </summary> + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "By design")] [ContractVerification(true)] public sealed class OpenIdProvider : IDisposable { /// <summary> @@ -43,6 +45,12 @@ namespace DotNetOpenAuth.OpenId.Provider { private ProviderSecuritySettings securitySettings; /// <summary> + /// The relying party used to perform discovery on identifiers being sent in + /// unsolicited positive assertions. + /// </summary> + private RP.OpenIdRelyingParty relyingParty; + + /// <summary> /// Initializes a new instance of the <see cref="OpenIdProvider"/> class. /// </summary> public OpenIdProvider() @@ -160,6 +168,13 @@ namespace DotNetOpenAuth.OpenId.Provider { } /// <summary> + /// Gets the list of services that can perform discovery on identifiers given to this relying party. + /// </summary> + internal IList<IIdentifierDiscoveryService> DiscoveryServices { + get { return this.RelyingParty.DiscoveryServices; } + } + + /// <summary> /// Gets the association store. /// </summary> internal IAssociationStore<AssociationRelyingPartyType> AssociationStore { get; private set; } @@ -173,6 +188,25 @@ namespace DotNetOpenAuth.OpenId.Provider { } /// <summary> + /// Gets the relying party used for discovery of identifiers sent in unsolicited assertions. + /// </summary> + private RP.OpenIdRelyingParty RelyingParty { + get { + if (this.relyingParty == null) { + lock (this) { + if (this.relyingParty == null) { + // we just need an RP that's capable of discovery, so stateless mode is fine. + this.relyingParty = new RP.OpenIdRelyingParty(null); + } + } + } + + this.relyingParty.Channel.WebRequestHandler = this.WebRequestHandler; + return this.relyingParty; + } + } + + /// <summary> /// Gets the incoming OpenID request if there is one, or null if none was detected. /// </summary> /// <returns>The request that the hosting Provider should possibly process and then transmit the response for.</returns> @@ -314,7 +348,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// web site and log him/her in immediately in one uninterrupted step. /// </summary> /// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param> - /// <param name="relyingParty">The URL of the Relying Party web site. + /// <param name="relyingPartyRealm">The URL of the Relying Party web site. /// This will typically be the home page, but may be a longer URL if /// that Relying Party considers the scope of its realm to be more specific. /// The URL provided here must allow discovery of the Relying Party's @@ -323,15 +357,15 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <param name="localIdentifier">The Identifier you know your user by internally. This will typically /// be the same as <paramref name="claimedIdentifier"/>.</param> /// <param name="extensions">The extensions.</param> - public void SendUnsolicitedAssertion(Uri providerEndpoint, Realm relyingParty, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) { + public void SendUnsolicitedAssertion(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) { Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); Contract.Requires<ArgumentNullException>(providerEndpoint != null); Contract.Requires<ArgumentException>(providerEndpoint.IsAbsoluteUri); - Contract.Requires<ArgumentNullException>(relyingParty != null); + Contract.Requires<ArgumentNullException>(relyingPartyRealm != null); Contract.Requires<ArgumentNullException>(claimedIdentifier != null); Contract.Requires<ArgumentNullException>(localIdentifier != null); - this.PrepareUnsolicitedAssertion(providerEndpoint, relyingParty, claimedIdentifier, localIdentifier, extensions).Send(); + this.PrepareUnsolicitedAssertion(providerEndpoint, relyingPartyRealm, claimedIdentifier, localIdentifier, extensions).Send(); } /// <summary> @@ -340,7 +374,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// web site and log him/her in immediately in one uninterrupted step. /// </summary> /// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param> - /// <param name="relyingParty">The URL of the Relying Party web site. + /// <param name="relyingPartyRealm">The URL of the Relying Party web site. /// This will typically be the home page, but may be a longer URL if /// that Relying Party considers the scope of its realm to be more specific. /// The URL provided here must allow discovery of the Relying Party's @@ -353,10 +387,10 @@ namespace DotNetOpenAuth.OpenId.Provider { /// A <see cref="OutgoingWebResponse"/> object describing the HTTP response to send /// the user agent to allow the redirect with assertion to happen. /// </returns> - public OutgoingWebResponse PrepareUnsolicitedAssertion(Uri providerEndpoint, Realm relyingParty, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) { + public OutgoingWebResponse PrepareUnsolicitedAssertion(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) { Contract.Requires<ArgumentNullException>(providerEndpoint != null); Contract.Requires<ArgumentException>(providerEndpoint.IsAbsoluteUri); - Contract.Requires<ArgumentNullException>(relyingParty != null); + Contract.Requires<ArgumentNullException>(relyingPartyRealm != null); Contract.Requires<ArgumentNullException>(claimedIdentifier != null); Contract.Requires<ArgumentNullException>(localIdentifier != null); Contract.Requires<InvalidOperationException>(this.Channel.WebRequestHandler != null); @@ -366,8 +400,8 @@ namespace DotNetOpenAuth.OpenId.Provider { // do due diligence by performing our own discovery on the claimed identifier // and make sure that it is tied to this OP and OP local identifier. if (this.SecuritySettings.UnsolicitedAssertionVerification != ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.NeverVerify) { - var serviceEndpoint = DotNetOpenAuth.OpenId.RelyingParty.ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null); - var discoveredEndpoints = claimedIdentifier.Discover(this.WebRequestHandler); + var serviceEndpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null); + var discoveredEndpoints = this.RelyingParty.Discover(claimedIdentifier); if (!discoveredEndpoints.Contains(serviceEndpoint)) { Logger.OpenId.WarnFormat( "Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}", @@ -385,11 +419,11 @@ namespace DotNetOpenAuth.OpenId.Provider { Logger.OpenId.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier); RelyingPartyEndpointDescription returnToEndpoint = null; - var returnToEndpoints = relyingParty.DiscoverReturnToEndpoints(this.WebRequestHandler, true); + var returnToEndpoints = relyingPartyRealm.DiscoverReturnToEndpoints(this.WebRequestHandler, true); if (returnToEndpoints != null) { returnToEndpoint = returnToEndpoints.FirstOrDefault(); } - ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingParty); + ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingPartyRealm); var positiveAssertion = new PositiveAssertionResponse(returnToEndpoint) { ProviderEndpoint = providerEndpoint, @@ -428,6 +462,10 @@ namespace DotNetOpenAuth.OpenId.Provider { if (channel != null) { channel.Dispose(); } + + if (this.relyingParty != null) { + this.relyingParty.Dispose(); + } } } diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs index 445978e..e792a81 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs @@ -98,11 +98,15 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </remarks> public static IAuthenticationRequest PendingAuthenticationRequest { get { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); Contract.Ensures(Contract.Result<IAuthenticationRequest>() == null || PendingRequest != null); return HttpContext.Current.Session[PendingRequestKey] as IAuthenticationRequest; } set { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); HttpContext.Current.Session[PendingRequestKey] = value; } } @@ -118,11 +122,15 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </remarks> public static IAnonymousRequest PendingAnonymousRequest { get { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); Contract.Ensures(Contract.Result<IAnonymousRequest>() == null || PendingRequest != null); return HttpContext.Current.Session[PendingRequestKey] as IAnonymousRequest; } set { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); HttpContext.Current.Session[PendingRequestKey] = value; } } @@ -137,8 +145,17 @@ namespace DotNetOpenAuth.OpenId.Provider { /// before responding to the relying party's request. /// </remarks> public static IHostProcessedRequest PendingRequest { - get { return HttpContext.Current.Session[PendingRequestKey] as IHostProcessedRequest; } - set { HttpContext.Current.Session[PendingRequestKey] = value; } + get { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + return HttpContext.Current.Session[PendingRequestKey] as IHostProcessedRequest; + } + + set { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + HttpContext.Current.Session[PendingRequestKey] = value; + } } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs index 4cfbac5..6514ffd 100644 --- a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs +++ b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId { using System; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using DotNetOpenAuth.Messaging; @@ -21,7 +22,7 @@ namespace DotNetOpenAuth.OpenId { /// This is an immutable type. /// </remarks> [Serializable] - internal class ProviderEndpointDescription : IProviderEndpoint { + internal sealed class ProviderEndpointDescription : IProviderEndpoint { /// <summary> /// Initializes a new instance of the <see cref="ProviderEndpointDescription"/> class. /// </summary> @@ -31,8 +32,9 @@ namespace DotNetOpenAuth.OpenId { Contract.Requires<ArgumentNullException>(providerEndpoint != null); Contract.Requires<ArgumentNullException>(openIdVersion != null); - this.Endpoint = providerEndpoint; - this.ProtocolVersion = openIdVersion; + this.Uri = providerEndpoint; + this.Version = openIdVersion; + this.Capabilities = new ReadOnlyCollection<string>(EmptyList<string>.Instance); } /// <summary> @@ -44,42 +46,24 @@ namespace DotNetOpenAuth.OpenId { Contract.Requires<ArgumentNullException>(providerEndpoint != null); Contract.Requires<ArgumentNullException>(serviceTypeURIs != null); - this.Endpoint = providerEndpoint; + this.Uri = providerEndpoint; 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) { - this.ProtocolVersion = opIdentifierProtocol.Version; + this.Version = opIdentifierProtocol.Version; } else if (claimedIdentifierProviderVersion != null) { - this.ProtocolVersion = claimedIdentifierProviderVersion.Version; + this.Version = claimedIdentifierProviderVersion.Version; + } else { + ErrorUtilities.ThrowProtocol(OpenIdStrings.ProviderVersionUnrecognized, this.Uri); } - - ErrorUtilities.VerifyProtocol(this.ProtocolVersion != null, OpenIdStrings.ProviderVersionUnrecognized, this.Endpoint); } - #region IProviderEndpoint Properties - - /// <summary> - /// Gets the detected version of OpenID implemented by the Provider. - /// </summary> - Version IProviderEndpoint.Version { - get { return this.ProtocolVersion; } - } - - /// <summary> - /// Gets the URL that the OpenID Provider receives authentication requests at. - /// </summary> - Uri IProviderEndpoint.Uri { - get { return this.Endpoint; } - } - - #endregion - /// <summary> /// Gets the URL that the OpenID Provider listens for incoming OpenID messages on. /// </summary> - internal Uri Endpoint { get; private set; } + public Uri Uri { get; private set; } /// <summary> /// Gets the OpenID protocol version this endpoint supports. @@ -88,14 +72,14 @@ namespace DotNetOpenAuth.OpenId { /// If an endpoint supports multiple versions, each version must be represented /// by its own <see cref="ProviderEndpointDescription"/> object. /// </remarks> - internal Version ProtocolVersion { get; private set; } + public Version Version { get; private set; } /// <summary> /// Gets the collection of service type URIs found in the XRDS document describing this Provider. /// </summary> internal ReadOnlyCollection<string> Capabilities { get; private set; } - #region IProviderEndpoint Methods + #region IProviderEndpoint Members /// <summary> /// Checks whether the OpenId Identifier claims support for a given extension. @@ -111,10 +95,8 @@ namespace DotNetOpenAuth.OpenId { /// The only way to be sure of support for a given extension is to include /// the extension in the request and see if a response comes back for that extension. /// </remarks> - public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() { - ErrorUtilities.VerifyOperation(this.Capabilities != null, OpenIdStrings.ExtensionLookupSupportUnavailable); - T extension = new T(); - return this.IsExtensionSupported(extension); + bool IProviderEndpoint.IsExtensionSupported<T>() { + throw new NotImplementedException(); } /// <summary> @@ -131,52 +113,22 @@ namespace DotNetOpenAuth.OpenId { /// The only way to be sure of support for a given extension is to include /// the extension in the request and see if a response comes back for that extension. /// </remarks> - public bool IsExtensionSupported(Type extensionType) { - ErrorUtilities.VerifyOperation(this.Capabilities != null, OpenIdStrings.ExtensionLookupSupportUnavailable); - var extension = (IOpenIdMessageExtension)Activator.CreateInstance(extensionType); - return this.IsExtensionSupported(extension); + bool IProviderEndpoint.IsExtensionSupported(Type extensionType) { + throw new NotImplementedException(); } #endregion +#if CONTRACTS_FULL /// <summary> - /// Determines whether some extension is supported by the Provider. + /// Verifies conditions that should be true for any valid state of this object. /// </summary> - /// <param name="extensionUri">The extension URI.</param> - /// <returns> - /// <c>true</c> if the extension is supported; otherwise, <c>false</c>. - /// </returns> - protected internal bool IsExtensionSupported(string extensionUri) { - Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(extensionUri)); - Contract.Requires<InvalidOperationException>(this.Capabilities != null, OpenIdStrings.ExtensionLookupSupportUnavailable); - return this.Capabilities.Contains(extensionUri); - } - - /// <summary> - /// Determines whether a given extension is supported by this endpoint. - /// </summary> - /// <param name="extension">An instance of the extension to check support for.</param> - /// <returns> - /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>. - /// </returns> - protected internal bool IsExtensionSupported(IOpenIdMessageExtension extension) { - Contract.Requires<ArgumentNullException>(extension != null); - Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(extension.TypeUri)); - Contract.Requires<InvalidOperationException>(this.Capabilities != null, OpenIdStrings.ExtensionLookupSupportUnavailable); - - // Consider the primary case. - if (this.IsExtensionSupported(extension.TypeUri)) { - return true; - } - - // Consider the secondary cases. - if (extension.AdditionalSupportedTypeUris != null) { - if (extension.AdditionalSupportedTypeUris.Any(typeUri => this.IsExtensionSupported(typeUri))) { - return true; - } - } - - return false; + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")] + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] + [ContractInvariantMethod] + private void ObjectInvariant() { + Contract.Invariant(this.Capabilities != null); } +#endif } } diff --git a/src/DotNetOpenAuth/OpenId/Realm.cs b/src/DotNetOpenAuth/OpenId/Realm.cs index 600e6c0..98e3598 100644 --- a/src/DotNetOpenAuth/OpenId/Realm.cs +++ b/src/DotNetOpenAuth/OpenId/Realm.cs @@ -13,6 +13,7 @@ namespace DotNetOpenAuth.OpenId { using System.Globalization; using System.Linq; using System.Text.RegularExpressions; + using System.Web; using System.Xml; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Xrds; @@ -98,6 +99,40 @@ namespace DotNetOpenAuth.OpenId { : this(SafeUriBuilderToString(realmUriBuilder)) { } /// <summary> + /// Gets the suggested realm to use for the calling web application. + /// </summary> + /// <value>A realm that matches this applications root URL.</value> + /// <remarks> + /// <para>For most circumstances the Realm generated by this property is sufficient. + /// However a wildcard Realm, such as "http://*.microsoft.com/" may at times be more + /// desirable than "http://www.microsoft.com/" in order to allow identifier + /// correlation across related web sites for directed identity Providers.</para> + /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> + /// </remarks> + public static Realm AutoDetect { + get { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); + Contract.Ensures(Contract.Result<Realm>() != null); + + HttpRequestInfo requestInfo = new HttpRequestInfo(HttpContext.Current.Request); + UriBuilder realmUrl = new UriBuilder(requestInfo.UrlBeforeRewriting); + realmUrl.Path = HttpContext.Current.Request.ApplicationPath; + realmUrl.Query = null; + realmUrl.Fragment = null; + + // For RP discovery, the realm url MUST NOT redirect. To prevent this for + // virtual directory hosted apps, we need to make sure that the realm path ends + // in a slash (since our calculation above guarantees it doesn't end in a specific + // page like default.aspx). + if (!realmUrl.Path.EndsWith("/", StringComparison.Ordinal)) { + realmUrl.Path += "/"; + } + + return realmUrl.Uri; + } + } + + /// <summary> /// Gets a value indicating whether a '*.' prefix to the hostname is /// used in the realm to allow subdomains or hosts to be added to the URL. /// </summary> @@ -145,6 +180,14 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Gets the original string. + /// </summary> + /// <value>The original string.</value> + internal string OriginalString { + get { return this.uri.OriginalString; } + } + + /// <summary> /// Gets the realm URL. If the realm includes a wildcard, it is not included here. /// </summary> internal Uri NoWildcardUri { @@ -219,6 +262,7 @@ namespace DotNetOpenAuth.OpenId { [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Not all Realms are valid URLs.")] [DebuggerStepThrough] public static implicit operator Realm(string uri) { + Contract.Ensures((Contract.Result<Realm>() != null) == (uri != null)); return uri != null ? new Realm(uri) : null; } @@ -229,6 +273,7 @@ namespace DotNetOpenAuth.OpenId { /// <returns>The result of the conversion.</returns> [DebuggerStepThrough] public static implicit operator Realm(Uri uri) { + Contract.Ensures((Contract.Result<Realm>() != null) == (uri != null)); return uri != null ? new Realm(uri) : null; } @@ -239,6 +284,7 @@ namespace DotNetOpenAuth.OpenId { /// <returns>The result of the conversion.</returns> [DebuggerStepThrough] public static implicit operator string(Realm realm) { + Contract.Ensures((Contract.Result<string>() != null) == (realm != null)); return realm != null ? realm.ToString() : null; } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs index bc2b6ca..ac70387 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs @@ -96,7 +96,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="provider">The provider to create an association with.</param> /// <returns>The association if one exists and has useful life remaining. Otherwise <c>null</c>.</returns> - internal Association GetExistingAssociation(ProviderEndpointDescription provider) { + internal Association GetExistingAssociation(IProviderEndpoint provider) { Contract.Requires<ArgumentNullException>(provider != null); // If the RP has no application store for associations, there's no point in creating one. @@ -104,7 +104,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return null; } - Association association = this.associationStore.GetAssociation(provider.Endpoint, this.SecuritySettings); + Association association = this.associationStore.GetAssociation(provider.Uri, this.SecuritySettings); // If the returned association does not fulfill security requirements, ignore it. if (association != null && !this.SecuritySettings.IsAssociationInPermittedRange(association)) { @@ -124,7 +124,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="provider">The provider to get an association for.</param> /// <returns>The existing or new association; <c>null</c> if none existed and one could not be created.</returns> - internal Association GetOrCreateAssociation(ProviderEndpointDescription provider) { + internal Association GetOrCreateAssociation(IProviderEndpoint provider) { return this.GetExistingAssociation(provider) ?? this.CreateNewAssociation(provider); } @@ -141,7 +141,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// association store. /// Any new association is automatically added to the <see cref="associationStore"/>. /// </remarks> - private Association CreateNewAssociation(ProviderEndpointDescription provider) { + private Association CreateNewAssociation(IProviderEndpoint provider) { Contract.Requires<ArgumentNullException>(provider != null); // If there is no association store, there is no point in creating an association. @@ -160,7 +160,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // sometimes causes the CLR to throw: // "VerificationException: Operation could destabilize the runtime." // Just give up and use dumb mode in this case. - Logger.OpenId.ErrorFormat("VerificationException occurred while trying to create an association with {0}. {1}", provider.Endpoint, ex); + Logger.OpenId.ErrorFormat("VerificationException occurred while trying to create an association with {0}. {1}", provider.Uri, ex); return null; } } @@ -175,7 +175,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The newly created association, or null if no association can be created with /// the given Provider given the current security settings. /// </returns> - private Association CreateNewAssociation(ProviderEndpointDescription provider, AssociateRequest associateRequest, int retriesRemaining) { + private Association CreateNewAssociation(IProviderEndpoint provider, AssociateRequest associateRequest, int retriesRemaining) { Contract.Requires<ArgumentNullException>(provider != null); if (associateRequest == null || retriesRemaining < 0) { @@ -190,7 +190,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse; if (associateSuccessfulResponse != null) { Association association = associateSuccessfulResponse.CreateAssociation(associateRequest, null); - this.associationStore.StoreAssociation(provider.Endpoint, association); + this.associationStore.StoreAssociation(provider.Uri, association); return association; } else if (associateUnsuccessfulResponse != null) { if (string.IsNullOrEmpty(associateUnsuccessfulResponse.AssociationType)) { @@ -198,7 +198,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return null; } - if (!this.securitySettings.IsAssociationInPermittedRange(Protocol.Lookup(provider.ProtocolVersion), associateUnsuccessfulResponse.AssociationType)) { + if (!this.securitySettings.IsAssociationInPermittedRange(Protocol.Lookup(provider.Version), associateUnsuccessfulResponse.AssociationType)) { Logger.OpenId.DebugFormat("Provider rejected an association request and suggested '{0}' as an association to try, which this Relying Party does not support. Giving up.", associateUnsuccessfulResponse.AssociationType); return null; } @@ -209,7 +209,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } // Make sure the Provider isn't suggesting an incompatible pair of association/session types. - Protocol protocol = Protocol.Lookup(provider.ProtocolVersion); + Protocol protocol = Protocol.Lookup(provider.Version); ErrorUtilities.VerifyProtocol( HmacShaAssociation.IsDHSessionCompatible(protocol, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType), OpenIdStrings.IncompatibleAssociationAndSessionTypes, @@ -231,7 +231,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // Since having associations with OPs is not totally critical, we'll log and eat // the exception so that auth may continue in dumb mode. - Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Endpoint, ex); + Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Uri, ex); return null; } } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs index def8f34..09383bb 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs @@ -4,8 +4,6 @@ // </copyright> //----------------------------------------------------------------------- -using System.Threading; - namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Collections.Generic; @@ -13,6 +11,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Diagnostics.Contracts; using System.Linq; using System.Text; + using System.Threading; using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.ChannelElements; @@ -34,12 +33,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private readonly OpenIdRelyingParty RelyingParty; /// <summary> - /// The endpoint that describes the particular OpenID Identifier and Provider that - /// will be used to create the authentication request. - /// </summary> - private readonly ServiceEndpoint endpoint; - - /// <summary> /// How an association may or should be created or used in the formulation of the /// authentication request. /// </summary> @@ -69,17 +62,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Initializes a new instance of the <see cref="AuthenticationRequest"/> class. /// </summary> - /// <param name="endpoint">The endpoint that describes the OpenID Identifier and Provider that will complete the authentication.</param> + /// <param name="discoveryResult">The endpoint that describes the OpenID Identifier and Provider that will complete the authentication.</param> /// <param name="realm">The realm, or root URL, of the host web site.</param> /// <param name="returnToUrl">The base return_to URL that the Provider should return the user to to complete authentication. This should not include callback parameters as these should be added using the <see cref="AddCallbackArguments(string, string)"/> method.</param> /// <param name="relyingParty">The relying party that created this instance.</param> - private AuthenticationRequest(ServiceEndpoint endpoint, Realm realm, Uri returnToUrl, OpenIdRelyingParty relyingParty) { - Contract.Requires<ArgumentNullException>(endpoint != null); + private AuthenticationRequest(IdentifierDiscoveryResult discoveryResult, Realm realm, Uri returnToUrl, OpenIdRelyingParty relyingParty) { + Contract.Requires<ArgumentNullException>(discoveryResult != null); Contract.Requires<ArgumentNullException>(realm != null); Contract.Requires<ArgumentNullException>(returnToUrl != null); Contract.Requires<ArgumentNullException>(relyingParty != null); - this.endpoint = endpoint; + this.DiscoveryResult = discoveryResult; this.RelyingParty = relyingParty; this.Realm = realm; this.ReturnToUrl = returnToUrl; @@ -139,7 +132,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// property for a null value. /// </remarks> public Identifier ClaimedIdentifier { - get { return this.IsDirectedIdentity ? null : this.endpoint.ClaimedIdentifier; } + get { return this.IsDirectedIdentity ? null : this.DiscoveryResult.ClaimedIdentifier; } } /// <summary> @@ -147,7 +140,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// determine and send the ClaimedIdentifier after authentication. /// </summary> public bool IsDirectedIdentity { - get { return this.endpoint.ClaimedIdentifier == this.endpoint.Protocol.ClaimedIdentifierForOPIdentifier; } + get { return this.DiscoveryResult.ClaimedIdentifier == this.DiscoveryResult.Protocol.ClaimedIdentifierForOPIdentifier; } } /// <summary> @@ -166,9 +159,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// location. /// </summary> public IProviderEndpoint Provider { - get { return this.endpoint; } + get { return this.DiscoveryResult; } } + /// <summary> + /// Gets the discovery result leading to the formulation of this request. + /// </summary> + /// <value>The discovery result.</value> + public IdentifierDiscoveryResult DiscoveryResult { get; private set; } + #endregion /// <summary> @@ -194,13 +193,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { get { return this.extensions; } } - /// <summary> - /// Gets the service endpoint. - /// </summary> - internal ServiceEndpoint Endpoint { - get { return this.endpoint; } - } - #region IAuthenticationRequest methods /// <summary> @@ -367,12 +359,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { ErrorUtilities.VerifyProtocol(realm.Contains(returnToUrl), OpenIdStrings.ReturnToNotUnderRealm, returnToUrl, realm); // Perform discovery right now (not deferred). - IEnumerable<ServiceEndpoint> serviceEndpoints; + IEnumerable<IdentifierDiscoveryResult> serviceEndpoints; try { - serviceEndpoints = userSuppliedIdentifier.Discover(relyingParty.WebRequestHandler); + var results = relyingParty.Discover(userSuppliedIdentifier).CacheGeneratedResults(); + + // If any OP Identifier service elements were found, we must not proceed + // to use any Claimed Identifier services, per OpenID 2.0 sections 7.3.2.2 and 11.2. + // For a discussion on this topic, see + // http://groups.google.com/group/dotnetopenid/browse_thread/thread/4b5a8c6b2210f387/5e25910e4d2252c8 + // Usually the Discover method we called will automatically filter this for us, but + // just to be sure, we'll do it here as well since the RP may be configured to allow + // these dual identifiers for assertion verification purposes. + var opIdentifiers = results.Where(result => result.ClaimedIdentifier == result.Protocol.ClaimedIdentifierForOPIdentifier).CacheGeneratedResults(); + var claimedIdentifiers = results.Where(result => result.ClaimedIdentifier != result.Protocol.ClaimedIdentifierForOPIdentifier); + serviceEndpoints = opIdentifiers.Any() ? opIdentifiers : claimedIdentifiers; } catch (ProtocolException ex) { Logger.Yadis.ErrorFormat("Error while performing discovery on: \"{0}\": {1}", userSuppliedIdentifier, ex); - serviceEndpoints = EmptyList<ServiceEndpoint>.Instance; + serviceEndpoints = Enumerable.Empty<IdentifierDiscoveryResult>(); } // Filter disallowed endpoints. @@ -383,6 +386,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Creates an instance of <see cref="AuthenticationRequest"/> FOR TESTING PURPOSES ONLY. + /// </summary> + /// <param name="discoveryResult">The discovery result.</param> + /// <param name="realm">The realm.</param> + /// <param name="returnTo">The return to.</param> + /// <param name="rp">The relying party.</param> + /// <returns>The instantiated <see cref="AuthenticationRequest"/>.</returns> + internal static AuthenticationRequest CreateForTest(IdentifierDiscoveryResult discoveryResult, Realm realm, Uri returnTo, OpenIdRelyingParty rp) { + return new AuthenticationRequest(discoveryResult, realm, returnTo, rp); + } + + /// <summary> /// Performs deferred request generation for the <see cref="Create"/> method. /// </summary> /// <param name="userSuppliedIdentifier">The user supplied identifier.</param> @@ -399,7 +414,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// All data validation and cleansing steps must have ALREADY taken place /// before calling this method. /// </remarks> - private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<ServiceEndpoint> serviceEndpoints, bool createNewAssociationsAsNeeded) { + private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<IdentifierDiscoveryResult> serviceEndpoints, bool createNewAssociationsAsNeeded) { // DO NOT USE CODE CONTRACTS IN THIS METHOD, since it uses yield return ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty"); @@ -411,12 +426,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { ErrorUtilities.VerifyOperation(!relyingParty.SecuritySettings.RequireAssociation || relyingParty.AssociationManager.HasAssociationStore, OpenIdStrings.AssociationStoreRequired); Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier); - IEnumerable<ServiceEndpoint> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty); + IEnumerable<IdentifierDiscoveryResult> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty); // Maintain a list of endpoints that we could not form an association with. // We'll fallback to generating requests to these if the ones we CAN create // an association with run out. - var failedAssociationEndpoints = new List<ServiceEndpoint>(0); + var failedAssociationEndpoints = new List<IdentifierDiscoveryResult>(0); foreach (var endpoint in endpoints) { Logger.OpenId.DebugFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier); @@ -427,7 +442,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // In some scenarios (like the AJAX control wanting ALL auth requests possible), // we don't want to create associations with every Provider. But we'll use // associations where they are already formed from previous authentications. - association = createNewAssociationsAsNeeded ? relyingParty.AssociationManager.GetOrCreateAssociation(endpoint.ProviderDescription) : relyingParty.AssociationManager.GetExistingAssociation(endpoint.ProviderDescription); + association = createNewAssociationsAsNeeded ? relyingParty.AssociationManager.GetOrCreateAssociation(endpoint) : relyingParty.AssociationManager.GetExistingAssociation(endpoint); if (association == null && createNewAssociationsAsNeeded) { Logger.OpenId.WarnFormat("Failed to create association with {0}. Skipping to next endpoint.", endpoint.ProviderEndpoint); @@ -470,17 +485,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <param name="endpoints">The endpoints.</param> /// <param name="relyingParty">The relying party.</param> /// <returns>A filtered and sorted list of endpoints; may be empty if the input was empty or the filter removed all endpoints.</returns> - private static List<ServiceEndpoint> FilterAndSortEndpoints(IEnumerable<ServiceEndpoint> endpoints, OpenIdRelyingParty relyingParty) { + private static List<IdentifierDiscoveryResult> FilterAndSortEndpoints(IEnumerable<IdentifierDiscoveryResult> endpoints, OpenIdRelyingParty relyingParty) { Contract.Requires<ArgumentNullException>(endpoints != null); Contract.Requires<ArgumentNullException>(relyingParty != null); // Construct the endpoints filters based on criteria given by the host web site. - EndpointSelector versionFilter = ep => ((ServiceEndpoint)ep).Protocol.Version >= Protocol.Lookup(relyingParty.SecuritySettings.MinimumRequiredOpenIdVersion).Version; + EndpointSelector versionFilter = ep => ep.Version >= Protocol.Lookup(relyingParty.SecuritySettings.MinimumRequiredOpenIdVersion).Version; EndpointSelector hostingSiteFilter = relyingParty.EndpointFilter ?? (ep => true); bool anyFilteredOut = false; - var filteredEndpoints = new List<IXrdsProviderEndpoint>(); - foreach (ServiceEndpoint endpoint in endpoints) { + var filteredEndpoints = new List<IdentifierDiscoveryResult>(); + foreach (var endpoint in endpoints) { if (versionFilter(endpoint) && hostingSiteFilter(endpoint)) { filteredEndpoints.Add(endpoint); } else { @@ -489,10 +504,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } // Sort endpoints so that the first one in the list is the most preferred one. - filteredEndpoints.Sort(relyingParty.EndpointOrder); + filteredEndpoints.OrderBy(ep => ep, relyingParty.EndpointOrder); - List<ServiceEndpoint> endpointList = new List<ServiceEndpoint>(filteredEndpoints.Count); - foreach (ServiceEndpoint endpoint in filteredEndpoints) { + var endpointList = new List<IdentifierDiscoveryResult>(filteredEndpoints.Count); + foreach (var endpoint in filteredEndpoints) { endpointList.Add(endpoint); } @@ -521,20 +536,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { SignedResponseRequest request; if (!this.IsExtensionOnly) { - CheckIdRequest authRequest = new CheckIdRequest(this.endpoint.Protocol.Version, this.endpoint.ProviderEndpoint, this.Mode); - authRequest.ClaimedIdentifier = this.endpoint.ClaimedIdentifier; - authRequest.LocalIdentifier = this.endpoint.ProviderLocalIdentifier; + CheckIdRequest authRequest = new CheckIdRequest(this.DiscoveryResult.Version, this.DiscoveryResult.ProviderEndpoint, this.Mode); + authRequest.ClaimedIdentifier = this.DiscoveryResult.ClaimedIdentifier; + authRequest.LocalIdentifier = this.DiscoveryResult.ProviderLocalIdentifier; request = authRequest; } else { - request = new SignedResponseRequest(this.endpoint.Protocol.Version, this.endpoint.ProviderEndpoint, this.Mode); + request = new SignedResponseRequest(this.DiscoveryResult.Version, this.DiscoveryResult.ProviderEndpoint, this.Mode); } request.Realm = this.Realm; request.ReturnTo = this.ReturnToUrl; request.AssociationHandle = association != null ? association.Handle : null; request.SignReturnTo = this.returnToArgsMustBeSigned; request.AddReturnToArguments(this.returnToArgs); - if (this.endpoint.UserSuppliedIdentifier != null) { - request.AddReturnToArguments(UserSuppliedIdentifierParameterName, this.endpoint.UserSuppliedIdentifier.OriginalString); + if (this.DiscoveryResult.UserSuppliedIdentifier != null) { + request.AddReturnToArguments(UserSuppliedIdentifierParameterName, this.DiscoveryResult.UserSuppliedIdentifier.OriginalString); } foreach (IOpenIdMessageExtension extension in this.extensions) { request.Extensions.Add(extension); @@ -551,7 +566,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { Association association = null; switch (this.associationPreference) { case AssociationPreference.IfPossible: - association = this.RelyingParty.AssociationManager.GetOrCreateAssociation(this.endpoint.ProviderDescription); + association = this.RelyingParty.AssociationManager.GetOrCreateAssociation(this.DiscoveryResult); if (association == null) { // Avoid trying to create the association again if the redirecting response // is generated again. @@ -559,7 +574,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } break; case AssociationPreference.IfAlreadyEstablished: - association = this.RelyingParty.AssociationManager.GetExistingAssociation(this.endpoint.ProviderDescription); + association = this.RelyingParty.AssociationManager.GetExistingAssociation(this.DiscoveryResult); break; case AssociationPreference.Never: break; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/DuplicateRequestedHostsComparer.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/DuplicateRequestedHostsComparer.cs new file mode 100644 index 0000000..94eb5ba --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/DuplicateRequestedHostsComparer.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------- +// <copyright file="DuplicateRequestedHostsComparer.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.RelyingParty { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// An authentication request comparer that judges equality solely on the OP endpoint hostname. + /// </summary> + internal class DuplicateRequestedHostsComparer : IEqualityComparer<IAuthenticationRequest> { + /// <summary> + /// The singleton instance of this comparer. + /// </summary> + private static IEqualityComparer<IAuthenticationRequest> instance = new DuplicateRequestedHostsComparer(); + + /// <summary> + /// Prevents a default instance of the <see cref="DuplicateRequestedHostsComparer"/> class from being created. + /// </summary> + private DuplicateRequestedHostsComparer() { + } + + /// <summary> + /// Gets the singleton instance of this comparer. + /// </summary> + internal static IEqualityComparer<IAuthenticationRequest> Instance { + get { return instance; } + } + + #region IEqualityComparer<IAuthenticationRequest> Members + + /// <summary> + /// Determines whether the specified objects are equal. + /// </summary> + /// <param name="x">The first object to compare.</param> + /// <param name="y">The second object to compare.</param> + /// <returns> + /// true if the specified objects are equal; otherwise, false. + /// </returns> + public bool Equals(IAuthenticationRequest x, IAuthenticationRequest y) { + if (x == null && y == null) { + return true; + } + + if (x == null || y == null) { + return false; + } + + // We'll distinguish based on the host name only, which + // admittedly is only a heuristic, but if we remove one that really wasn't a duplicate, well, + // this multiple OP attempt thing was just a convenience feature anyway. + return string.Equals(x.Provider.Uri.Host, y.Provider.Uri.Host, StringComparison.OrdinalIgnoreCase); + } + + /// <summary> + /// Returns a hash code for the specified object. + /// </summary> + /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param> + /// <returns>A hash code for the specified object.</returns> + /// <exception cref="T:System.ArgumentNullException"> + /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null. + /// </exception> + public int GetHashCode(IAuthenticationRequest obj) { + return obj.Provider.Uri.Host.GetHashCode(); + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs index 3808c85..65db0bd 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs @@ -97,6 +97,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { IProviderEndpoint Provider { get; } /// <summary> + /// Gets the discovery result leading to the formulation of this request. + /// </summary> + /// <value>The discovery result.</value> + IdentifierDiscoveryResult DiscoveryResult { get; } + + /// <summary> /// Makes a dictionary of key/value pairs available when the authentication is completed. /// </summary> /// <param name="arguments">The arguments to add to the request's return_to URI. Values must not be null.</param> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs index 41cc4e9..cd36cc7 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs @@ -32,11 +32,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } Realm IAuthenticationRequest.Realm { - get { throw new NotImplementedException(); } + get { + Contract.Ensures(Contract.Result<Realm>() != null); + throw new NotImplementedException(); + } } Identifier IAuthenticationRequest.ClaimedIdentifier { - get { throw new NotImplementedException(); } + get { + throw new NotImplementedException(); + } } bool IAuthenticationRequest.IsDirectedIdentity { @@ -54,7 +59,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } IProviderEndpoint IAuthenticationRequest.Provider { - get { throw new NotImplementedException(); } + get { + Contract.Ensures(Contract.Result<IProviderEndpoint>() != null); + throw new NotImplementedException(); + } + } + + IdentifierDiscoveryResult IAuthenticationRequest.DiscoveryResult { + get { + Contract.Ensures(Contract.Result<IdentifierDiscoveryResult>() != null); + throw new NotImplementedException(); + } } void IAuthenticationRequest.AddCallbackArguments(IDictionary<string, string> arguments) { diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs index a90ddd4..5d8918d 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System; + using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; @@ -30,6 +31,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Gets the URL that the OpenID Provider receives authentication requests at. /// </summary> + /// <value> + /// This value MUST be an absolute HTTP or HTTPS URL. + /// </value> Uri Uri { get; } /// <summary> @@ -45,6 +49,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// the extension in the request and see if a response comes back for that extension. /// </remarks> [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "No parameter at all.")] + [Obsolete("Use IAuthenticationRequest.DiscoveryResult.IsExtensionSupported instead.")] bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new(); /// <summary> @@ -59,6 +64,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The only way to be sure of support for a given extension is to include /// the extension in the request and see if a response comes back for that extension. /// </remarks> + [Obsolete("Use IAuthenticationRequest.DiscoveryResult.IsExtensionSupported instead.")] bool IsExtensionSupported(Type extensionType); } @@ -67,20 +73,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> [ContractClassFor(typeof(IProviderEndpoint))] internal abstract class IProviderEndpointContract : IProviderEndpoint { + /// <summary> + /// Prevents a default instance of the <see cref="IProviderEndpointContract"/> class from being created. + /// </summary> + private IProviderEndpointContract() { + } + #region IProviderEndpoint Members /// <summary> /// Gets the detected version of OpenID implemented by the Provider. /// </summary> Version IProviderEndpoint.Version { - get { throw new NotImplementedException(); } + get { + Contract.Ensures(Contract.Result<Version>() != null); + throw new System.NotImplementedException(); + } } /// <summary> /// Gets the URL that the OpenID Provider receives authentication requests at. /// </summary> Uri IProviderEndpoint.Uri { - get { throw new NotImplementedException(); } + get { + Contract.Ensures(Contract.Result<Uri>() != null); + throw new System.NotImplementedException(); + } } /// <summary> @@ -118,7 +136,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { bool IProviderEndpoint.IsExtensionSupported(Type extensionType) { Contract.Requires<ArgumentNullException>(extensionType != null); Contract.Requires<ArgumentException>(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType)); - ////ErrorUtilities.VerifyArgument(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType), string.Format(CultureInfo.CurrentCulture, OpenIdStrings.TypeMustImplementX, typeof(IOpenIdMessageExtension).FullName)); throw new NotImplementedException(); } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpoint.cs deleted file mode 100644 index 89b4ef0..0000000 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpoint.cs +++ /dev/null @@ -1,41 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="IXrdsProviderEndpoint.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OpenId.RelyingParty { - using System; - using System.Diagnostics.CodeAnalysis; - using System.Diagnostics.Contracts; - - /// <summary> - /// An <see cref="IProviderEndpoint"/> interface with additional members for use - /// in sorting for most preferred endpoint. - /// </summary> - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xrds", Justification = "Xrds is an acronym.")] - [ContractClass(typeof(IXrdsProviderEndpointContract))] - public interface IXrdsProviderEndpoint : IProviderEndpoint { - /// <summary> - /// Gets the priority associated with this service that may have been given - /// in the XRDS document. - /// </summary> - int? ServicePriority { get; } - - /// <summary> - /// Gets the priority associated with the service endpoint URL. - /// </summary> - /// <remarks> - /// When sorting by priority, this property should be considered second after - /// <see cref="ServicePriority"/>. - /// </remarks> - int? UriPriority { get; } - - /// <summary> - /// Checks for the presence of a given Type URI in an XRDS service. - /// </summary> - /// <param name="typeUri">The type URI to check for.</param> - /// <returns><c>true</c> if the service type uri is present; <c>false</c> otherwise.</returns> - bool IsTypeUriPresent(string typeUri); - } -} diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpointContract.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpointContract.cs deleted file mode 100644 index e0e2b0b..0000000 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpointContract.cs +++ /dev/null @@ -1,59 +0,0 @@ -// <auto-generated /> - -namespace DotNetOpenAuth.OpenId.RelyingParty { - using System; - using System.Diagnostics.CodeAnalysis; - using System.Diagnostics.Contracts; - using System.Globalization; - using DotNetOpenAuth.OpenId.Messages; - - [ContractClassFor(typeof(IXrdsProviderEndpoint))] - internal abstract class IXrdsProviderEndpointContract : IXrdsProviderEndpoint { - #region IXrdsProviderEndpoint Properties - - int? IXrdsProviderEndpoint.ServicePriority { - get { throw new System.NotImplementedException(); } - } - - int? IXrdsProviderEndpoint.UriPriority { - get { throw new System.NotImplementedException(); } - } - - #endregion - - #region IProviderEndpoint Properties - - Version IProviderEndpoint.Version { - get { throw new System.NotImplementedException(); } - } - - Uri IProviderEndpoint.Uri { - get { throw new System.NotImplementedException(); } - } - - #endregion - - #region IXrdsProviderEndpoint Methods - - bool IXrdsProviderEndpoint.IsTypeUriPresent(string typeUri) { - throw new System.NotImplementedException(); - } - - #endregion - - #region IProviderEndpoint Methods - - bool IProviderEndpoint.IsExtensionSupported<T>() { - throw new System.NotImplementedException(); - } - - bool IProviderEndpoint.IsExtensionSupported(System.Type extensionType) { - Contract.Requires<ArgumentNullException>(extensionType != null); - Contract.Requires<ArgumentException>(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType)); - ////ErrorUtilities.VerifyArgument(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType), string.Format(CultureInfo.CurrentCulture, OpenIdStrings.TypeMustImplementX, typeof(IOpenIdMessageExtension).FullName)); - throw new System.NotImplementedException(); - } - - #endregion - } -} diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs new file mode 100644 index 0000000..ae9fbdc --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxRelyingParty.cs @@ -0,0 +1,238 @@ +//----------------------------------------------------------------------- +// <copyright file="OpenIdAjaxRelyingParty.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.RelyingParty { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.Linq; + using System.Net; + using System.Net.Mime; + using System.Text; + using System.Web; + using System.Web.Script.Serialization; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.Extensions; + using DotNetOpenAuth.OpenId.Extensions.UI; + + /// <summary> + /// Provides the programmatic facilities to act as an AJAX-enabled OpenID relying party. + /// </summary> + public class OpenIdAjaxRelyingParty : OpenIdRelyingParty { + /// <summary> + /// Initializes a new instance of the <see cref="OpenIdAjaxRelyingParty"/> class. + /// </summary> + public OpenIdAjaxRelyingParty() { + Reporting.RecordFeatureUse(this); + } + + /// <summary> + /// Initializes a new instance of the <see cref="OpenIdAjaxRelyingParty"/> class. + /// </summary> + /// <param name="applicationStore">The application store. If <c>null</c>, the relying party will always operate in "dumb mode".</param> + public OpenIdAjaxRelyingParty(IRelyingPartyApplicationStore applicationStore) + : base(applicationStore) { + Reporting.RecordFeatureUse(this); + } + + /// <summary> + /// Generates AJAX-ready authentication requests that can satisfy the requirements of some OpenID Identifier. + /// </summary> + /// <param name="userSuppliedIdentifier">The Identifier supplied by the user. This may be a URL, an XRI or i-name.</param> + /// <param name="realm">The shorest URL that describes this relying party web site's address. + /// For example, if your login page is found at https://www.example.com/login.aspx, + /// your realm would typically be https://www.example.com/.</param> + /// <param name="returnToUrl">The URL of the login page, or the page prepared to receive authentication + /// responses from the OpenID Provider.</param> + /// <returns> + /// A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier. + /// Never null, but may be empty. + /// </returns> + /// <remarks> + /// <para>Any individual generated request can satisfy the authentication. + /// The generated requests are sorted in preferred order. + /// Each request is generated as it is enumerated to. Associations are created only as + /// <see cref="IAuthenticationRequest.RedirectingResponse"/> is called.</para> + /// <para>No exception is thrown if no OpenID endpoints were discovered. + /// An empty enumerable is returned instead.</para> + /// </remarks> + public override IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { + var requests = base.CreateRequests(userSuppliedIdentifier, realm, returnToUrl); + + // Alter the requests so that have AJAX characteristics. + // Some OPs may be listed multiple times (one with HTTPS and the other with HTTP, for example). + // Since we're gathering OPs to try one after the other, just take the first choice of each OP + // and don't try it multiple times. + requests = requests.Distinct(DuplicateRequestedHostsComparer.Instance); + + // Configure each generated request. + int reqIndex = 0; + foreach (var req in requests) { + // Inform ourselves in return_to that we're in a popup. + req.SetUntrustedCallbackArgument(OpenIdRelyingPartyControlBase.UIPopupCallbackKey, "1"); + + if (req.DiscoveryResult.IsExtensionSupported<UIRequest>()) { + // Inform the OP that we'll be using a popup window consistent with the UI extension. + req.AddExtension(new UIRequest()); + + // Provide a hint for the client javascript about whether the OP supports the UI extension. + // This is so the window can be made the correct size for the extension. + // If the OP doesn't advertise support for the extension, the javascript will use + // a bigger popup window. + req.SetUntrustedCallbackArgument(OpenIdRelyingPartyControlBase.PopupUISupportedJSHint, "1"); + } + + req.SetUntrustedCallbackArgument("index", (reqIndex++).ToString(CultureInfo.InvariantCulture)); + + // If the ReturnToUrl was explicitly set, we'll need to reset our first parameter + if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)[AuthenticationRequest.UserSuppliedIdentifierParameterName])) { + req.SetUntrustedCallbackArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName, userSuppliedIdentifier.OriginalString); + } + + // Our javascript needs to let the user know which endpoint responded. So we force it here. + // This gives us the info even for 1.0 OPs and 2.0 setup_required responses. + req.SetUntrustedCallbackArgument(OpenIdRelyingPartyAjaxControlBase.OPEndpointParameterName, req.Provider.Uri.AbsoluteUri); + req.SetUntrustedCallbackArgument(OpenIdRelyingPartyAjaxControlBase.ClaimedIdParameterName, (string)req.ClaimedIdentifier ?? string.Empty); + + // Inform ourselves in return_to that we're in a popup or iframe. + req.SetUntrustedCallbackArgument(OpenIdRelyingPartyAjaxControlBase.UIPopupCallbackKey, "1"); + + // We append a # at the end so that if the OP happens to support it, + // the OpenID response "query string" is appended after the hash rather than before, resulting in the + // browser being super-speedy in closing the popup window since it doesn't try to pull a newer version + // of the static resource down from the server merely because of a changed URL. + // http://www.nabble.com/Re:-Defining-how-OpenID-should-behave-with-fragments-in-the-return_to-url-p22694227.html + ////TODO: + + yield return req; + } + } + + /// <summary> + /// Serializes discovery results on some <i>single</i> identifier on behalf of Javascript running on the browser. + /// </summary> + /// <param name="requests">The discovery results from just <i>one</i> identifier to serialize as a JSON response.</param> + /// <returns> + /// The JSON result to return to the user agent. + /// </returns> + /// <remarks> + /// We prepare a JSON object with this interface: + /// <code> + /// class jsonResponse { + /// string claimedIdentifier; + /// Array requests; // never null + /// string error; // null if no error + /// } + /// </code> + /// Each element in the requests array looks like this: + /// <code> + /// class jsonAuthRequest { + /// string endpoint; // URL to the OP endpoint + /// string immediate; // URL to initiate an immediate request + /// string setup; // URL to initiate a setup request. + /// } + /// </code> + /// </remarks> + public OutgoingWebResponse AsAjaxDiscoveryResult(IEnumerable<IAuthenticationRequest> requests) { + Contract.Requires<ArgumentNullException>(requests != null); + + var serializer = new JavaScriptSerializer(); + return new OutgoingWebResponse { + Body = serializer.Serialize(this.AsJsonDiscoveryResult(requests)), + }; + } + + /// <summary> + /// Serializes discovery on a set of identifiers for preloading into an HTML page that carries + /// an AJAX-aware OpenID control. + /// </summary> + /// <param name="requests">The discovery results to serialize as a JSON response.</param> + /// <returns> + /// The JSON result to return to the user agent. + /// </returns> + public string AsAjaxPreloadedDiscoveryResult(IEnumerable<IAuthenticationRequest> requests) { + Contract.Requires<ArgumentNullException>(requests != null); + + var serializer = new JavaScriptSerializer(); + string json = serializer.Serialize(this.AsJsonPreloadedDiscoveryResult(requests)); + + string script = "window.dnoa_internal.loadPreloadedDiscoveryResults(" + json + ");"; + return script; + } + + /// <summary> + /// Converts a sequence of authentication requests to a JSON object for seeding an AJAX-enabled login page. + /// </summary> + /// <param name="requests">The discovery results from just <i>one</i> identifier to serialize as a JSON response.</param> + /// <returns>A JSON object, not yet serialized.</returns> + internal object AsJsonDiscoveryResult(IEnumerable<IAuthenticationRequest> requests) { + Contract.Requires<ArgumentNullException>(requests != null); + + requests = requests.CacheGeneratedResults(); + + if (requests.Any()) { + return new { + claimedIdentifier = (string)requests.First().ClaimedIdentifier, + requests = requests.Select(req => new { + endpoint = req.Provider.Uri.AbsoluteUri, + immediate = this.GetRedirectUrl(req, true), + setup = this.GetRedirectUrl(req, false), + }).ToArray() + }; + } else { + return new { + requests = new object[0], + error = OpenIdStrings.OpenIdEndpointNotFound, + }; + } + } + + /// <summary> + /// Serializes discovery on a set of identifiers for preloading into an HTML page that carries + /// an AJAX-aware OpenID control. + /// </summary> + /// <param name="requests">The discovery results to serialize as a JSON response.</param> + /// <returns> + /// A JSON object, not yet serialized to a string. + /// </returns> + private object AsJsonPreloadedDiscoveryResult(IEnumerable<IAuthenticationRequest> requests) { + Contract.Requires<ArgumentNullException>(requests != null); + + // We prepare a JSON object with this interface: + // Array discoveryWrappers; + // Where each element in the above array has this interface: + // class discoveryWrapper { + // string userSuppliedIdentifier; + // jsonResponse discoveryResult; // contains result of call to SerializeDiscoveryAsJson(Identifier) + // } + var json = (from request in requests + group request by request.DiscoveryResult.UserSuppliedIdentifier into requestsByIdentifier + select new { + userSuppliedIdentifier = (string)requestsByIdentifier.Key, + discoveryResult = this.AsJsonDiscoveryResult(requestsByIdentifier), + }).ToArray(); + + return json; + } + + /// <summary> + /// Gets the full URL that carries an OpenID message, even if it exceeds the normal maximum size of a URL, + /// for purposes of sending to an AJAX component running in the browser. + /// </summary> + /// <param name="request">The authentication request.</param> + /// <param name="immediate"><c>true</c>to create a checkid_immediate request; + /// <c>false</c> to create a checkid_setup request.</param> + /// <returns>The absolute URL that carries the entire OpenID message.</returns> + private Uri GetRedirectUrl(IAuthenticationRequest request, bool immediate) { + Contract.Requires<ArgumentNullException>(request != null); + + request.Mode = immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup; + return request.RedirectingResponse.GetDirectUriRequest(this.Channel); + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs index 097d065..d80bf6a 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs @@ -64,6 +64,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> internal const bool DownloadYahooUILibraryDefault = true; + /// <summary> + /// The default value for the <see cref="Throttle"/> property. + /// </summary> + internal const int ThrottleDefault = 3; + #region Property viewstate keys /// <summary> @@ -221,11 +226,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private const string AuthenticationFailedToolTipDefault = "Authentication failed."; /// <summary> - /// The default value for the <see cref="Throttle"/> property. - /// </summary> - private const int ThrottleDefault = 3; - - /// <summary> /// The default value for the <see cref="LogOnText"/> property. /// </summary> private const string LogOnTextDefault = "LOG IN"; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs index f89ec0a..4aa78a5 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs @@ -643,11 +643,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { set { base.UsePersistentCookie = value; - // use conditional here to prevent infinite recursion - // with CheckedChanged event. - bool rememberMe = value != LogOnPersistence.Session; - if (this.rememberMeCheckBox.Checked != rememberMe) { - this.rememberMeCheckBox.Checked = rememberMe; + if (this.rememberMeCheckBox != null) { + // use conditional here to prevent infinite recursion + // with CheckedChanged event. + bool rememberMe = value != LogOnPersistence.Session; + if (this.rememberMeCheckBox.Checked != rememberMe) { + this.rememberMeCheckBox.Checked = rememberMe; + } } } } @@ -700,79 +702,104 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // top row, left cell cell = new TableCell(); - this.label = new HtmlGenericControl("label"); - this.label.InnerText = LabelTextDefault; - cell.Controls.Add(this.label); - row1.Cells.Add(cell); + try { + this.label = new HtmlGenericControl("label"); + this.label.InnerText = LabelTextDefault; + cell.Controls.Add(this.label); + row1.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } // top row, middle cell cell = new TableCell(); - cell.Controls.Add(new InPlaceControl(this)); - row1.Cells.Add(cell); + try { + cell.Controls.Add(new InPlaceControl(this)); + row1.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } // top row, right cell cell = new TableCell(); - this.loginButton = new Button(); - this.loginButton.ID = "loginButton"; - this.loginButton.Text = ButtonTextDefault; - this.loginButton.ToolTip = ButtonToolTipDefault; - this.loginButton.Click += this.LoginButton_Click; - this.loginButton.ValidationGroup = ValidationGroupDefault; + try { + this.loginButton = new Button(); + this.loginButton.ID = "loginButton"; + this.loginButton.Text = ButtonTextDefault; + this.loginButton.ToolTip = ButtonToolTipDefault; + this.loginButton.Click += this.LoginButton_Click; + this.loginButton.ValidationGroup = ValidationGroupDefault; #if !Mono - this.panel.DefaultButton = this.loginButton.ID; + this.panel.DefaultButton = this.loginButton.ID; #endif - cell.Controls.Add(this.loginButton); - row1.Cells.Add(cell); + cell.Controls.Add(this.loginButton); + row1.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } // middle row, left cell row2.Cells.Add(new TableCell()); // middle row, middle cell cell = new TableCell(); - cell.Style[HtmlTextWriterStyle.Color] = "gray"; - cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; - this.requiredValidator = new RequiredFieldValidator(); - this.requiredValidator.ErrorMessage = RequiredTextDefault + RequiredTextSuffix; - this.requiredValidator.Text = RequiredTextDefault + RequiredTextSuffix; - this.requiredValidator.Display = ValidatorDisplay.Dynamic; - this.requiredValidator.ValidationGroup = ValidationGroupDefault; - cell.Controls.Add(this.requiredValidator); - this.identifierFormatValidator = new CustomValidator(); - this.identifierFormatValidator.ErrorMessage = UriFormatTextDefault + RequiredTextSuffix; - this.identifierFormatValidator.Text = UriFormatTextDefault + RequiredTextSuffix; - this.identifierFormatValidator.ServerValidate += this.IdentifierFormatValidator_ServerValidate; - this.identifierFormatValidator.Enabled = UriValidatorEnabledDefault; - this.identifierFormatValidator.Display = ValidatorDisplay.Dynamic; - this.identifierFormatValidator.ValidationGroup = ValidationGroupDefault; - cell.Controls.Add(this.identifierFormatValidator); - this.errorLabel = new Label(); - this.errorLabel.EnableViewState = false; - this.errorLabel.ForeColor = System.Drawing.Color.Red; - this.errorLabel.Style[HtmlTextWriterStyle.Display] = "block"; // puts it on its own line - this.errorLabel.Visible = false; - cell.Controls.Add(this.errorLabel); - this.examplePrefixLabel = new Label(); - this.examplePrefixLabel.Text = ExamplePrefixDefault; - cell.Controls.Add(this.examplePrefixLabel); - cell.Controls.Add(new LiteralControl(" ")); - this.exampleUrlLabel = new Label(); - this.exampleUrlLabel.Font.Bold = true; - this.exampleUrlLabel.Text = ExampleUrlDefault; - cell.Controls.Add(this.exampleUrlLabel); - row2.Cells.Add(cell); + try { + cell.Style[HtmlTextWriterStyle.Color] = "gray"; + cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; + this.requiredValidator = new RequiredFieldValidator(); + this.requiredValidator.ErrorMessage = RequiredTextDefault + RequiredTextSuffix; + this.requiredValidator.Text = RequiredTextDefault + RequiredTextSuffix; + this.requiredValidator.Display = ValidatorDisplay.Dynamic; + this.requiredValidator.ValidationGroup = ValidationGroupDefault; + cell.Controls.Add(this.requiredValidator); + this.identifierFormatValidator = new CustomValidator(); + this.identifierFormatValidator.ErrorMessage = UriFormatTextDefault + RequiredTextSuffix; + this.identifierFormatValidator.Text = UriFormatTextDefault + RequiredTextSuffix; + this.identifierFormatValidator.ServerValidate += this.IdentifierFormatValidator_ServerValidate; + this.identifierFormatValidator.Enabled = UriValidatorEnabledDefault; + this.identifierFormatValidator.Display = ValidatorDisplay.Dynamic; + this.identifierFormatValidator.ValidationGroup = ValidationGroupDefault; + cell.Controls.Add(this.identifierFormatValidator); + this.errorLabel = new Label(); + this.errorLabel.EnableViewState = false; + this.errorLabel.ForeColor = System.Drawing.Color.Red; + this.errorLabel.Style[HtmlTextWriterStyle.Display] = "block"; // puts it on its own line + this.errorLabel.Visible = false; + cell.Controls.Add(this.errorLabel); + this.examplePrefixLabel = new Label(); + this.examplePrefixLabel.Text = ExamplePrefixDefault; + cell.Controls.Add(this.examplePrefixLabel); + cell.Controls.Add(new LiteralControl(" ")); + this.exampleUrlLabel = new Label(); + this.exampleUrlLabel.Font.Bold = true; + this.exampleUrlLabel.Text = ExampleUrlDefault; + cell.Controls.Add(this.exampleUrlLabel); + row2.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } // middle row, right cell cell = new TableCell(); - cell.Style[HtmlTextWriterStyle.Color] = "gray"; - cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; - cell.Style[HtmlTextWriterStyle.TextAlign] = "center"; - this.registerLink = new HyperLink(); - this.registerLink.Text = RegisterTextDefault; - this.registerLink.ToolTip = RegisterToolTipDefault; - this.registerLink.NavigateUrl = RegisterUrlDefault; - this.registerLink.Visible = RegisterVisibleDefault; - cell.Controls.Add(this.registerLink); - row2.Cells.Add(cell); + try { + cell.Style[HtmlTextWriterStyle.Color] = "gray"; + cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; + cell.Style[HtmlTextWriterStyle.TextAlign] = "center"; + this.registerLink = new HyperLink(); + this.registerLink.Text = RegisterTextDefault; + this.registerLink.ToolTip = RegisterToolTipDefault; + this.registerLink.NavigateUrl = RegisterUrlDefault; + this.registerLink.Visible = RegisterVisibleDefault; + cell.Controls.Add(this.registerLink); + row2.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } // bottom row, left cell cell = new TableCell(); @@ -780,17 +807,27 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // bottom row, middle cell cell = new TableCell(); - this.rememberMeCheckBox = new CheckBox(); - this.rememberMeCheckBox.Text = RememberMeTextDefault; - this.rememberMeCheckBox.Checked = RememberMeDefault; - this.rememberMeCheckBox.Visible = RememberMeVisibleDefault; - this.rememberMeCheckBox.CheckedChanged += this.RememberMeCheckBox_CheckedChanged; - cell.Controls.Add(this.rememberMeCheckBox); - row3.Cells.Add(cell); + try { + this.rememberMeCheckBox = new CheckBox(); + this.rememberMeCheckBox.Text = RememberMeTextDefault; + this.rememberMeCheckBox.Checked = this.UsePersistentCookie != LogOnPersistence.Session; + this.rememberMeCheckBox.Visible = RememberMeVisibleDefault; + this.rememberMeCheckBox.CheckedChanged += this.RememberMeCheckBox_CheckedChanged; + cell.Controls.Add(this.rememberMeCheckBox); + row3.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } // bottom row, right cell cell = new TableCell(); - row3.Cells.Add(cell); + try { + row3.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } // this sets all the controls' tab indexes this.TabIndex = TabIndexDefault; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs index dbf9530..8684bd1 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs @@ -762,13 +762,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { IRelyingPartyApplicationStore store = this.Stateless ? null : (this.CustomApplicationStore ?? DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore)); var rp = new OpenIdRelyingParty(store); - - // Only set RequireSsl to true, as we don't want to override - // a .config setting of true with false. - if (this.RequireSsl) { - rp.SecuritySettings.RequireSsl = true; + try { + // Only set RequireSsl to true, as we don't want to override + // a .config setting of true with false. + if (this.RequireSsl) { + rp.SecuritySettings.RequireSsl = true; + } + return rp; + } catch { + rp.Dispose(); + throw; } - return rp; } } } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index 5e67d5b..a416f3a 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -12,7 +12,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; + using System.Globalization; using System.Linq; + using System.Net; + using System.Net.Mime; + using System.Text; using System.Web; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; @@ -30,13 +34,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <c>True</c> if the endpoint should be considered. /// <c>False</c> to remove it from the pool of acceptable providers. /// </returns> - public delegate bool EndpointSelector(IXrdsProviderEndpoint endpoint); + public delegate bool EndpointSelector(IProviderEndpoint endpoint); /// <summary> - /// Provides the programmatic facilities to act as an OpenId consumer. + /// Provides the programmatic facilities to act as an OpenID relying party. /// </summary> [ContractVerification(true)] - public sealed class OpenIdRelyingParty : IDisposable { + public class OpenIdRelyingParty : IDisposable { /// <summary> /// The name of the key to use in the HttpApplication cache to store the /// instance of <see cref="StandardRelyingPartyApplicationStore"/> to use. @@ -49,6 +53,27 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private readonly ObservableCollection<IRelyingPartyBehavior> behaviors = new ObservableCollection<IRelyingPartyBehavior>(); /// <summary> + /// Backing field for the <see cref="DiscoveryServices"/> property. + /// </summary> + private readonly IList<IIdentifierDiscoveryService> discoveryServices = new List<IIdentifierDiscoveryService>(2); + + /// <summary> + /// Backing field for the <see cref="NonVerifyingRelyingParty"/> property. + /// </summary> + private OpenIdRelyingParty nonVerifyingRelyingParty; + + /// <summary> + /// The lock to obtain when initializing the <see cref="nonVerifyingRelyingParty"/> member. + /// </summary> + private object nonVerifyingRelyingPartyInitLock = new object(); + + /// <summary> + /// A dictionary of extension response types and the javascript member + /// name to map them to on the user agent. + /// </summary> + private Dictionary<Type, string> clientScriptExtensions = new Dictionary<Type, string>(); + + /// <summary> /// Backing field for the <see cref="SecuritySettings"/> property. /// </summary> private RelyingPartySecuritySettings securitySettings; @@ -56,7 +81,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Backing store for the <see cref="EndpointOrder"/> property. /// </summary> - private Comparison<IXrdsProviderEndpoint> endpointOrder = DefaultEndpointOrder; + private Comparison<IdentifierDiscoveryResult> endpointOrder = DefaultEndpointOrder; /// <summary> /// Backing field for the <see cref="Channel"/> property. @@ -73,7 +98,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Initializes a new instance of the <see cref="OpenIdRelyingParty"/> class. /// </summary> - /// <param name="applicationStore">The application store. If null, the relying party will always operate in "dumb mode".</param> + /// <param name="applicationStore">The application store. If <c>null</c>, the relying party will always operate in "dumb mode".</param> public OpenIdRelyingParty(IRelyingPartyApplicationStore applicationStore) : this(applicationStore, applicationStore) { } @@ -90,6 +115,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { Contract.Requires<ArgumentException>(associationStore == null || nonceStore != null, OpenIdStrings.AssociationStoreRequiresNonceStore); this.securitySettings = DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.SecuritySettings.CreateSecuritySettings(); + + foreach (var discoveryService in DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.DiscoveryServices.CreateInstances(true)) { + this.discoveryServices.Add(discoveryService); + } + this.behaviors.CollectionChanged += this.OnBehaviorsChanged; foreach (var behavior in DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.Behaviors.CreateInstances(false)) { this.behaviors.Add(behavior); @@ -98,11 +128,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // Without a nonce store, we must rely on the Provider to protect against // replay attacks. But only 2.0+ Providers can be expected to provide // replay protection. - if (nonceStore == null) { - if (this.SecuritySettings.MinimumRequiredOpenIdVersion < ProtocolVersion.V20) { - Logger.OpenId.Warn("Raising minimum OpenID version requirement for Providers to 2.0 to protect this stateless RP from replay attacks."); - this.SecuritySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; - } + if (nonceStore == null && + this.SecuritySettings.ProtectDownlevelReplayAttacks && + this.SecuritySettings.MinimumRequiredOpenIdVersion < ProtocolVersion.V20) { + Logger.OpenId.Warn("Raising minimum OpenID version requirement for Providers to 2.0 to protect this stateless RP from replay attacks."); + this.SecuritySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; } this.channel = new OpenIdChannel(associationStore, nonceStore, this.SecuritySettings); @@ -119,8 +149,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Endpoints lacking any priority value are sorted to the end of the list. /// </remarks> [EditorBrowsable(EditorBrowsableState.Advanced)] - public static Comparison<IXrdsProviderEndpoint> DefaultEndpointOrder { - get { return ServiceEndpoint.EndpointOrder; } + public static Comparison<IdentifierDiscoveryResult> DefaultEndpointOrder { + get { return IdentifierDiscoveryResult.EndpointOrder; } } /// <summary> @@ -202,7 +232,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// can be set to the value of <see cref="DefaultEndpointOrder"/>. /// </remarks> [EditorBrowsable(EditorBrowsableState.Advanced)] - public Comparison<IXrdsProviderEndpoint> EndpointOrder { + public Comparison<IdentifierDiscoveryResult> EndpointOrder { get { return this.endpointOrder; } @@ -232,6 +262,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets the list of services that can perform discovery on identifiers given to this relying party. + /// </summary> + public IList<IIdentifierDiscoveryService> DiscoveryServices { + get { return this.discoveryServices; } + } + + /// <summary> /// Gets a value indicating whether this Relying Party can sign its return_to /// parameter in outgoing authentication requests. /// </summary> @@ -253,6 +290,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { internal AssociationManager AssociationManager { get; private set; } /// <summary> + /// Gets the <see cref="OpenIdRelyingParty"/> instance used to process authentication responses + /// without verifying the assertion or consuming nonces. + /// </summary> + protected OpenIdRelyingParty NonVerifyingRelyingParty { + get { + if (this.nonVerifyingRelyingParty == null) { + lock (this.nonVerifyingRelyingPartyInitLock) { + if (this.nonVerifyingRelyingParty == null) { + this.nonVerifyingRelyingParty = OpenIdRelyingParty.CreateNonVerifying(); + } + } + } + + return this.nonVerifyingRelyingParty; + } + } + + /// <summary> /// Creates an authentication request to verify that a user controls /// some given Identifier. /// </summary> @@ -372,13 +427,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <para>No exception is thrown if no OpenID endpoints were discovered. /// An empty enumerable is returned instead.</para> /// </remarks> - public IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { + public virtual IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null); Contract.Requires<ArgumentNullException>(realm != null); Contract.Requires<ArgumentNullException>(returnToUrl != null); Contract.Ensures(Contract.Result<IEnumerable<IAuthenticationRequest>>() != null); - return AuthenticationRequest.Create(userSuppliedIdentifier, this, realm, returnToUrl, true).Cast<IAuthenticationRequest>(); + return AuthenticationRequest.Create(userSuppliedIdentifier, this, realm, returnToUrl, true).Cast<IAuthenticationRequest>().CacheGeneratedResults(); } /// <summary> @@ -459,21 +514,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); Contract.Ensures(Contract.Result<IEnumerable<IAuthenticationRequest>>() != null); - // Build the realm URL - UriBuilder realmUrl = new UriBuilder(this.Channel.GetRequestFromContext().UrlBeforeRewriting); - realmUrl.Path = HttpContext.Current.Request.ApplicationPath; - realmUrl.Query = null; - realmUrl.Fragment = null; - - // For RP discovery, the realm url MUST NOT redirect. To prevent this for - // virtual directory hosted apps, we need to make sure that the realm path ends - // in a slash (since our calculation above guarantees it doesn't end in a specific - // page like default.aspx). - if (!realmUrl.Path.EndsWith("/", StringComparison.Ordinal)) { - realmUrl.Path += "/"; - } - - return this.CreateRequests(userSuppliedIdentifier, new Realm(realmUrl.Uri)); + return this.CreateRequests(userSuppliedIdentifier, Realm.AutoDetect); } /// <summary> @@ -484,6 +525,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> /// </remarks> public IAuthenticationResponse GetResponse() { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); return this.GetResponse(this.Channel.GetRequestFromContext()); } @@ -530,6 +572,52 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } } + /// <summary> + /// Processes the response received in a popup window or iframe to an AJAX-directed OpenID authentication. + /// </summary> + /// <returns>The HTTP response to send to this HTTP request.</returns> + /// <remarks> + /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> + /// </remarks> + public OutgoingWebResponse ProcessResponseFromPopup() { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); + Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + + return this.ProcessResponseFromPopup(this.Channel.GetRequestFromContext()); + } + + /// <summary> + /// Processes the response received in a popup window or iframe to an AJAX-directed OpenID authentication. + /// </summary> + /// <param name="request">The incoming HTTP request that is expected to carry an OpenID authentication response.</param> + /// <returns>The HTTP response to send to this HTTP request.</returns> + public OutgoingWebResponse ProcessResponseFromPopup(HttpRequestInfo request) { + Contract.Requires<ArgumentNullException>(request != null); + Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + + return this.ProcessResponseFromPopup(request, null); + } + + /// <summary> + /// Allows an OpenID extension to read data out of an unverified positive authentication assertion + /// and send it down to the client browser so that Javascript running on the page can perform + /// some preprocessing on the extension data. + /// </summary> + /// <typeparam name="T">The extension <i>response</i> type that will read data from the assertion.</typeparam> + /// <param name="propertyName">The property name on the openid_identifier input box object that will be used to store the extension data. For example: sreg</param> + /// <remarks> + /// This method should be called before <see cref="ProcessResponseFromPopup()"/>. + /// </remarks> + [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")] + public void RegisterClientScriptExtension<T>(string propertyName) where T : IClientScriptExtensionResponse { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(propertyName)); + ErrorUtilities.VerifyArgumentNamed(!this.clientScriptExtensions.ContainsValue(propertyName), "propertyName", OpenIdStrings.ClientScriptExtensionPropertyNameCollision, propertyName); + foreach (var ext in this.clientScriptExtensions.Keys) { + ErrorUtilities.VerifyArgument(ext != typeof(T), OpenIdStrings.ClientScriptExtensionTypeCollision, typeof(T).FullName); + } + this.clientScriptExtensions.Add(typeof(T), propertyName); + } + #region IDisposable Members /// <summary> @@ -577,11 +665,112 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Processes the response received in a popup window or iframe to an AJAX-directed OpenID authentication. + /// </summary> + /// <param name="request">The incoming HTTP request that is expected to carry an OpenID authentication response.</param> + /// <param name="callback">The callback fired after the response status has been determined but before the Javascript response is formulated.</param> + /// <returns> + /// The HTTP response to send to this HTTP request. + /// </returns> + internal OutgoingWebResponse ProcessResponseFromPopup(HttpRequestInfo request, Action<AuthenticationStatus> callback) { + Contract.Requires<ArgumentNullException>(request != null); + Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + + string extensionsJson = null; + var authResponse = this.NonVerifyingRelyingParty.GetResponse(); + ErrorUtilities.VerifyProtocol(authResponse != null, "OpenID popup window or iframe did not recognize an OpenID response in the request."); + + // Give the caller a chance to notify the hosting page and fill up the clientScriptExtensions collection. + if (callback != null) { + callback(authResponse.Status); + } + + Logger.OpenId.DebugFormat("Popup or iframe callback from OP: {0}", request.Url); + Logger.Controls.DebugFormat( + "An authentication response was found in a popup window or iframe using a non-verifying RP with status: {0}", + authResponse.Status); + if (authResponse.Status == AuthenticationStatus.Authenticated) { + var extensionsDictionary = new Dictionary<string, string>(); + foreach (var pair in this.clientScriptExtensions) { + IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key); + if (extension == null) { + continue; + } + var positiveResponse = (PositiveAuthenticationResponse)authResponse; + string js = extension.InitializeJavaScriptData(positiveResponse.Response); + if (!string.IsNullOrEmpty(js)) { + extensionsDictionary[pair.Value] = js; + } + } + + extensionsJson = MessagingUtilities.CreateJsonObject(extensionsDictionary, true); + } + + string payload = "document.URL"; + if (request.HttpMethod == "POST") { + // Promote all form variables to the query string, but since it won't be passed + // to any server (this is a javascript window-to-window transfer) the length of + // it can be arbitrarily long, whereas it was POSTed here probably because it + // was too long for HTTP transit. + UriBuilder payloadUri = new UriBuilder(request.Url); + payloadUri.AppendQueryArgs(request.Form.ToDictionary()); + payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri); + } + + if (!string.IsNullOrEmpty(extensionsJson)) { + payload += ", " + extensionsJson; + } + + return InvokeParentPageScript("dnoa_internal.processAuthorizationResult(" + payload + ")"); + } + + /// <summary> + /// Performs discovery on the specified identifier. + /// </summary> + /// <param name="identifier">The identifier to discover services for.</param> + /// <returns>A non-null sequence of services discovered for the identifier.</returns> + internal IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); + + IEnumerable<IdentifierDiscoveryResult> results = Enumerable.Empty<IdentifierDiscoveryResult>(); + foreach (var discoverer in this.DiscoveryServices) { + bool abortDiscoveryChain; + var discoveryResults = discoverer.Discover(identifier, this.WebRequestHandler, out abortDiscoveryChain).CacheGeneratedResults(); + results = results.Concat(discoveryResults); + if (abortDiscoveryChain) { + Logger.OpenId.InfoFormat("Further discovery on '{0}' was stopped by the {1} discovery service.", identifier, discoverer.GetType().Name); + break; + } + } + + // If any OP Identifier service elements were found, we must not proceed + // to use any Claimed Identifier services, per OpenID 2.0 sections 7.3.2.2 and 11.2. + // For a discussion on this topic, see + // http://groups.google.com/group/dotnetopenid/browse_thread/thread/4b5a8c6b2210f387/5e25910e4d2252c8 + // Sometimes the IIdentifierDiscoveryService will automatically filter this for us, but + // just to be sure, we'll do it here as well. + if (!this.SecuritySettings.AllowDualPurposeIdentifiers) { + results = results.CacheGeneratedResults(); // avoid performing discovery repeatedly + var opIdentifiers = results.Where(result => result.ClaimedIdentifier == result.Protocol.ClaimedIdentifierForOPIdentifier); + var claimedIdentifiers = results.Where(result => result.ClaimedIdentifier != result.Protocol.ClaimedIdentifierForOPIdentifier); + results = opIdentifiers.Any() ? opIdentifiers : claimedIdentifiers; + } + + return results; + } + + /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - private void Dispose(bool disposing) { + protected virtual void Dispose(bool disposing) { if (disposing) { + if (this.nonVerifyingRelyingParty != null) { + this.nonVerifyingRelyingParty.Dispose(); + this.nonVerifyingRelyingParty = null; + } + // Tear off the instance member as a local variable for thread safety. IDisposable disposableChannel = this.channel as IDisposable; if (disposableChannel != null) { @@ -591,6 +780,42 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Invokes a method on a parent frame or window and closes the calling popup window if applicable. + /// </summary> + /// <param name="methodCall">The method to call on the parent window, including + /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param> + /// <returns>The entire HTTP response to send to the popup window or iframe to perform the invocation.</returns> + private static OutgoingWebResponse InvokeParentPageScript(string methodCall) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(methodCall)); + + Logger.OpenId.DebugFormat("Sending Javascript callback: {0}", methodCall); + StringBuilder builder = new StringBuilder(); + builder.AppendLine("<html><body><script type='text/javascript' language='javascript'><!--"); + builder.AppendLine("//<![CDATA["); + builder.Append(@" var inPopup = !window.frameElement; + var objSrc = inPopup ? window.opener : window.frameElement; +"); + + // Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable, + // so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already + // whether to call window.self.close() after the call. + string htmlFormat = @" if (inPopup) {{ + objSrc.{0}; + window.self.close(); + }} else {{ + objSrc.{0}; + }}"; + builder.AppendFormat(CultureInfo.InvariantCulture, htmlFormat, methodCall); + builder.AppendLine("//]]>--></script>"); + builder.AppendLine("</body></html>"); + + var response = new OutgoingWebResponse(); + response.Body = builder.ToString(); + response.Headers.Add(HttpResponseHeader.ContentType, new ContentType("text/html").ToString()); + return response; + } + + /// <summary> /// Called by derived classes when behaviors are added or removed. /// </summary> /// <param name="sender">The collection being modified.</param> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs index 0254346..f22645f 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs @@ -16,6 +16,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Linq; using System.Text; using System.Web; + using System.Web.Script.Serialization; using System.Web.UI; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; @@ -31,30 +32,30 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { internal const string EmbeddedAjaxJavascriptResource = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdRelyingPartyAjaxControlBase.js"; /// <summary> - /// The name of the javascript function that will initiate a synchronous callback. + /// The "dnoa.op_endpoint" string. /// </summary> - protected const string CallbackJSFunction = "window.dnoa_internal.callback"; + internal const string OPEndpointParameterName = OpenIdUtilities.CustomParameterPrefix + "op_endpoint"; /// <summary> - /// The name of the javascript function that will initiate an asynchronous callback. + /// The "dnoa.claimed_id" string. /// </summary> - protected const string CallbackJSFunctionAsync = "window.dnoa_internal.callbackAsync"; + internal const string ClaimedIdParameterName = OpenIdUtilities.CustomParameterPrefix + "claimed_id"; /// <summary> /// The name of the javascript field that stores the maximum time a positive assertion is /// good for before it must be refreshed. /// </summary> - private const string MaxPositiveAssertionLifetimeJsName = "window.dnoa_internal.maxPositiveAssertionLifetime"; + internal const string MaxPositiveAssertionLifetimeJsName = "window.dnoa_internal.maxPositiveAssertionLifetime"; /// <summary> - /// The "dnoa.op_endpoint" string. + /// The name of the javascript function that will initiate an asynchronous callback. /// </summary> - private const string OPEndpointParameterName = OpenIdUtilities.CustomParameterPrefix + "op_endpoint"; + protected internal const string CallbackJSFunctionAsync = "window.dnoa_internal.callbackAsync"; /// <summary> - /// The "dnoa.claimed_id" string. + /// The name of the javascript function that will initiate a synchronous callback. /// </summary> - private const string ClaimedIdParameterName = OpenIdUtilities.CustomParameterPrefix + "claimed_id"; + protected const string CallbackJSFunction = "window.dnoa_internal.callback"; #region Property viewstate keys @@ -86,11 +87,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private const LogOnSiteNotification LogOnModeDefault = LogOnSiteNotification.None; /// <summary> - /// Backing field for the <see cref="RelyingPartyNonVerifying"/> property. - /// </summary> - private static OpenIdRelyingParty relyingPartyNonVerifying; - - /// <summary> /// The authentication response that just came in. /// </summary> private IAuthenticationResponse authenticationResponse; @@ -102,12 +98,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private string discoveryResult; /// <summary> - /// A dictionary of extension response types and the javascript member - /// name to map them to on the user agent. - /// </summary> - private Dictionary<Type, string> clientScriptExtensions = new Dictionary<Type, string>(); - - /// <summary> /// Initializes a new instance of the <see cref="OpenIdRelyingPartyAjaxControlBase"/> class. /// </summary> protected OpenIdRelyingPartyAjaxControlBase() { @@ -152,6 +142,31 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets or sets the <see cref="OpenIdRelyingParty"/> instance to use. + /// </summary> + /// <value> + /// The default value is an <see cref="OpenIdRelyingParty"/> instance initialized according to the web.config file. + /// </value> + /// <remarks> + /// A performance optimization would be to store off the + /// instance as a static member in your web site and set it + /// to this property in your <see cref="Control.Load">Page.Load</see> + /// event since instantiating these instances can be expensive on + /// heavily trafficked web pages. + /// </remarks> + public override OpenIdRelyingParty RelyingParty { + get { + return base.RelyingParty; + } + + set { + // Make sure we get an AJAX-ready instance. + ErrorUtilities.VerifyArgument(value is OpenIdAjaxRelyingParty, OpenIdStrings.TypeMustImplementX, typeof(OpenIdAjaxRelyingParty).Name); + base.RelyingParty = value; + } + } + + /// <summary> /// Gets the completed authentication response. /// </summary> public IAuthenticationResponse AuthenticationResponse { @@ -193,22 +208,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Gets the name of the open id auth data form key (for the value as stored at the user agent as a FORM field). + /// Gets the relying party as its AJAX type. /// </summary> - /// <value>Usually a concatenation of the control's name and <c>"_openidAuthData"</c>.</value> - protected abstract string OpenIdAuthDataFormKey { get; } + protected OpenIdAjaxRelyingParty AjaxRelyingParty { + get { return (OpenIdAjaxRelyingParty)this.RelyingParty; } + } /// <summary> - /// Gets the relying party to use when verification of incoming messages is NOT wanted. + /// Gets the name of the open id auth data form key (for the value as stored at the user agent as a FORM field). /// </summary> - private static OpenIdRelyingParty RelyingPartyNonVerifying { - get { - if (relyingPartyNonVerifying == null) { - relyingPartyNonVerifying = OpenIdRelyingParty.CreateNonVerifying(); - } - return relyingPartyNonVerifying; - } - } + /// <value>Usually a concatenation of the control's name and <c>"_openidAuthData"</c>.</value> + protected abstract string OpenIdAuthDataFormKey { get; } /// <summary> /// Gets or sets a value indicating whether an authentication in the page's view state @@ -232,11 +242,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")] public void RegisterClientScriptExtension<T>(string propertyName) where T : IClientScriptExtensionResponse { Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(propertyName)); - ErrorUtilities.VerifyArgumentNamed(!this.clientScriptExtensions.ContainsValue(propertyName), "propertyName", OpenIdStrings.ClientScriptExtensionPropertyNameCollision, propertyName); - foreach (var ext in this.clientScriptExtensions.Keys) { - ErrorUtilities.VerifyArgument(ext != typeof(T), OpenIdStrings.ClientScriptExtensionTypeCollision, typeof(T).FullName); - } - this.clientScriptExtensions.Add(typeof(T), propertyName); + this.RelyingParty.RegisterClientScriptExtension<T>(propertyName); } #region ICallbackEventHandler Members @@ -263,27 +269,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { #endregion /// <summary> - /// Creates the authentication requests for a given user-supplied Identifier. - /// </summary> - /// <param name="identifier">The identifier to create a request for.</param> - /// <returns> - /// A sequence of authentication requests, any one of which may be - /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>. - /// </returns> - protected internal override IEnumerable<IAuthenticationRequest> CreateRequests(Identifier identifier) { - // If this control is actually a member of another OpenID RP control, - // delegate creation of requests to the parent control. - var parentOwner = this.ParentControls.OfType<OpenIdRelyingPartyControlBase>().FirstOrDefault(); - if (parentOwner != null) { - return parentOwner.CreateRequests(identifier); - } else { - // We delegate all our logic to another method, since invoking base. methods - // within an iterator method results in unverifiable code. - return this.CreateRequestsCore(base.CreateRequests(identifier)); - } - } - - /// <summary> /// Returns the results of a callback event that targets a control. /// </summary> /// <returns>The result of the callback.</returns> @@ -305,7 +290,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { Logger.OpenId.InfoFormat("AJAX discovery on {0} requested.", userSuppliedIdentifier); this.Identifier = userSuppliedIdentifier; - this.discoveryResult = this.SerializeDiscoveryAsJson(this.Identifier); + + var serializer = new JavaScriptSerializer(); + IEnumerable<IAuthenticationRequest> requests = this.CreateRequests(this.Identifier); + this.discoveryResult = serializer.Serialize(this.AjaxRelyingParty.AsJsonDiscoveryResult(requests)); + } + + /// <summary> + /// Creates the relying party instance used to generate authentication requests. + /// </summary> + /// <param name="store">The store to pass to the relying party constructor.</param> + /// <returns>The instantiated relying party.</returns> + protected override OpenIdRelyingParty CreateRelyingParty(IRelyingPartyApplicationStore store) { + return new OpenIdAjaxRelyingParty(store); } /// <summary> @@ -323,8 +320,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="identifiers">The identifiers to perform discovery on.</param> protected void PreloadDiscovery(IEnumerable<Identifier> identifiers) { - string discoveryResults = this.SerializeDiscoveryAsJson(identifiers); - string script = "window.dnoa_internal.loadPreloadedDiscoveryResults(" + discoveryResults + ");"; + string script = this.AjaxRelyingParty.AsAjaxPreloadedDiscoveryResult( + identifiers.SelectMany(id => this.CreateRequests(id))); this.Page.ClientScript.RegisterClientScriptBlock(typeof(OpenIdRelyingPartyAjaxControlBase), this.ClientID, script, true); } @@ -402,6 +399,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param> protected override void Render(HtmlTextWriter writer) { + Contract.Assume(writer != null, "Missing contract."); base.Render(writer); // Emit a hidden field to let the javascript on the user agent know if an @@ -420,172 +418,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Notifies the user agent via an AJAX response of a completed authentication attempt. /// </summary> protected override void ScriptClosingPopupOrIFrame() { - Logger.OpenId.DebugFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url); - string extensionsJson = null; - - var authResponse = RelyingPartyNonVerifying.GetResponse(); - Logger.Controls.DebugFormat( - "The {0} control checked for an authentication response from a popup window or iframe using a non-verifying RP and found: {1}", - this.ID, - authResponse.Status); - if (authResponse.Status == AuthenticationStatus.Authenticated) { - this.OnUnconfirmedPositiveAssertion(); // event handler will fill the clientScriptExtensions collection. - var extensionsDictionary = new Dictionary<string, string>(); - foreach (var pair in this.clientScriptExtensions) { - IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key); - if (extension == null) { - continue; - } - var positiveResponse = (PositiveAuthenticationResponse)authResponse; - string js = extension.InitializeJavaScriptData(positiveResponse.Response); - if (!string.IsNullOrEmpty(js)) { - extensionsDictionary[pair.Value] = js; - } + Action<AuthenticationStatus> callback = status => { + if (status == AuthenticationStatus.Authenticated) { + this.OnUnconfirmedPositiveAssertion(); // event handler will fill the clientScriptExtensions collection. } + }; - extensionsJson = MessagingUtilities.CreateJsonObject(extensionsDictionary, true); - } - - string payload = "document.URL"; - if (Page.Request.HttpMethod == "POST") { - // Promote all form variables to the query string, but since it won't be passed - // to any server (this is a javascript window-to-window transfer) the length of - // it can be arbitrarily long, whereas it was POSTed here probably because it - // was too long for HTTP transit. - UriBuilder payloadUri = new UriBuilder(Page.Request.Url); - payloadUri.AppendQueryArgs(Page.Request.Form.ToDictionary()); - payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri); - } - - if (!string.IsNullOrEmpty(extensionsJson)) { - payload += ", " + extensionsJson; - } - - this.CallbackUserAgentMethod("dnoa_internal.processAuthorizationResult(" + payload + ")"); - } - - /// <summary> - /// Serializes the discovery of multiple identifiers as a JSON object. - /// </summary> - /// <param name="identifiers">The identifiers to perform discovery on and create requests for.</param> - /// <returns>The serialized JSON object.</returns> - private string SerializeDiscoveryAsJson(IEnumerable<Identifier> identifiers) { - ErrorUtilities.VerifyArgumentNotNull(identifiers, "identifiers"); - - // We prepare a JSON object with this interface: - // Array discoveryWrappers; - // Where each element in the above array has this interface: - // class discoveryWrapper { - // string userSuppliedIdentifier; - // jsonResponse discoveryResult; // contains result of call to SerializeDiscoveryAsJson(Identifier) - // } - StringBuilder discoveryResultBuilder = new StringBuilder(); - discoveryResultBuilder.Append("["); - foreach (var identifier in identifiers) { // TODO: parallelize discovery on these identifiers - discoveryResultBuilder.Append("{"); - discoveryResultBuilder.AppendFormat("userSuppliedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(identifier)); - discoveryResultBuilder.AppendFormat("discoveryResult: {0}", this.SerializeDiscoveryAsJson(identifier)); - discoveryResultBuilder.Append("},"); - } - - discoveryResultBuilder.Length -= 1; // trim last comma - discoveryResultBuilder.Append("]"); - return discoveryResultBuilder.ToString(); - } - - /// <summary> - /// Serializes the results of discovery and the created auth requests as a JSON object - /// for the user agent to initiate. - /// </summary> - /// <param name="identifier">The identifier to perform discovery on.</param> - /// <returns>The JSON string.</returns> - private string SerializeDiscoveryAsJson(Identifier identifier) { - ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier"); - - // We prepare a JSON object with this interface: - // class jsonResponse { - // string claimedIdentifier; - // Array requests; // never null - // string error; // null if no error - // } - // Each element in the requests array looks like this: - // class jsonAuthRequest { - // string endpoint; // URL to the OP endpoint - // string immediate; // URL to initiate an immediate request - // string setup; // URL to initiate a setup request. - // } - StringBuilder discoveryResultBuilder = new StringBuilder(); - discoveryResultBuilder.Append("{"); - try { - IEnumerable<IAuthenticationRequest> requests = this.CreateRequests(identifier).CacheGeneratedResults(); - if (requests.Any()) { - discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests.First().ClaimedIdentifier)); - discoveryResultBuilder.Append("requests: ["); - foreach (IAuthenticationRequest request in requests) { - discoveryResultBuilder.Append("{"); - discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri)); - request.Mode = AuthenticationRequestMode.Immediate; - OutgoingWebResponse response = request.RedirectingResponse; - discoveryResultBuilder.AppendFormat("immediate: {0},", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri)); - request.Mode = AuthenticationRequestMode.Setup; - response = request.RedirectingResponse; - discoveryResultBuilder.AppendFormat("setup: {0}", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri)); - discoveryResultBuilder.Append("},"); - } - discoveryResultBuilder.Length -= 1; // trim off last comma - discoveryResultBuilder.Append("]"); - } else { - discoveryResultBuilder.Append("requests: [],"); - discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(OpenIdStrings.OpenIdEndpointNotFound)); - } - } catch (ProtocolException ex) { - discoveryResultBuilder.Append("requests: [],"); - discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(ex.Message)); - } - - discoveryResultBuilder.Append("}"); - return discoveryResultBuilder.ToString(); - } - - /// <summary> - /// Creates the authentication requests for a given user-supplied Identifier. - /// </summary> - /// <param name="requests">The authentication requests to prepare.</param> - /// <returns> - /// A sequence of authentication requests, any one of which may be - /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>. - /// </returns> - private IEnumerable<IAuthenticationRequest> CreateRequestsCore(IEnumerable<IAuthenticationRequest> requests) { - ErrorUtilities.VerifyArgumentNotNull(requests, "requests"); // NO CODE CONTRACTS! (yield return used here) - - // Configure each generated request. - int reqIndex = 0; - foreach (var req in requests) { - req.SetUntrustedCallbackArgument("index", (reqIndex++).ToString(CultureInfo.InvariantCulture)); - - // If the ReturnToUrl was explicitly set, we'll need to reset our first parameter - if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)[AuthenticationRequest.UserSuppliedIdentifierParameterName])) { - Identifier userSuppliedIdentifier = ((AuthenticationRequest)req).Endpoint.UserSuppliedIdentifier; - req.SetUntrustedCallbackArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName, userSuppliedIdentifier.OriginalString); - } - - // Our javascript needs to let the user know which endpoint responded. So we force it here. - // This gives us the info even for 1.0 OPs and 2.0 setup_required responses. - req.SetUntrustedCallbackArgument(OPEndpointParameterName, req.Provider.Uri.AbsoluteUri); - req.SetUntrustedCallbackArgument(ClaimedIdParameterName, (string)req.ClaimedIdentifier ?? string.Empty); - - // Inform ourselves in return_to that we're in a popup or iframe. - req.SetUntrustedCallbackArgument(UIPopupCallbackKey, "1"); - - // We append a # at the end so that if the OP happens to support it, - // the OpenID response "query string" is appended after the hash rather than before, resulting in the - // browser being super-speedy in closing the popup window since it doesn't try to pull a newer version - // of the static resource down from the server merely because of a changed URL. - // http://www.nabble.com/Re:-Defining-how-OpenID-should-behave-with-fragments-in-the-return_to-url-p22694227.html - ////TODO: + OutgoingWebResponse response = this.RelyingParty.ProcessResponseFromPopup( + this.RelyingParty.Channel.GetRequestFromContext(), + callback); - yield return req; - } + response.Send(); } /// <summary> @@ -615,33 +458,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox, - /// and closes the calling popup window if applicable. - /// </summary> - /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including - /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param> - private void CallbackUserAgentMethod(string methodCall) { - Logger.OpenId.DebugFormat("Sending Javascript callback: {0}", methodCall); - Page.Response.Write(@"<html><body><script language='javascript'> - var inPopup = !window.frameElement; - var objSrc = inPopup ? window.opener : window.frameElement; -"); - - // Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable, - // so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already - // whether to call window.self.close() after the call. - string htmlFormat = @" if (inPopup) {{ - objSrc.{0}; - window.self.close(); - }} else {{ - objSrc.{0}; - }} -</script></body></html>"; - Page.Response.Write(string.Format(CultureInfo.InvariantCulture, htmlFormat, methodCall)); - Page.Response.End(); - } - - /// <summary> /// Sets the window.aspnetapppath variable on the user agent so that cookies can be set with the proper path. /// </summary> private void SetWebAppPathOnUserAgent() { diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js index 6faad56..4de5188 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js @@ -701,7 +701,7 @@ window.dnoa_internal.PositiveAssertion = function(uri) { }; window.dnoa_internal.clone = function(obj) { - if (obj === null || typeof (obj) != 'object') { + if (obj === null || typeof (obj) != 'object' || !isNaN(obj)) { // !isNaN catches Date objects return obj; } @@ -710,6 +710,10 @@ window.dnoa_internal.clone = function(obj) { temp[key] = window.dnoa_internal.clone(obj[key]); } + // Copy over some built-in methods that were not included in the above loop, + // but nevertheless may have been overridden. + temp.toString = window.dnoa_internal.clone(obj.toString); + return temp; }; @@ -732,7 +736,7 @@ window.dnoa_internal.clearExpiredPositiveAssertions = function() { var discoveryResult = window.dnoa_internal.discoveryResults[identifier]; if (typeof (discoveryResult) != 'object') { continue; } // skip functions for (var i = 0; i < discoveryResult.length; i++) { - if (discoveryResult[i].result === window.dnoa_internal.authSuccess) { + if (discoveryResult[i] && discoveryResult[i].result === window.dnoa_internal.authSuccess) { if (new Date() - discoveryResult[i].successReceived > window.dnoa_internal.maxPositiveAssertionLifetime) { // This positive assertion is too old, and may eventually be rejected by DNOA during verification. // Let's clear out the positive assertion so it can be renewed. diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs index 02df7a2..5090ecd 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs @@ -92,6 +92,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> internal const string ReturnToReceivingControlId = OpenIdUtilities.CustomParameterPrefix + "receiver"; + #region Protected internal callback parameter names + + /// <summary> + /// The callback parameter to use for recognizing when the callback is in a popup window or hidden iframe. + /// </summary> + protected internal const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup"; + + /// <summary> + /// The parameter name to include in the formulated auth request so that javascript can know whether + /// the OP advertises support for the UI extension. + /// </summary> + protected internal const string PopupUISupportedJSHint = OpenIdUtilities.CustomParameterPrefix + "popupUISupported"; + + #endregion + #region Property category constants /// <summary> @@ -111,18 +126,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { #endregion - #region Callback parameter names - - /// <summary> - /// The callback parameter to use for recognizing when the callback is in a popup window or hidden iframe. - /// </summary> - protected const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup"; - - /// <summary> - /// The parameter name to include in the formulated auth request so that javascript can know whether - /// the OP advertises support for the UI extension. - /// </summary> - protected const string PopupUISupportedJSHint = OpenIdUtilities.CustomParameterPrefix + "popupUISupported"; + #region Private callback parameter names /// <summary> /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property. @@ -293,10 +297,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// heavily trafficked web pages. /// </remarks> [Browsable(false)] - public OpenIdRelyingParty RelyingParty { + public virtual OpenIdRelyingParty RelyingParty { get { if (this.relyingParty == null) { this.relyingParty = this.CreateRelyingParty(); + this.ConfigureRelyingParty(this.relyingParty); this.relyingPartyOwned = true; } return this.relyingParty; @@ -353,6 +358,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } set { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(value)); + if (Page != null && !DesignMode) { // Validate new value by trying to construct a Realm object based on it. new Realm(OpenIdUtilities.GetResolvedRealm(this.Page, value, this.RelyingParty.Channel.GetRequestFromContext())); // throws an exception on failure. @@ -556,8 +563,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { protected internal virtual IEnumerable<IAuthenticationRequest> CreateRequests(Identifier identifier) { Contract.Requires<ArgumentNullException>(identifier != null); - // Delegate to a private method to keep 'yield return' and Code Contract separate. - return this.CreateRequestsCore(identifier); + // If this control is actually a member of another OpenID RP control, + // delegate creation of requests to the parent control. + var parentOwner = this.ParentControls.OfType<OpenIdRelyingPartyControlBase>().FirstOrDefault(); + if (parentOwner != null) { + return parentOwner.CreateRequests(identifier); + } else { + // Delegate to a private method to keep 'yield return' and Code Contract separate. + return this.CreateRequestsCore(identifier); + } } /// <summary> @@ -634,6 +648,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Notifies the user agent via an AJAX response of a completed authentication attempt. + /// </summary> + protected virtual void ScriptClosingPopupOrIFrame() { + this.RelyingParty.ProcessResponseFromPopup(); + } + + /// <summary> /// Called when the <see cref="Identifier"/> property is changed. /// </summary> protected virtual void OnIdentifierChanged() { @@ -769,29 +790,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Creates the relying party instance used to generate authentication requests. /// </summary> /// <returns>The instantiated relying party.</returns> - protected virtual OpenIdRelyingParty CreateRelyingParty() { - return this.CreateRelyingParty(true); + protected OpenIdRelyingParty CreateRelyingParty() { + IRelyingPartyApplicationStore store = this.Stateless ? null : DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore); + return this.CreateRelyingParty(store); } /// <summary> /// Creates the relying party instance used to generate authentication requests. /// </summary> - /// <param name="verifySignature"> - /// A value indicating whether message protections should be applied to the processed messages. - /// Use <c>false</c> to postpone verification to a later time without invalidating nonces. - /// </param> + /// <param name="store">The store to pass to the relying party constructor.</param> /// <returns>The instantiated relying party.</returns> - protected virtual OpenIdRelyingParty CreateRelyingParty(bool verifySignature) { - IRelyingPartyApplicationStore store = this.Stateless ? null : DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore); - var rp = verifySignature ? new OpenIdRelyingParty(store) : OpenIdRelyingParty.CreateNonVerifying(); + protected virtual OpenIdRelyingParty CreateRelyingParty(IRelyingPartyApplicationStore store) { + return new OpenIdRelyingParty(store); + } + + /// <summary> + /// Configures the relying party. + /// </summary> + /// <param name="relyingParty">The relying party.</param> + protected virtual void ConfigureRelyingParty(OpenIdRelyingParty relyingParty) { + Contract.Requires<ArgumentNullException>(relyingParty != null); // Only set RequireSsl to true, as we don't want to override // a .config setting of true with false. if (this.RequireSsl) { - rp.SecuritySettings.RequireSsl = true; + relyingParty.SecuritySettings.RequireSsl = true; } - - return rp; } /// <summary> @@ -810,7 +834,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { case PopupBehavior.Always: return true; case PopupBehavior.IfProviderSupported: - return request.Provider.IsExtensionSupported<UIRequest>(); + return request.DiscoveryResult.IsExtensionSupported<UIRequest>(); default: throw ErrorUtilities.ThrowInternal("Unexpected value for Popup property."); } @@ -841,21 +865,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Wires the popup window to close itself and pass the authentication result to the parent window. - /// </summary> - protected virtual void ScriptClosingPopupOrIFrame() { - StringBuilder startupScript = new StringBuilder(); - startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL);"); - startupScript.AppendLine("window.close();"); - - this.Page.ClientScript.RegisterStartupScript(typeof(OpenIdRelyingPartyControlBase), "loginPopupClose", startupScript.ToString(), true); - - // TODO: alternately we should probably take over rendering this page here to avoid - // a lot of unnecessary work on the server and possible momentary display of the - // page in the popup window. - } - - /// <summary> /// Creates the identifier-persisting cookie, either for saving or deleting. /// </summary> /// <param name="response">The positive authentication response; or <c>null</c> to clear the cookie.</param> @@ -937,9 +946,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // Inform ourselves in return_to that we're in a popup. req.SetUntrustedCallbackArgument(UIPopupCallbackKey, "1"); - if (req.Provider.IsExtensionSupported<UIRequest>()) { + if (req.DiscoveryResult.IsExtensionSupported<UIRequest>()) { // Inform the OP that we'll be using a popup window consistent with the UI extension. - req.AddExtension(new UIRequest()); + // But beware that the extension MAY have already been added if we're using + // the OpenIdAjaxRelyingParty class. + if (!((AuthenticationRequest)req).Extensions.OfType<UIRequest>().Any()) { + req.AddExtension(new UIRequest()); + } // Provide a hint for the client javascript about whether the OP supports the UI extension. // This is so the window can be made the correct size for the extension. @@ -1029,67 +1042,5 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return false; } - - /// <summary> - /// An authentication request comparer that judges equality solely on the OP endpoint hostname. - /// </summary> - private class DuplicateRequestedHostsComparer : IEqualityComparer<IAuthenticationRequest> { - /// <summary> - /// The singleton instance of this comparer. - /// </summary> - private static IEqualityComparer<IAuthenticationRequest> instance = new DuplicateRequestedHostsComparer(); - - /// <summary> - /// Prevents a default instance of the <see cref="DuplicateRequestedHostsComparer"/> class from being created. - /// </summary> - private DuplicateRequestedHostsComparer() { - } - - /// <summary> - /// Gets the singleton instance of this comparer. - /// </summary> - internal static IEqualityComparer<IAuthenticationRequest> Instance { - get { return instance; } - } - - #region IEqualityComparer<IAuthenticationRequest> Members - - /// <summary> - /// Determines whether the specified objects are equal. - /// </summary> - /// <param name="x">The first object to compare.</param> - /// <param name="y">The second object to compare.</param> - /// <returns> - /// true if the specified objects are equal; otherwise, false. - /// </returns> - public bool Equals(IAuthenticationRequest x, IAuthenticationRequest y) { - if (x == null && y == null) { - return true; - } - - if (x == null || y == null) { - return false; - } - - // We'll distinguish based on the host name only, which - // admittedly is only a heuristic, but if we remove one that really wasn't a duplicate, well, - // this multiple OP attempt thing was just a convenience feature anyway. - return string.Equals(x.Provider.Uri.Host, y.Provider.Uri.Host, StringComparison.OrdinalIgnoreCase); - } - - /// <summary> - /// Returns a hash code for the specified object. - /// </summary> - /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param> - /// <returns>A hash code for the specified object.</returns> - /// <exception cref="T:System.ArgumentNullException"> - /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null. - /// </exception> - public int GetHashCode(IAuthenticationRequest obj) { - return obj.Provider.Uri.Host.GetHashCode(); - } - - #endregion - } } } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs index e93383d..b7a54eb 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs @@ -81,11 +81,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { private HiddenField positiveAssertionField; /// <summary> - /// A field to store the value to set on the <see cref="textBox"/> control after it's created. - /// </summary> - private bool downloadYuiLibrary = OpenIdAjaxTextBox.DownloadYahooUILibraryDefault; - - /// <summary> /// Initializes a new instance of the <see cref="OpenIdSelector"/> class. /// </summary> public OpenIdSelector() { @@ -102,6 +97,50 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public event EventHandler<TokenProcessingErrorEventArgs> TokenProcessingError; /// <summary> + /// Gets the text box where applicable. + /// </summary> + public OpenIdAjaxTextBox TextBox { + get { + this.EnsureChildControlsAreCreatedSafe(); + return this.textBox; + } + } + + /// <summary> + /// Gets or sets the maximum number of OpenID Providers to simultaneously try to authenticate with. + /// </summary> + [Browsable(true), DefaultValue(OpenIdAjaxTextBox.ThrottleDefault), Category(BehaviorCategory)] + [Description("The maximum number of OpenID Providers to simultaneously try to authenticate with.")] + public int Throttle { + get { + this.EnsureChildControlsAreCreatedSafe(); + return this.textBox.Throttle; + } + + set { + this.EnsureChildControlsAreCreatedSafe(); + this.textBox.Throttle = value; + } + } + + /// <summary> + /// Gets or sets the time duration for the AJAX control to wait for an OP to respond before reporting failure to the user. + /// </summary> + [Browsable(true), DefaultValue(typeof(TimeSpan), "00:00:08"), Category(BehaviorCategory)] + [Description("The time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.")] + public TimeSpan Timeout { + get { + this.EnsureChildControlsAreCreatedSafe(); + return this.textBox.Timeout; + } + + set { + this.EnsureChildControlsAreCreatedSafe(); + this.textBox.Timeout = value; + } + } + + /// <summary> /// Gets or sets the tool tip text that appears on the green checkmark when authentication succeeds. /// </summary> [Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault), Localizable(true), Category(AppearanceCategory)] @@ -126,19 +165,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { [Description("Whether a split button will be used for the \"log in\" when the user provides an identifier that delegates to more than one Provider.")] public bool DownloadYahooUILibrary { get { - return this.textBox != null ? this.textBox.DownloadYahooUILibrary : this.downloadYuiLibrary; + this.EnsureChildControlsAreCreatedSafe(); + return this.textBox.DownloadYahooUILibrary; } set { - // We don't just call EnsureChildControls() and then set the property on - // this.textBox itself because (apparently) setting this property in the ASPX - // page and thus calling this EnsureID() via EnsureChildControls() this early - // results in no ID. - if (this.textBox != null) { - this.textBox.DownloadYahooUILibrary = value; - } else { - this.downloadYuiLibrary = value; - } + this.EnsureChildControlsAreCreatedSafe(); + this.textBox.DownloadYahooUILibrary = value; } } @@ -207,10 +240,37 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering. /// </summary> protected override void CreateChildControls() { + this.EnsureChildControlsAreCreatedSafe(); + base.CreateChildControls(); + + // Now do the ID specific work. this.EnsureID(); ErrorUtilities.VerifyInternal(!string.IsNullOrEmpty(this.UniqueID), "Control.EnsureID() failed to give us a unique ID. Try setting an ID on the OpenIdSelector control. But please also file this bug with the project owners."); + this.Controls.Add(this.textBox); + + this.positiveAssertionField.ID = this.ID + AuthDataFormKeySuffix; + this.Controls.Add(this.positiveAssertionField); + } + + /// <summary> + /// Ensures that the child controls have been built, but doesn't set control + /// properties that require executing <see cref="Control.EnsureID"/> in order to avoid + /// certain initialization order problems. + /// </summary> + /// <remarks> + /// We don't just call EnsureChildControls() and then set the property on + /// this.textBox itself because (apparently) setting this property in the ASPX + /// page and thus calling this EnsureID() via EnsureChildControls() this early + /// results in no ID. + /// </remarks> + protected virtual void EnsureChildControlsAreCreatedSafe() { + // If we've already created the child controls, this method is a no-op. + if (this.textBox != null) { + return; + } + var selectorButton = this.Buttons.OfType<SelectorInfoCardButton>().FirstOrDefault(); if (selectorButton != null) { var selector = selectorButton.InfoCardSelector; @@ -225,12 +285,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.textBox.ID = "openid_identifier"; this.textBox.HookFormSubmit = false; this.textBox.ShowLogOnPostBackButton = true; - this.textBox.DownloadYahooUILibrary = this.downloadYuiLibrary; - this.Controls.Add(this.textBox); this.positiveAssertionField = new HiddenField(); - this.positiveAssertionField.ID = this.ID + AuthDataFormKeySuffix; - this.Controls.Add(this.positiveAssertionField); } /// <summary> @@ -254,11 +310,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.EnsureValidButtons(); var css = new HtmlLink(); - css.Href = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedStylesheetResourceName); - css.Attributes["rel"] = "stylesheet"; - css.Attributes["type"] = "text/css"; - ErrorUtilities.VerifyHost(this.Page.Header != null, OpenIdStrings.HeadTagMustIncludeRunatServer); - this.Page.Header.Controls.AddAt(0, css); // insert at top so host page can override + try { + css.Href = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedStylesheetResourceName); + css.Attributes["rel"] = "stylesheet"; + css.Attributes["type"] = "text/css"; + ErrorUtilities.VerifyHost(this.Page.Header != null, OpenIdStrings.HeadTagMustIncludeRunatServer); + this.Page.Header.Controls.AddAt(0, css); // insert at top so host page can override + } catch { + css.Dispose(); + throw; + } // Import the .js file where most of the code is. this.Page.ClientScript.RegisterClientScriptResource(typeof(OpenIdSelector), EmbeddedScriptResourceName); @@ -288,6 +349,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param> protected override void Render(HtmlTextWriter writer) { + Contract.Assume(writer != null, "Missing contract"); writer.AddAttribute(HtmlTextWriterAttribute.Class, "OpenIdProviders"); writer.RenderBeginTag(HtmlTextWriterTag.Ul); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js index c58e06e..297ea23 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js @@ -178,15 +178,19 @@ $(function() { ajaxbox.focus(); } }); + + $(ajaxbox.form).keydown(function(e) { + if (e.keyCode == $.ui.keyCode.ENTER) { + // we do NOT want to submit the form on ENTER. + e.preventDefault(); + } + }); } // Make popup window close on escape (the dialog style is already taken care of) $(document).keydown(function(e) { if (e.keyCode == $.ui.keyCode.ESCAPE) { window.close(); - } else if (e.keyCode == $.ui.keyCode.ENTER) { - // we do NOT want to submit the form on ENTER. - e.preventDefault(); } }); });
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs index 4d635fb..335b435 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdTextBox.EmbeddedLogoResourceName, "image/gif")] +[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdTextBox.EmbeddedLogoResourceName, "image/png")] #pragma warning disable 0809 // marking inherited, unsupported properties as obsolete to discourage their use @@ -45,7 +45,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The name of the manifest stream containing the /// OpenID logo that is placed inside the text box. /// </summary> - internal const string EmbeddedLogoResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.openid_login.gif"; + internal const string EmbeddedLogoResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.openid_login.png"; /// <summary> /// Default value for <see cref="TabIndex"/> property. @@ -584,6 +584,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param> protected override void Render(HtmlTextWriter writer) { + Contract.Assume(writer != null, "Missing contract."); + if (this.ShowLogo) { string logoUrl = Page.ClientScript.GetWebResourceUrl( typeof(OpenIdTextBox), EmbeddedLogoResourceName); @@ -625,6 +627,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// true if the server control's state changes as a result of the postback; otherwise, false. /// </returns> protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) { + Contract.Assume(postCollection != null, "Missing contract"); + // If the control was temporarily hidden, it won't be in the Form data, // and we'll just implicitly keep the last Text setting. if (postCollection[this.Name] != null) { diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs index 6e7d7ef..5cfa191 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs @@ -26,7 +26,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Information about the OP endpoint that issued this assertion. /// </summary> - private readonly ProviderEndpointDescription provider; + private readonly IProviderEndpoint provider; /// <summary> /// Initializes a new instance of the <see cref="PositiveAnonymousResponse"/> class. diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs index 695aa1e..3e2298c 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs @@ -28,7 +28,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { : base(response) { Contract.Requires<ArgumentNullException>(relyingParty != null); - this.Endpoint = ServiceEndpoint.CreateForClaimedIdentifier( + this.Endpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier( this.Response.ClaimedIdentifier, this.Response.GetReturnToArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName), this.Response.LocalIdentifier, @@ -114,7 +114,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// the claimed identifier to avoid a Provider asserting an Identifier /// for which it has no authority. /// </remarks> - internal ServiceEndpoint Endpoint { get; private set; } + internal IdentifierDiscoveryResult Endpoint { get; private set; } /// <summary> /// Gets the positive assertion response message. @@ -146,6 +146,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } } + // Check whether this particular identifier presents a problem with HTTP discovery + // due to limitations in the .NET Uri class. + UriIdentifier claimedIdUri = claimedId as UriIdentifier; + if (claimedIdUri != null && claimedIdUri.ProblematicNormalization) { + ErrorUtilities.VerifyProtocol(relyingParty.SecuritySettings.AllowApproximateIdentifierDiscovery, OpenIdStrings.ClaimedIdentifierDefiesDotNetNormalization); + Logger.OpenId.WarnFormat("Positive assertion for claimed identifier {0} cannot be precisely verified under partial trust hosting due to .NET limitation. An approximate verification will be attempted.", claimedId); + } + // While it LOOKS like we're performing discovery over HTTP again // Yadis.IdentifierDiscoveryCachePolicy is set to HttpRequestCacheLevel.CacheIfAvailable // which means that the .NET runtime is caching our discoveries for us. This turns out @@ -155,7 +163,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // is signed by the RP before it's considered reliable. In 1.x stateless mode, this RP // doesn't (and can't) sign its own return_to URL, so its cached discovery information // is merely a hint that must be verified by performing discovery again here. - var discoveryResults = claimedId.Discover(relyingParty.WebRequestHandler); + var discoveryResults = relyingParty.Discover(claimedId); ErrorUtilities.VerifyProtocol( discoveryResults.Contains(this.Endpoint), OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery, diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs index ff29498..a7686c5 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs @@ -16,11 +16,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> public sealed class RelyingPartySecuritySettings : SecuritySettings { /// <summary> + /// The default value for the <see cref="ProtectDownlevelReplayAttacks"/> property. + /// </summary> + internal const bool ProtectDownlevelReplayAttacksDefault = true; + + /// <summary> /// Initializes a new instance of the <see cref="RelyingPartySecuritySettings"/> class. /// </summary> internal RelyingPartySecuritySettings() : base(false) { this.PrivateSecretMaximumAge = TimeSpan.FromDays(7); + this.ProtectDownlevelReplayAttacks = ProtectDownlevelReplayAttacksDefault; + this.AllowApproximateIdentifierDiscovery = true; } /// <summary> @@ -111,11 +118,51 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public bool RequireAssociation { get; set; } /// <summary> + /// Gets or sets a value indicating whether identifiers that are both OP Identifiers and Claimed Identifiers + /// should ever be recognized as claimed identifiers. + /// </summary> + /// <value> + /// The default value is <c>false</c>, per the OpenID 2.0 spec. + /// </value> + /// <remarks> + /// OpenID 2.0 sections 7.3.2.2 and 11.2 specify that OP Identifiers never be recognized as Claimed Identifiers. + /// However, for some scenarios it may be desirable for an RP to override this behavior and allow this. + /// The security ramifications of setting this property to <c>true</c> have not been fully explored and + /// therefore this setting should only be changed with caution. + /// </remarks> + public bool AllowDualPurposeIdentifiers { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether certain Claimed Identifiers that exploit + /// features that .NET does not have the ability to send exact HTTP requests for will + /// still be allowed by using an approximate HTTP request. + /// </summary> + /// <value> + /// The default value is <c>true</c>. + /// </value> + public bool AllowApproximateIdentifierDiscovery { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether special measures are taken to + /// protect users from replay attacks when those users' identities are hosted + /// by OpenID 1.x Providers. + /// </summary> + /// <value>The default value is <c>true</c>.</value> + /// <remarks> + /// <para>Nonces for protection against replay attacks were not mandated + /// by OpenID 1.x, which leaves users open to replay attacks.</para> + /// <para>This feature works by adding a signed nonce to the authentication request. + /// This might increase the request size beyond what some OpenID 1.1 Providers + /// (such as Blogger) are capable of handling.</para> + /// </remarks> + internal bool ProtectDownlevelReplayAttacks { get; set; } + + /// <summary> /// Filters out any disallowed endpoints. /// </summary> /// <param name="endpoints">The endpoints discovered on an Identifier.</param> /// <returns>A sequence of endpoints that satisfy all security requirements.</returns> - internal IEnumerable<ServiceEndpoint> FilterEndpoints(IEnumerable<ServiceEndpoint> endpoints) { + internal IEnumerable<IdentifierDiscoveryResult> FilterEndpoints(IEnumerable<IdentifierDiscoveryResult> endpoints) { return endpoints .Where(se => !this.RejectDelegatingIdentifiers || se.ClaimedIdentifier == se.ProviderLocalIdentifier) .Where(se => !this.RequireDirectedIdentity || se.ClaimedIdentifier == se.Protocol.ClaimedIdentifierForOPIdentifier); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs index 15b6ca7..ac4dcbf 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId.RelyingParty { + using System; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Drawing.Design; @@ -24,6 +25,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Initializes a new instance of the <see cref="SelectorOpenIdButton"/> class. + /// </summary> + /// <param name="imageUrl">The image to display on the button.</param> + public SelectorOpenIdButton(string imageUrl) + : this() { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); + + this.Image = imageUrl; + } + + /// <summary> /// Gets or sets the path to the image to display on the button's surface. /// </summary> /// <value>The virtual path to the image.</value> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs index 3a05287..2195e73 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId.RelyingParty { + using System; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Drawing.Design; @@ -25,6 +26,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Initializes a new instance of the <see cref="SelectorProviderButton"/> class. + /// </summary> + /// <param name="providerIdentifier">The OP Identifier.</param> + /// <param name="imageUrl">The image to display on the button.</param> + public SelectorProviderButton(Identifier providerIdentifier, string imageUrl) + : this() { + Contract.Requires<ArgumentNullException>(providerIdentifier != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); + + this.OPIdentifier = providerIdentifier; + this.Image = imageUrl; + } + + /// <summary> /// Gets or sets the path to the image to display on the button's surface. /// </summary> /// <value>The virtual path to the image.</value> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs index 912b8f4..678f69a 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs @@ -6,6 +6,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System; + using System.Collections.ObjectModel; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; /// <summary> @@ -13,7 +15,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// assertions (particularly unsolicited ones) are received from OP endpoints that /// are deemed permissible by the host RP. /// </summary> - internal class SimpleXrdsProviderEndpoint : IXrdsProviderEndpoint { + internal class SimpleXrdsProviderEndpoint : IProviderEndpoint { /// <summary> /// Initializes a new instance of the <see cref="SimpleXrdsProviderEndpoint"/> class. /// </summary> @@ -23,29 +25,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.Version = positiveAssertion.Version; } - #region IXrdsProviderEndpoint Properties - - /// <summary> - /// Gets the priority associated with this service that may have been given - /// in the XRDS document. - /// </summary> - public int? ServicePriority { - get { return null; } - } - - /// <summary> - /// Gets the priority associated with the service endpoint URL. - /// </summary> - /// <remarks> - /// When sorting by priority, this property should be considered second after - /// <see cref="ServicePriority"/>. - /// </remarks> - public int? UriPriority { - get { return null; } - } - - #endregion - #region IProviderEndpoint Members /// <summary> @@ -56,7 +35,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Gets the URL that the OpenID Provider receives authentication requests at. /// </summary> - /// <value></value> public Uri Uri { get; private set; } /// <summary> @@ -73,8 +51,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The only way to be sure of support for a given extension is to include /// the extension in the request and see if a response comes back for that extension. /// </remarks> - public bool IsExtensionSupported<T>() where T : DotNetOpenAuth.OpenId.Messages.IOpenIdMessageExtension, new() { - throw new NotSupportedException(); + bool IProviderEndpoint.IsExtensionSupported<T>() { + throw new NotImplementedException(); } /// <summary> @@ -91,23 +69,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The only way to be sure of support for a given extension is to include /// the extension in the request and see if a response comes back for that extension. /// </remarks> - public bool IsExtensionSupported(Type extensionType) { - throw new NotSupportedException(); - } - - #endregion - - #region IXrdsProviderEndpoint Methods - - /// <summary> - /// Checks for the presence of a given Type URI in an XRDS service. - /// </summary> - /// <param name="typeUri">The type URI to check for.</param> - /// <returns> - /// <c>true</c> if the service type uri is present; <c>false</c> otherwise. - /// </returns> - public bool IsTypeUriPresent(string typeUri) { - throw new NotSupportedException(); + bool IProviderEndpoint.IsExtensionSupported(Type extensionType) { + throw new NotImplementedException(); } #endregion diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/openid_login.gif b/src/DotNetOpenAuth/OpenId/RelyingParty/openid_login.gif Binary files differdeleted file mode 100644 index cde836c..0000000 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/openid_login.gif +++ /dev/null diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/openid_login.png b/src/DotNetOpenAuth/OpenId/RelyingParty/openid_login.png Binary files differnew file mode 100644 index 0000000..caebd58 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/openid_login.png diff --git a/src/DotNetOpenAuth/OpenId/UriDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/UriDiscoveryService.cs new file mode 100644 index 0000000..7d17fd9 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/UriDiscoveryService.cs @@ -0,0 +1,139 @@ +//----------------------------------------------------------------------- +// <copyright file="UriDiscoveryService.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 System.Text.RegularExpressions; + using System.Web.UI.HtmlControls; + using System.Xml; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.RelyingParty; + using DotNetOpenAuth.Xrds; + using DotNetOpenAuth.Yadis; + + /// <summary> + /// The discovery service for URI identifiers. + /// </summary> + public class UriDiscoveryService : IIdentifierDiscoveryService { + /// <summary> + /// Initializes a new instance of the <see cref="UriDiscoveryService"/> class. + /// </summary> + public UriDiscoveryService() { + } + + #region IDiscoveryService Members + + /// <summary> + /// Performs discovery on the specified identifier. + /// </summary> + /// <param name="identifier">The identifier to perform discovery on.</param> + /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> + /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> + /// <returns> + /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. + /// </returns> + public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { + abortDiscoveryChain = false; + var uriIdentifier = identifier as UriIdentifier; + if (uriIdentifier == null) { + return Enumerable.Empty<IdentifierDiscoveryResult>(); + } + + var endpoints = new List<IdentifierDiscoveryResult>(); + + // Attempt YADIS discovery + DiscoveryResult yadisResult = Yadis.Discover(requestHandler, uriIdentifier, identifier.IsDiscoverySecureEndToEnd); + if (yadisResult != null) { + if (yadisResult.IsXrds) { + try { + XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText); + var xrdsEndpoints = xrds.XrdElements.CreateServiceEndpoints(yadisResult.NormalizedUri, uriIdentifier); + + // Filter out insecure endpoints if high security is required. + if (uriIdentifier.IsDiscoverySecureEndToEnd) { + xrdsEndpoints = xrdsEndpoints.Where(se => se.ProviderEndpoint.IsTransportSecure()); + } + endpoints.AddRange(xrdsEndpoints); + } catch (XmlException ex) { + Logger.Yadis.Error("Error while parsing the XRDS document. Falling back to HTML discovery.", ex); + } + } + + // Failing YADIS discovery of an XRDS document, we try HTML discovery. + if (endpoints.Count == 0) { + yadisResult.TryRevertToHtmlResponse(); + var htmlEndpoints = new List<IdentifierDiscoveryResult>(DiscoverFromHtml(yadisResult.NormalizedUri, uriIdentifier, yadisResult.ResponseText)); + if (htmlEndpoints.Any()) { + Logger.Yadis.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count); + Logger.Yadis.Debug(htmlEndpoints.ToStringDeferred(true)); + endpoints.AddRange(htmlEndpoints.Where(ep => !uriIdentifier.IsDiscoverySecureEndToEnd || ep.ProviderEndpoint.IsTransportSecure())); + if (endpoints.Count == 0) { + Logger.Yadis.Info("No HTML discovered endpoints met the security requirements."); + } + } else { + Logger.Yadis.Debug("HTML discovery failed to find any endpoints."); + } + } else { + Logger.Yadis.Debug("Skipping HTML discovery because XRDS contained service endpoints."); + } + } + return endpoints; + } + + #endregion + + /// <summary> + /// Searches HTML for the HEAD META tags that describe OpenID provider services. + /// </summary> + /// <param name="claimedIdentifier">The final URL that provided this HTML document. + /// This may not be the same as (this) userSuppliedIdentifier if the + /// userSuppliedIdentifier pointed to a 301 Redirect.</param> + /// <param name="userSuppliedIdentifier">The user supplied identifier.</param> + /// <param name="html">The HTML that was downloaded and should be searched.</param> + /// <returns> + /// A sequence of any discovered ServiceEndpoints. + /// </returns> + private static IEnumerable<IdentifierDiscoveryResult> DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html) { + var linkTags = new List<HtmlLink>(HtmlParser.HeadTags<HtmlLink>(html)); + foreach (var protocol in Protocol.AllPracticalVersions) { + // rel attributes are supposed to be interpreted with case INsensitivity, + // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes) + var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase)); + if (serverLinkTag == null) { + continue; + } + + Uri providerEndpoint = null; + if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) { + // See if a LocalId tag of the discovered version exists + Identifier providerLocalIdentifier = null; + var delegateLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase)); + if (delegateLinkTag != null) { + if (Identifier.IsValid(delegateLinkTag.Href)) { + providerLocalIdentifier = delegateLinkTag.Href; + } else { + Logger.Yadis.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", delegateLinkTag.Href); + continue; // skip to next version + } + } + + // Choose the TypeURI to match the OpenID version detected. + string[] typeURIs = { protocol.ClaimedIdentifierServiceTypeURI }; + yield return IdentifierDiscoveryResult.CreateForClaimedIdentifier( + claimedIdentifier, + userSuppliedIdentifier, + providerLocalIdentifier, + new ProviderEndpointDescription(providerEndpoint, typeURIs), + (int?)null, + (int?)null); + } + } + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs index 28d8b37..639ff57 100644 --- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs @@ -10,6 +10,9 @@ namespace DotNetOpenAuth.OpenId { using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; + using System.Reflection; + using System.Security; + using System.Text; using System.Text.RegularExpressions; using System.Web.UI.HtmlControls; using System.Xml; @@ -30,6 +33,68 @@ namespace DotNetOpenAuth.OpenId { private static readonly string[] allowedSchemes = { "http", "https" }; /// <summary> + /// The special scheme to use for HTTP URLs that should not have their paths compressed. + /// </summary> + private static NonPathCompressingUriParser roundTrippingHttpParser = new NonPathCompressingUriParser(Uri.UriSchemeHttp); + + /// <summary> + /// The special scheme to use for HTTPS URLs that should not have their paths compressed. + /// </summary> + private static NonPathCompressingUriParser roundTrippingHttpsParser = new NonPathCompressingUriParser(Uri.UriSchemeHttps); + + /// <summary> + /// The special scheme to use for HTTP URLs that should not have their paths compressed. + /// </summary> + private static NonPathCompressingUriParser publishableHttpParser = new NonPathCompressingUriParser(Uri.UriSchemeHttp); + + /// <summary> + /// The special scheme to use for HTTPS URLs that should not have their paths compressed. + /// </summary> + private static NonPathCompressingUriParser publishableHttpsParser = new NonPathCompressingUriParser(Uri.UriSchemeHttps); + + /// <summary> + /// A value indicating whether scheme substitution is being used to workaround + /// .NET path compression that invalidates some OpenIDs that have trailing periods + /// in one of their path segments. + /// </summary> + private static bool schemeSubstitution; + + /// <summary> + /// Initializes static members of the <see cref="UriIdentifier"/> class. + /// </summary> + /// <remarks> + /// This method attempts to workaround the .NET Uri class parsing bug described here: + /// https://connect.microsoft.com/VisualStudio/feedback/details/386695/system-uri-incorrectly-strips-trailing-dots?wa=wsignin1.0#tabs + /// since some identifiers (like some of the pseudonymous identifiers from Yahoo) include path segments + /// that end with periods, which the Uri class will typically trim off. + /// </remarks> + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Some things just can't be done in a field initializer.")] + static UriIdentifier() { + // Our first attempt to handle trailing periods in path segments is to leverage + // full trust if it's available to rewrite the rules. + // In fact this is the ONLY way in .NET 3.5 (and arguably in .NET 4.0) to send + // outbound HTTP requests with trailing periods, so it's the only way to perform + // discovery on such an identifier. + try { + UriParser.Register(roundTrippingHttpParser, "dnoarthttp", 80); + UriParser.Register(roundTrippingHttpsParser, "dnoarthttps", 443); + UriParser.Register(publishableHttpParser, "dnoahttp", 80); + UriParser.Register(publishableHttpsParser, "dnoahttps", 443); + roundTrippingHttpParser.Initialize(false); + roundTrippingHttpsParser.Initialize(false); + publishableHttpParser.Initialize(true); + publishableHttpsParser.Initialize(true); + schemeSubstitution = true; + Logger.OpenId.Debug(".NET Uri class path compression overridden."); + Reporting.RecordFeatureUse("FullTrust"); + } catch (SecurityException) { + // We must be running in partial trust. Nothing more we can do. + Logger.OpenId.Warn("Unable to coerce .NET to stop compressing URI paths due to partial trust limitations. Some URL identifiers may be unable to complete login."); + Reporting.RecordFeatureUse("PartialTrust"); + } + } + + /// <summary> /// Initializes a new instance of the <see cref="UriIdentifier"/> class. /// </summary> /// <param name="uri">The value this identifier will represent.</param> @@ -62,7 +127,8 @@ namespace DotNetOpenAuth.OpenId { /// Initializes a new instance of the <see cref="UriIdentifier"/> class. /// </summary> /// <param name="uri">The value this identifier will represent.</param> - internal UriIdentifier(Uri uri) : this(uri, false) { + internal UriIdentifier(Uri uri) + : this(uri, false) { } /// <summary> @@ -73,7 +139,13 @@ namespace DotNetOpenAuth.OpenId { internal UriIdentifier(Uri uri, bool requireSslDiscovery) : base(uri != null ? uri.OriginalString : null, requireSslDiscovery) { Contract.Requires<ArgumentNullException>(uri != null); - if (!TryCanonicalize(new UriBuilder(uri), out uri)) { + + string uriAsString = uri.OriginalString; + if (schemeSubstitution) { + uriAsString = NormalSchemeToSpecialRoundTrippingScheme(uriAsString); + } + + if (!TryCanonicalize(uriAsString, out uri)) { throw new UriFormatException(); } if (requireSslDiscovery && uri.Scheme != Uri.UriSchemeHttps) { @@ -96,6 +168,26 @@ namespace DotNetOpenAuth.OpenId { internal bool SchemeImplicitlyPrepended { get; private set; } /// <summary> + /// Gets a value indicating whether this Identifier has characters or patterns that + /// the <see cref="Uri"/> class normalizes away and invalidating the Identifier. + /// </summary> + internal bool ProblematicNormalization { + get { + if (schemeSubstitution) { + // With full trust, we have no problematic URIs + return false; + } + + var simpleUri = new SimpleUri(this.OriginalString); + if (simpleUri.Path.EndsWith(".", StringComparison.Ordinal) || simpleUri.Path.Contains("./")) { + return true; + } + + return false; + } + } + + /// <summary> /// Converts a <see cref="UriIdentifier"/> instance to a <see cref="Uri"/> instance. /// </summary> /// <param name="identifier">The identifier to convert to an ordinary <see cref="Uri"/> instance.</param> @@ -137,7 +229,12 @@ namespace DotNetOpenAuth.OpenId { if (other == null) { return false; } - return this.Uri == other.Uri; + + if (this.ProblematicNormalization || other.ProblematicNormalization) { + return new SimpleUri(this.OriginalString).Equals(new SimpleUri(other.OriginalString)); + } else { + return this.Uri == other.Uri; + } } /// <summary> @@ -157,16 +254,13 @@ namespace DotNetOpenAuth.OpenId { /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. /// </returns> public override string ToString() { - return Uri.AbsoluteUri; - } -#if UNUSED - static bool TryCanonicalize(string uri, out string canonicalUri) { - Uri normalizedUri; - bool result = TryCanonicalize(uri, out normalizedUri); - canonicalUri = normalizedUri.AbsoluteUri; - return result; + if (this.ProblematicNormalization) { + return new SimpleUri(this.OriginalString).ToString(); + } else { + return this.Uri.AbsoluteUri; + } } -#endif + /// <summary> /// Determines whether a URI is a valid OpenID Identifier (of any kind). /// </summary> @@ -207,54 +301,6 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Performs discovery on the Identifier. - /// </summary> - /// <param name="requestHandler">The web request handler to use for discovery.</param> - /// <returns> - /// An initialized structure containing the discovered provider endpoint information. - /// </returns> - internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { - List<ServiceEndpoint> endpoints = new List<ServiceEndpoint>(); - - // Attempt YADIS discovery - DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this, IsDiscoverySecureEndToEnd); - if (yadisResult != null) { - if (yadisResult.IsXrds) { - try { - XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText); - var xrdsEndpoints = xrds.CreateServiceEndpoints(yadisResult.NormalizedUri, this); - - // Filter out insecure endpoints if high security is required. - if (IsDiscoverySecureEndToEnd) { - xrdsEndpoints = xrdsEndpoints.Where(se => se.IsSecure); - } - endpoints.AddRange(xrdsEndpoints); - } catch (XmlException ex) { - Logger.Yadis.Error("Error while parsing the XRDS document. Falling back to HTML discovery.", ex); - } - } - - // Failing YADIS discovery of an XRDS document, we try HTML discovery. - if (endpoints.Count == 0) { - var htmlEndpoints = new List<ServiceEndpoint>(DiscoverFromHtml(yadisResult.NormalizedUri, this, yadisResult.ResponseText)); - if (htmlEndpoints.Any()) { - Logger.Yadis.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count); - Logger.Yadis.Debug(htmlEndpoints.ToStringDeferred(true)); - endpoints.AddRange(htmlEndpoints.Where(ep => !IsDiscoverySecureEndToEnd || ep.IsSecure)); - if (endpoints.Count == 0) { - Logger.Yadis.Info("No HTML discovered endpoints met the security requirements."); - } - } else { - Logger.Yadis.Debug("HTML discovery failed to find any endpoints."); - } - } else { - Logger.Yadis.Debug("Skipping HTML discovery because XRDS contained service endpoints."); - } - } - return endpoints; - } - - /// <summary> /// Returns an <see cref="Identifier"/> that has no URI fragment. /// Quietly returns the original <see cref="Identifier"/> if it is not /// a <see cref="UriIdentifier"/> or no fragment exists. @@ -270,9 +316,7 @@ namespace DotNetOpenAuth.OpenId { } // Strip the fragment. - UriBuilder builder = new UriBuilder(Uri); - builder.Fragment = null; - return builder.Uri; + return new UriIdentifier(this.OriginalString.Substring(0, this.OriginalString.IndexOf('#'))); } /// <summary> @@ -318,54 +362,6 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Searches HTML for the HEAD META tags that describe OpenID provider services. - /// </summary> - /// <param name="claimedIdentifier">The final URL that provided this HTML document. - /// This may not be the same as (this) userSuppliedIdentifier if the - /// userSuppliedIdentifier pointed to a 301 Redirect.</param> - /// <param name="userSuppliedIdentifier">The user supplied identifier.</param> - /// <param name="html">The HTML that was downloaded and should be searched.</param> - /// <returns> - /// A sequence of any discovered ServiceEndpoints. - /// </returns> - private static IEnumerable<ServiceEndpoint> DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html) { - var linkTags = new List<HtmlLink>(HtmlParser.HeadTags<HtmlLink>(html)); - foreach (var protocol in Protocol.AllPracticalVersions) { - // rel attributes are supposed to be interpreted with case INsensitivity, - // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes) - var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase)); - if (serverLinkTag == null) { - continue; - } - - Uri providerEndpoint = null; - if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) { - // See if a LocalId tag of the discovered version exists - Identifier providerLocalIdentifier = null; - var delegateLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase)); - if (delegateLinkTag != null) { - if (Identifier.IsValid(delegateLinkTag.Href)) { - providerLocalIdentifier = delegateLinkTag.Href; - } else { - Logger.Yadis.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", delegateLinkTag.Href); - continue; // skip to next version - } - } - - // Choose the TypeURI to match the OpenID version detected. - string[] typeURIs = { protocol.ClaimedIdentifierServiceTypeURI }; - yield return ServiceEndpoint.CreateForClaimedIdentifier( - claimedIdentifier, - userSuppliedIdentifier, - providerLocalIdentifier, - new ProviderEndpointDescription(providerEndpoint, typeURIs), - (int?)null, - (int?)null); - } - } - } - - /// <summary> /// Determines whether the given URI is using a scheme in the list of allowed schemes. /// </summary> /// <param name="uri">The URI whose scheme is to be checked.</param> @@ -431,8 +427,12 @@ namespace DotNetOpenAuth.OpenId { schemePrepended = true; } + if (schemeSubstitution) { + uri = NormalSchemeToSpecialRoundTrippingScheme(uri); + } + // Use a UriBuilder because it helps to normalize the URL as well. - return TryCanonicalize(new UriBuilder(uri), out canonicalUri); + return TryCanonicalize(uri, out canonicalUri); } catch (UriFormatException) { // We try not to land here with checks in the try block, but just in case. return false; @@ -440,9 +440,9 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Removes the fragment from a URL and sets the host to lowercase. + /// Fixes up the scheme if appropriate. /// </summary> - /// <param name="uriBuilder">The URI builder with the value to canonicalize.</param> + /// <param name="uri">The URI to canonicalize.</param> /// <param name="canonicalUri">The resulting canonical URI.</param> /// <returns><c>true</c> if the canonicalization was successful; <c>false</c> otherwise.</returns> /// <remarks> @@ -452,12 +452,48 @@ namespace DotNetOpenAuth.OpenId { /// For this, you should lookup the value stored in IAuthenticationResponse.ClaimedIdentifier. /// </remarks> [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "The user will see the result of this operation and they want to see it in lower case.")] - private static bool TryCanonicalize(UriBuilder uriBuilder, out Uri canonicalUri) { - uriBuilder.Host = uriBuilder.Host.ToLowerInvariant(); - canonicalUri = uriBuilder.Uri; + private static bool TryCanonicalize(string uri, out Uri canonicalUri) { + Contract.Requires<ArgumentNullException>(uri != null); + + if (schemeSubstitution) { + UriBuilder uriBuilder = new UriBuilder(uri); + + // Swap out our round-trippable scheme for the publishable (hidden) scheme. + uriBuilder.Scheme = uriBuilder.Scheme == roundTrippingHttpParser.RegisteredScheme ? publishableHttpParser.RegisteredScheme : publishableHttpsParser.RegisteredScheme; + canonicalUri = uriBuilder.Uri; + } else { + canonicalUri = new Uri(uri); + } + return true; } + /// <summary> + /// Gets the special non-compressing scheme or URL for a standard scheme or URL. + /// </summary> + /// <param name="normal">The ordinary URL or scheme name.</param> + /// <returns>The non-compressing equivalent scheme or URL for the given value.</returns> + private static string NormalSchemeToSpecialRoundTrippingScheme(string normal) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(normal)); + Contract.Requires<InternalErrorException>(schemeSubstitution); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); + + int delimiterIndex = normal.IndexOf(Uri.SchemeDelimiter); + string normalScheme = delimiterIndex < 0 ? normal : normal.Substring(0, delimiterIndex); + string nonCompressingScheme; + if (string.Equals(normalScheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) || + string.Equals(normalScheme, publishableHttpParser.RegisteredScheme, StringComparison.OrdinalIgnoreCase)) { + nonCompressingScheme = roundTrippingHttpParser.RegisteredScheme; + } else if (string.Equals(normalScheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) || + string.Equals(normalScheme, publishableHttpsParser.RegisteredScheme, StringComparison.OrdinalIgnoreCase)) { + nonCompressingScheme = roundTrippingHttpsParser.RegisteredScheme; + } else { + throw new NotSupportedException(); + } + + return delimiterIndex < 0 ? nonCompressingScheme : nonCompressingScheme + normal.Substring(delimiterIndex); + } + #if CONTRACTS_FULL /// <summary> /// Verifies conditions that should be true for any valid state of this object. @@ -470,5 +506,193 @@ namespace DotNetOpenAuth.OpenId { Contract.Invariant(this.Uri.AbsoluteUri != null); } #endif + + /// <summary> + /// A simple URI class that doesn't suffer from the parsing problems of the <see cref="Uri"/> class. + /// </summary> + internal class SimpleUri { + /// <summary> + /// URI characters that separate the URI Path from subsequent elements. + /// </summary> + private static readonly char[] PathEndingCharacters = new char[] { '?', '#' }; + + /// <summary> + /// Initializes a new instance of the <see cref="SimpleUri"/> class. + /// </summary> + /// <param name="value">The value.</param> + internal SimpleUri(string value) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(value)); + + // Leverage the Uri class's parsing where we can. + Uri uri = new Uri(value); + this.Scheme = uri.Scheme; + this.Authority = uri.Authority; + this.Query = uri.Query; + this.Fragment = uri.Fragment; + + // Get the Path out ourselves, since the default Uri parser compresses it too much for OpenID. + int schemeLength = value.IndexOf(Uri.SchemeDelimiter, StringComparison.Ordinal); + Contract.Assume(schemeLength > 0); + int hostStart = schemeLength + Uri.SchemeDelimiter.Length; + int hostFinish = value.IndexOf('/', hostStart); + if (hostFinish < 0) { + this.Path = "/"; + } else { + int pathFinish = value.IndexOfAny(PathEndingCharacters, hostFinish); + Contract.Assume(pathFinish >= hostFinish || pathFinish < 0); + if (pathFinish < 0) { + this.Path = value.Substring(hostFinish); + } else { + this.Path = value.Substring(hostFinish, pathFinish - hostFinish); + } + } + + this.Path = NormalizePathEscaping(this.Path); + } + + /// <summary> + /// Gets the scheme. + /// </summary> + /// <value>The scheme.</value> + public string Scheme { get; private set; } + + /// <summary> + /// Gets the authority. + /// </summary> + /// <value>The authority.</value> + public string Authority { get; private set; } + + /// <summary> + /// Gets the path of the URI. + /// </summary> + /// <value>The path from the URI.</value> + public string Path { get; private set; } + + /// <summary> + /// Gets the query. + /// </summary> + /// <value>The query.</value> + public string Query { get; private set; } + + /// <summary> + /// Gets the fragment. + /// </summary> + /// <value>The fragment.</value> + public string Fragment { get; private set; } + + /// <summary> + /// Returns a <see cref="System.String"/> that represents this instance. + /// </summary> + /// <returns> + /// A <see cref="System.String"/> that represents this instance. + /// </returns> + public override string ToString() { + return this.Scheme + Uri.SchemeDelimiter + this.Authority + this.Path + this.Query + this.Fragment; + } + + /// <summary> + /// Determines whether the specified <see cref="System.Object"/> is equal to this instance. + /// </summary> + /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param> + /// <returns> + /// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>. + /// </returns> + /// <exception cref="T:System.NullReferenceException"> + /// The <paramref name="obj"/> parameter is null. + /// </exception> + public override bool Equals(object obj) { + SimpleUri other = obj as SimpleUri; + if (other == null) { + return false; + } + + // Note that this equality check is intentionally leaving off the Fragment part + // to match Uri behavior, and is intentionally being case sensitive and insensitive + // for different parts. + return string.Equals(this.Scheme, other.Scheme, StringComparison.OrdinalIgnoreCase) && + string.Equals(this.Authority, other.Authority, StringComparison.OrdinalIgnoreCase) && + string.Equals(this.Path, other.Path, StringComparison.Ordinal) && + string.Equals(this.Query, other.Query, StringComparison.Ordinal); + } + + /// <summary> + /// Returns a hash code for this instance. + /// </summary> + /// <returns> + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// </returns> + public override int GetHashCode() { + int hashCode = 0; + hashCode += StringComparer.OrdinalIgnoreCase.GetHashCode(this.Scheme); + hashCode += StringComparer.OrdinalIgnoreCase.GetHashCode(this.Authority); + hashCode += StringComparer.Ordinal.GetHashCode(this.Path); + hashCode += StringComparer.Ordinal.GetHashCode(this.Query); + return hashCode; + } + + /// <summary> + /// Normalizes the characters that are escaped in the given URI path. + /// </summary> + /// <param name="path">The path to normalize.</param> + /// <returns>The given path, with exactly those characters escaped which should be.</returns> + private static string NormalizePathEscaping(string path) { + Contract.Requires<ArgumentNullException>(path != null); + + string[] segments = path.Split('/'); + for (int i = 0; i < segments.Length; i++) { + segments[i] = Uri.EscapeDataString(Uri.UnescapeDataString(segments[i])); + } + + return string.Join("/", segments); + } + } + + /// <summary> + /// A URI parser that does not compress paths, such as trimming trailing periods from path segments. + /// </summary> + private class NonPathCompressingUriParser : GenericUriParser { + /// <summary> + /// The field that stores the scheme that this parser is registered under. + /// </summary> + private static FieldInfo schemeField; + + /// <summary> + /// The standard "http" or "https" scheme that this parser is subverting. + /// </summary> + private string standardScheme; + + /// <summary> + /// Initializes a new instance of the <see cref="NonPathCompressingUriParser"/> class. + /// </summary> + /// <param name="standardScheme">The standard scheme that this parser will be subverting.</param> + public NonPathCompressingUriParser(string standardScheme) + : base(GenericUriParserOptions.DontCompressPath | GenericUriParserOptions.IriParsing | GenericUriParserOptions.Idn) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(standardScheme)); + this.standardScheme = standardScheme; + } + + /// <summary> + /// Gets the scheme this parser is registered under. + /// </summary> + /// <value>The registered scheme.</value> + internal string RegisteredScheme { get; private set; } + + /// <summary> + /// Initializes this parser with the actual scheme it should appear to be. + /// </summary> + /// <param name="hideNonStandardScheme">if set to <c>true</c> Uris using this scheme will look like they're using the original standard scheme.</param> + [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Schemes are traditionally displayed in lowercase.")] + internal void Initialize(bool hideNonStandardScheme) { + if (schemeField == null) { + schemeField = typeof(UriParser).GetField("m_Scheme", BindingFlags.NonPublic | BindingFlags.Instance); + } + + this.RegisteredScheme = (string)schemeField.GetValue(this); + + if (hideNonStandardScheme) { + schemeField.SetValue(this, this.standardScheme.ToLowerInvariant()); + } + } + } } } diff --git a/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs new file mode 100644 index 0000000..b1a3430 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------- +// <copyright file="XriDiscoveryProxyService.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Xml; + using DotNetOpenAuth.Configuration; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.RelyingParty; + using DotNetOpenAuth.Xrds; + using DotNetOpenAuth.Yadis; + + /// <summary> + /// The discovery service for XRI identifiers that uses an XRI proxy resolver for discovery. + /// </summary> + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Justification = "Acronym")] + public class XriDiscoveryProxyService : IIdentifierDiscoveryService { + /// <summary> + /// The magic URL that will provide us an XRDS document for a given XRI identifier. + /// </summary> + /// <remarks> + /// We use application/xrd+xml instead of application/xrds+xml because it gets + /// xri.net to automatically give us exactly the right XRD element for community i-names + /// automatically, saving us having to choose which one to use out of the result. + /// The ssl=true parameter tells the proxy resolver to accept only SSL connections + /// when resolving community i-names. + /// </remarks> + private const string XriResolverProxyTemplate = "https://{1}/{0}?_xrd_r=application/xrd%2Bxml;sep=false"; + + /// <summary> + /// Initializes a new instance of the <see cref="XriDiscoveryProxyService"/> class. + /// </summary> + public XriDiscoveryProxyService() { + } + + #region IDiscoveryService Members + + /// <summary> + /// Performs discovery on the specified identifier. + /// </summary> + /// <param name="identifier">The identifier to perform discovery on.</param> + /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> + /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> + /// <returns> + /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. + /// </returns> + public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { + abortDiscoveryChain = false; + var xriIdentifier = identifier as XriIdentifier; + if (xriIdentifier == null) { + return Enumerable.Empty<IdentifierDiscoveryResult>(); + } + + return DownloadXrds(xriIdentifier, requestHandler).XrdElements.CreateServiceEndpoints(xriIdentifier); + } + + #endregion + + /// <summary> + /// Downloads the XRDS document for this XRI. + /// </summary> + /// <param name="identifier">The identifier.</param> + /// <param name="requestHandler">The request handler.</param> + /// <returns>The XRDS document.</returns> + private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + Contract.Ensures(Contract.Result<XrdsDocument>() != null); + XrdsDocument doc; + using (var xrdsResponse = Yadis.Request(requestHandler, GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd)) { + doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream)); + } + ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed); + return doc; + } + + /// <summary> + /// Gets the URL from which this XRI's XRDS document may be downloaded. + /// </summary> + /// <param name="identifier">The identifier.</param> + /// <returns>The URI to HTTP GET from to get the services.</returns> + private static Uri GetXrdsUrl(XriIdentifier identifier) { + ErrorUtilities.VerifyProtocol(DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Enabled, OpenIdStrings.XriResolutionDisabled); + string xriResolverProxy = XriResolverProxyTemplate; + if (identifier.IsDiscoverySecureEndToEnd) { + // Indicate to xri.net that we require SSL to be used for delegated resolution + // of community i-names. + xriResolverProxy += ";https=true"; + } + + return new Uri( + string.Format( + CultureInfo.InvariantCulture, + xriResolverProxy, + identifier, + DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Proxy.Name)); + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs index baba2e5..729f603 100644 --- a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs @@ -35,23 +35,6 @@ namespace DotNetOpenAuth.OpenId { private const string XriScheme = "xri://"; /// <summary> - /// The magic URL that will provide us an XRDS document for a given XRI identifier. - /// </summary> - /// <remarks> - /// We use application/xrd+xml instead of application/xrds+xml because it gets - /// xri.net to automatically give us exactly the right XRD element for community i-names - /// automatically, saving us having to choose which one to use out of the result. - /// The ssl=true parameter tells the proxy resolver to accept only SSL connections - /// when resolving community i-names. - /// </remarks> - private const string XriResolverProxyTemplate = "https://{1}/{0}?_xrd_r=application/xrd%2Bxml;sep=false"; - - /// <summary> - /// The XRI proxy resolver to use for finding XRDS documents from an XRI. - /// </summary> - private readonly string xriResolverProxy; - - /// <summary> /// Backing store for the <see cref="CanonicalXri"/> property. /// </summary> private readonly string canonicalXri; @@ -79,12 +62,6 @@ namespace DotNetOpenAuth.OpenId { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(xri)); Contract.Requires<FormatException>(IsValidXri(xri), OpenIdStrings.InvalidXri); Contract.Assume(xri != null); // Proven by IsValidXri - this.xriResolverProxy = XriResolverProxyTemplate; - if (requireSsl) { - // Indicate to xri.net that we require SSL to be used for delegated resolution - // of community i-names. - this.xriResolverProxy += ";https=true"; - } this.OriginalXri = xri; this.canonicalXri = CanonicalizeXri(xri); } @@ -105,21 +82,6 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Gets the URL from which this XRI's XRDS document may be downloaded. - /// </summary> - private Uri XrdsUrl { - get { - ErrorUtilities.VerifyProtocol(DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Enabled, OpenIdStrings.XriResolutionDisabled); - return new Uri( - string.Format( - CultureInfo.InvariantCulture, - this.xriResolverProxy, - this, - DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Proxy.Name)); - } - } - - /// <summary> /// Tests equality between this XRI and another XRI. /// </summary> /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param> @@ -180,30 +142,6 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Performs discovery on the Identifier. - /// </summary> - /// <param name="requestHandler">The web request handler to use for discovery.</param> - /// <returns> - /// An initialized structure containing the discovered provider endpoint information. - /// </returns> - internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { - return this.DownloadXrds(requestHandler).CreateServiceEndpoints(this); - } - - /// <summary> - /// Performs discovery on THIS identifier, but generates <see cref="ServiceEndpoint"/> - /// instances that treat another given identifier as the user-supplied identifier. - /// </summary> - /// <param name="requestHandler">The request handler to use in discovery.</param> - /// <param name="userSuppliedIdentifier">The user supplied identifier, which may differ from this XRI instance due to multiple discovery steps.</param> - /// <returns>A list of service endpoints offered for this identifier.</returns> - internal IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler, XriIdentifier userSuppliedIdentifier) { - Contract.Requires<ArgumentNullException>(requestHandler != null); - Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null); - return this.DownloadXrds(requestHandler).CreateServiceEndpoints(userSuppliedIdentifier); - } - - /// <summary> /// Returns an <see cref="Identifier"/> that has no URI fragment. /// Quietly returns the original <see cref="Identifier"/> if it is not /// a <see cref="UriIdentifier"/> or no fragment exists. @@ -255,22 +193,6 @@ namespace DotNetOpenAuth.OpenId { return xri; } - /// <summary> - /// Downloads the XRDS document for this XRI. - /// </summary> - /// <param name="requestHandler">The request handler.</param> - /// <returns>The XRDS document.</returns> - private XrdsDocument DownloadXrds(IDirectWebRequestHandler requestHandler) { - Contract.Requires<ArgumentNullException>(requestHandler != null); - Contract.Ensures(Contract.Result<XrdsDocument>() != null); - XrdsDocument doc; - using (var xrdsResponse = Yadis.Request(requestHandler, this.XrdsUrl, this.IsDiscoverySecureEndToEnd)) { - doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream)); - } - ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed); - return doc; - } - #if CONTRACTS_FULL /// <summary> /// Verifies conditions that should be true for any valid state of this object. @@ -279,7 +201,6 @@ namespace DotNetOpenAuth.OpenId { [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] [ContractInvariantMethod] private void ObjectInvariant() { - Contract.Invariant(this.xriResolverProxy != null); Contract.Invariant(this.canonicalXri != null); } #endif diff --git a/src/DotNetOpenAuth/Reporting.cs b/src/DotNetOpenAuth/Reporting.cs index 2235986..612845f 100644 --- a/src/DotNetOpenAuth/Reporting.cs +++ b/src/DotNetOpenAuth/Reporting.cs @@ -30,7 +30,27 @@ namespace DotNetOpenAuth { /// The statistical reporting mechanism used so this library's project authors /// know what versions and features are in use. /// </summary> - internal static class Reporting { + public static class Reporting { + /// <summary> + /// A value indicating whether reporting is desirable or not. Must be logical-AND'd with !<see cref="broken"/>. + /// </summary> + private static bool enabled; + + /// <summary> + /// A value indicating whether reporting experienced an error and cannot be enabled. + /// </summary> + private static bool broken; + + /// <summary> + /// A value indicating whether the reporting class has been initialized or not. + /// </summary> + private static bool initialized; + + /// <summary> + /// The object to lock during initialization. + /// </summary> + private static object initializationSync = new object(); + /// <summary> /// The isolated storage to use for collecting data in between published reports. /// </summary> @@ -93,37 +113,31 @@ namespace DotNetOpenAuth { [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Reporting MUST NOT cause unhandled exceptions.")] static Reporting() { Enabled = DotNetOpenAuthSection.Configuration.Reporting.Enabled; - if (Enabled) { - try { - file = GetIsolatedStorage(); - reportOriginIdentity = GetOrCreateOriginIdentity(); - - webRequestHandler = new StandardWebRequestHandler(); - observations.Add(observedRequests = new PersistentHashSet(file, "requests.txt", 3)); - observations.Add(observedCultures = new PersistentHashSet(file, "cultures.txt", 20)); - observations.Add(observedFeatures = new PersistentHashSet(file, "features.txt", int.MaxValue)); - - // Record site-wide features in use. - if (HttpContext.Current != null && HttpContext.Current.ApplicationInstance != null) { - // MVC or web forms? - // front-end or back end web farm? - // url rewriting? - ////RecordFeatureUse(IsMVC ? "ASP.NET MVC" : "ASP.NET Web Forms"); - } - } catch (Exception e) { - // This is supposed to be as low-risk as possible, so if it fails, just disable reporting - // and avoid rethrowing. - Enabled = false; - Logger.Library.Error("Error while trying to initialize reporting.", e); - } - } } /// <summary> /// Gets or sets a value indicating whether this reporting is enabled. /// </summary> /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value> - internal static bool Enabled { get; set; } + /// <remarks> + /// Setting this property to <c>true</c> <i>may</i> have no effect + /// if reporting has already experienced a failure of some kind. + /// </remarks> + public static bool Enabled { + get { + return enabled && !broken; + } + + set { + if (value) { + Initialize(); + } + + // Only set the static field here, so that other threads + // don't try to use reporting while we're initializing it. + enabled = value; + } + } /// <summary> /// Gets the configuration to use for reporting. @@ -137,6 +151,7 @@ namespace DotNetOpenAuth { /// </summary> /// <param name="eventName">Name of the event.</param> /// <param name="category">The category within the event. Null and empty strings are allowed, but considered the same.</param> + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "PersistentCounter instances are stored in a table for later use.")] internal static void RecordEventOccurrence(string eventName, string category) { Contract.Requires(!String.IsNullOrEmpty(eventName)); @@ -302,49 +317,89 @@ namespace DotNetOpenAuth { } /// <summary> + /// Initializes Reporting if it has not been initialized yet. + /// </summary> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This method must never throw.")] + private static void Initialize() { + lock (initializationSync) { + if (!broken && !initialized) { + try { + file = GetIsolatedStorage(); + reportOriginIdentity = GetOrCreateOriginIdentity(); + + webRequestHandler = new StandardWebRequestHandler(); + observations.Add(observedRequests = new PersistentHashSet(file, "requests.txt", 3)); + observations.Add(observedCultures = new PersistentHashSet(file, "cultures.txt", 20)); + observations.Add(observedFeatures = new PersistentHashSet(file, "features.txt", int.MaxValue)); + + // Record site-wide features in use. + if (HttpContext.Current != null && HttpContext.Current.ApplicationInstance != null) { + // MVC or web forms? + // front-end or back end web farm? + // url rewriting? + ////RecordFeatureUse(IsMVC ? "ASP.NET MVC" : "ASP.NET Web Forms"); + } + + initialized = true; + } catch (Exception e) { + // This is supposed to be as low-risk as possible, so if it fails, just disable reporting + // and avoid rethrowing. + broken = true; + Logger.Library.Error("Error while trying to initialize reporting.", e); + } + } + } + } + + /// <summary> /// Assembles a report for submission. /// </summary> /// <returns>A stream that contains the report.</returns> private static Stream GetReport() { var stream = new MemoryStream(); - var writer = new StreamWriter(stream, Encoding.UTF8); - writer.WriteLine(reportOriginIdentity.ToString("B")); - writer.WriteLine(Util.LibraryVersion); - writer.WriteLine(".NET Framework {0}", Environment.Version); - - foreach (var observation in observations) { - observation.Flush(); - writer.WriteLine("===================================="); - writer.WriteLine(observation.FileName); - try { - using (var fileStream = new IsolatedStorageFileStream(observation.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, file)) { - writer.Flush(); - fileStream.CopyTo(writer.BaseStream); + try { + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.WriteLine(reportOriginIdentity.ToString("B")); + writer.WriteLine(Util.LibraryVersion); + writer.WriteLine(".NET Framework {0}", Environment.Version); + + foreach (var observation in observations) { + observation.Flush(); + writer.WriteLine("===================================="); + writer.WriteLine(observation.FileName); + try { + using (var fileStream = new IsolatedStorageFileStream(observation.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, file)) { + writer.Flush(); + fileStream.CopyTo(writer.BaseStream); + } + } catch (FileNotFoundException) { + writer.WriteLine("(missing)"); } - } catch (FileNotFoundException) { - writer.WriteLine("(missing)"); } - } - // Not all event counters may have even loaded in this app instance. - // We flush the ones in memory, and then read all of them off disk. - foreach (var counter in events.Values) { - counter.Flush(); - } + // Not all event counters may have even loaded in this app instance. + // We flush the ones in memory, and then read all of them off disk. + foreach (var counter in events.Values) { + counter.Flush(); + } - foreach (string eventFile in file.GetFileNames("event-*.txt")) { - writer.WriteLine("===================================="); - writer.WriteLine(eventFile); - using (var fileStream = new IsolatedStorageFileStream(eventFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, file)) { - writer.Flush(); - fileStream.CopyTo(writer.BaseStream); + foreach (string eventFile in file.GetFileNames("event-*.txt")) { + writer.WriteLine("===================================="); + writer.WriteLine(eventFile); + using (var fileStream = new IsolatedStorageFileStream(eventFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, file)) { + writer.Flush(); + fileStream.CopyTo(writer.BaseStream); + } } - } - // Make sure the stream is positioned at the beginning. - writer.Flush(); - stream.Position = 0; - return stream; + // Make sure the stream is positioned at the beginning. + writer.Flush(); + stream.Position = 0; + return stream; + } catch { + stream.Dispose(); + throw; + } } /// <summary> @@ -459,7 +514,7 @@ namespace DotNetOpenAuth { } catch (Exception ex) { // Something bad and unexpected happened. Just deactivate to avoid more trouble. Logger.Library.Error("Error while trying to submit statistical report.", ex); - Enabled = false; + broken = true; } }); } diff --git a/src/DotNetOpenAuth/Strings.Designer.cs b/src/DotNetOpenAuth/Strings.Designer.cs index 38c89f7..70b9fb2 100644 --- a/src/DotNetOpenAuth/Strings.Designer.cs +++ b/src/DotNetOpenAuth/Strings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4927 +// Runtime Version:4.0.30104.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -70,20 +70,20 @@ namespace DotNetOpenAuth { } /// <summary> - /// Looks up a localized string similar to No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}.. + /// Looks up a localized string similar to The configuration XAML reference to {0} requires a current HttpContext to resolve.. /// </summary> - internal static string StoreRequiredWhenNoHttpContextAvailable { + internal static string ConfigurationXamlReferenceRequiresHttpContext { get { - return ResourceManager.GetString("StoreRequiredWhenNoHttpContextAvailable", resourceCulture); + return ResourceManager.GetString("ConfigurationXamlReferenceRequiresHttpContext", resourceCulture); } } /// <summary> - /// Looks up a localized string similar to The configuration XAML reference to {0} requires a current HttpContext to resolve.. + /// Looks up a localized string similar to No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}.. /// </summary> - internal static string ConfigurationXamlReferenceRequiresHttpContext { + internal static string StoreRequiredWhenNoHttpContextAvailable { get { - return ResourceManager.GetString("ConfigurationXamlReferenceRequiresHttpContext", resourceCulture); + return ResourceManager.GetString("StoreRequiredWhenNoHttpContextAvailable", resourceCulture); } } } diff --git a/src/DotNetOpenAuth/Util.cs b/src/DotNetOpenAuth/Util.cs index 9f8b30c..8a18ef8 100644 --- a/src/DotNetOpenAuth/Util.cs +++ b/src/DotNetOpenAuth/Util.cs @@ -126,7 +126,7 @@ namespace DotNetOpenAuth { sb.Append("\t"); sb.Append(objString); - if (!objString.EndsWith(Environment.NewLine)) { + if (!objString.EndsWith(Environment.NewLine, StringComparison.Ordinal)) { sb.AppendLine(); } sb.AppendLine("}, {"); diff --git a/src/DotNetOpenAuth/Xrds/XrdElement.cs b/src/DotNetOpenAuth/Xrds/XrdElement.cs index a8cc145..2cdc720 100644 --- a/src/DotNetOpenAuth/Xrds/XrdElement.cs +++ b/src/DotNetOpenAuth/Xrds/XrdElement.cs @@ -133,7 +133,7 @@ namespace DotNetOpenAuth.Xrds { /// </summary> /// <param name="p">A function that selects what element of the OpenID Protocol we're interested in finding.</param> /// <returns>A sequence of service elements that match the search criteria, sorted in XRDS @priority attribute order.</returns> - private IEnumerable<ServiceElement> SearchForServiceTypeUris(Func<Protocol, string> p) { + internal IEnumerable<ServiceElement> SearchForServiceTypeUris(Func<Protocol, string> p) { var xpath = new StringBuilder(); xpath.Append("xrd:Service["); foreach (var protocol in Protocol.AllVersions) { diff --git a/src/DotNetOpenAuth/Xrds/XrdsDocument.cs b/src/DotNetOpenAuth/Xrds/XrdsDocument.cs index dd0e19f..e2c2d72 100644 --- a/src/DotNetOpenAuth/Xrds/XrdsDocument.cs +++ b/src/DotNetOpenAuth/Xrds/XrdsDocument.cs @@ -18,6 +18,16 @@ namespace DotNetOpenAuth.Xrds { /// </summary> internal class XrdsDocument : XrdsNode { /// <summary> + /// The namespace used by XML digital signatures. + /// </summary> + private const string XmlDSigNamespace = "http://www.w3.org/2000/09/xmldsig#"; + + /// <summary> + /// The namespace used by Google Apps for Domains for OpenID URI templates. + /// </summary> + private const string GoogleOpenIdNamespace = "http://namespace.google.com/openid/xmlns"; + + /// <summary> /// Initializes a new instance of the <see cref="XrdsDocument"/> class. /// </summary> /// <param name="xrdsNavigator">The root node of the XRDS document.</param> @@ -26,6 +36,8 @@ namespace DotNetOpenAuth.Xrds { XmlNamespaceResolver.AddNamespace("xrd", XrdsNode.XrdNamespace); XmlNamespaceResolver.AddNamespace("xrds", XrdsNode.XrdsNamespace); XmlNamespaceResolver.AddNamespace("openid10", Protocol.V10.XmlNamespace); + XmlNamespaceResolver.AddNamespace("ds", XmlDSigNamespace); + XmlNamespaceResolver.AddNamespace("google", GoogleOpenIdNamespace); } /// <summary> diff --git a/src/DotNetOpenAuth/Xrds/XrdsNode.cs b/src/DotNetOpenAuth/Xrds/XrdsNode.cs index f8fa0af..39bd9b9 100644 --- a/src/DotNetOpenAuth/Xrds/XrdsNode.cs +++ b/src/DotNetOpenAuth/Xrds/XrdsNode.cs @@ -54,16 +54,16 @@ namespace DotNetOpenAuth.Xrds { /// <summary> /// Gets the node. /// </summary> - protected XPathNavigator Node { get; private set; } + internal XPathNavigator Node { get; private set; } /// <summary> /// Gets the parent node, or null if this is the root node. /// </summary> - protected XrdsNode ParentNode { get; private set; } + protected internal XrdsNode ParentNode { get; private set; } /// <summary> /// Gets the XML namespace resolver to use in XPath expressions. /// </summary> - protected XmlNamespaceManager XmlNamespaceResolver { get; private set; } + protected internal XmlNamespaceManager XmlNamespaceResolver { get; private set; } } } diff --git a/src/DotNetOpenAuth/Xrds/XrdsStrings.Designer.cs b/src/DotNetOpenAuth/Xrds/XrdsStrings.Designer.cs index 465c990..2279b5f 100644 --- a/src/DotNetOpenAuth/Xrds/XrdsStrings.Designer.cs +++ b/src/DotNetOpenAuth/Xrds/XrdsStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.3053 +// Runtime Version:4.0.30104.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.Xrds { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class XrdsStrings { diff --git a/src/DotNetOpenAuth/XrdsPublisher.cs b/src/DotNetOpenAuth/XrdsPublisher.cs index e7c04d8..83d82ff 100644 --- a/src/DotNetOpenAuth/XrdsPublisher.cs +++ b/src/DotNetOpenAuth/XrdsPublisher.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth { using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; using System.Drawing.Design; using System.Text; using System.Web; @@ -209,6 +210,7 @@ namespace DotNetOpenAuth { /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param> [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")] protected override void Render(HtmlTextWriter writer) { + Contract.Assume(writer != null, "Missing contract."); if (this.Enabled && this.Visible && !string.IsNullOrEmpty(this.XrdsUrl)) { Uri xrdsAddress = new Uri(MessagingUtilities.GetRequestUrlFromContext(), Page.Response.ApplyAppPathModifier(this.XrdsUrl)); if ((this.XrdsAdvertisement & XrdsUrlLocations.HttpHeader) != 0) { diff --git a/src/DotNetOpenAuth/Yadis/DiscoveryResult.cs b/src/DotNetOpenAuth/Yadis/DiscoveryResult.cs index 01dae40..06c6fc7 100644 --- a/src/DotNetOpenAuth/Yadis/DiscoveryResult.cs +++ b/src/DotNetOpenAuth/Yadis/DiscoveryResult.cs @@ -17,6 +17,12 @@ namespace DotNetOpenAuth.Yadis { /// </summary> internal class DiscoveryResult { /// <summary> + /// The original web response, backed up here if the final web response is the preferred response to use + /// in case it turns out to not work out. + /// </summary> + private CachedDirectWebResponse htmlFallback; + + /// <summary> /// Initializes a new instance of the <see cref="DiscoveryResult"/> class. /// </summary> /// <param name="requestUri">The user-supplied identifier.</param> @@ -25,10 +31,8 @@ namespace DotNetOpenAuth.Yadis { public DiscoveryResult(Uri requestUri, CachedDirectWebResponse initialResponse, CachedDirectWebResponse finalResponse) { this.RequestUri = requestUri; this.NormalizedUri = initialResponse.FinalUri; - if (finalResponse == null) { - this.ContentType = initialResponse.ContentType; - this.ResponseText = initialResponse.GetResponseString(); - this.IsXrds = this.ContentType != null && this.ContentType.MediaType == ContentTypes.Xrds; + if (finalResponse == null || finalResponse.Status != System.Net.HttpStatusCode.OK) { + this.ApplyHtmlResponse(initialResponse); } else { this.ContentType = finalResponse.ContentType; this.ResponseText = finalResponse.GetResponseString(); @@ -36,6 +40,9 @@ namespace DotNetOpenAuth.Yadis { if (initialResponse != finalResponse) { this.YadisLocation = finalResponse.RequestUri; } + + // Back up the initial HTML response in case the XRDS is not useful. + this.htmlFallback = initialResponse; } } @@ -77,13 +84,23 @@ namespace DotNetOpenAuth.Yadis { public bool IsXrds { get; private set; } /// <summary> - /// Gets a value indicating whether discovery resulted in an - /// XRDS document at a referred location. + /// Reverts to the HTML response after the XRDS response didn't work out. + /// </summary> + internal void TryRevertToHtmlResponse() { + if (this.htmlFallback != null) { + this.ApplyHtmlResponse(this.htmlFallback); + this.htmlFallback = null; + } + } + + /// <summary> + /// Applies the HTML response to the object. /// </summary> - /// <value><c>true</c> if the response to the userSuppliedIdentifier - /// pointed to a different URL for the XRDS document.</value> - public bool UsedYadisLocation { - get { return this.YadisLocation != null; } + /// <param name="initialResponse">The initial response.</param> + private void ApplyHtmlResponse(CachedDirectWebResponse initialResponse) { + this.ContentType = initialResponse.ContentType; + this.ResponseText = initialResponse.GetResponseString(); + this.IsXrds = this.ContentType != null && this.ContentType.MediaType == ContentTypes.Xrds; } } } diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs index f1c8be3..8b8c20f 100644 --- a/src/DotNetOpenAuth/Yadis/Yadis.cs +++ b/src/DotNetOpenAuth/Yadis/Yadis.cs @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.Yadis { /// The maximum number of bytes to read from an HTTP response /// in searching for a link to a YADIS document. /// </summary> - private const int MaximumResultToScan = 1024 * 1024; + internal const int MaximumResultToScan = 1024 * 1024; /// <summary> /// Performs YADIS discovery on some identifier. @@ -96,7 +96,6 @@ namespace DotNetOpenAuth.Yadis { response2 = Request(requestHandler, url, requireSsl, ContentTypes.Xrds).GetSnapshot(MaximumResultToScan); if (response2.Status != HttpStatusCode.OK) { Logger.Yadis.ErrorFormat("HTTP error {0} {1} while performing discovery on {2}.", (int)response2.Status, response2.Status, uri); - return null; } } else { Logger.Yadis.WarnFormat("XRDS document at insecure location '{0}'. Aborting YADIS discovery.", url); diff --git a/src/LocalTestRun.testrunconfig b/src/LocalTestRun.testrunconfig deleted file mode 100644 index f037171..0000000 --- a/src/LocalTestRun.testrunconfig +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<TestRunConfiguration name="Local Test Run" id="abbd81c0-7d7b-4c98-878c-05dbead62c27" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006"> - <Description>This is a default test run configuration for a local test run.</Description> - <CodeCoverage enabled="true"> - <Regular> - <CodeCoverageItem binaryFile="C:\git\dotnetoauth\bin\Debug\DotNetOpenAuth.dll" pdbFile="C:\git\dotnetoauth\bin\Debug\DotNetOpenAuth.pdb" instrumentInPlace="true" /> - </Regular> - </CodeCoverage> - <TestTypeSpecific> - <WebTestRunConfiguration testTypeId="4e7599fa-5ecb-43e9-a887-cd63cf72d207"> - <Browser name="Internet Explorer 7.0"> - <Headers> - <Header name="User-Agent" value="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" /> - <Header name="Accept" value="*/*" /> - <Header name="Accept-Language" value="{{$IEAcceptLanguage}}" /> - <Header name="Accept-Encoding" value="GZIP" /> - </Headers> - </Browser> - <Network Name="LAN" BandwidthInKbps="0" /> - </WebTestRunConfiguration> - </TestTypeSpecific> -</TestRunConfiguration>
\ No newline at end of file diff --git a/src/version.txt b/src/version.txt index 4772543..f989260 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -3.3.2 +3.4.4 |