diff options
125 files changed, 3793 insertions, 536 deletions
@@ -21,7 +21,6 @@ <DirtyDirectories Include=" $(ProjectRoot)\bin; $(ProjectRoot)\**\obj; - $(ProjectRoot)\doc\api; $(ProjectRoot)\drops; $(ProjectRoot)\src\PrecompiledWeb; " /> @@ -198,6 +197,7 @@ <DropLibFiles Include="@(DropLibSourceFiles->'$(DropLibDirectory)\%(RecurisveDir)%(FileName)%(Extension)')"/> <DropSamplesFiles Include="@(DropSamplesSourceFiles->'$(DropSamplesDirectory)\%(RecursiveDir)%(FileName)%(Extension)')"/> <DropSamplesRefreshFiles Include="@(DropSamplesRefreshSourceFiles->'$(DropSamplesDirectory)\%(RecursiveDir)%(FileName).refresh')"/> + <DropSamplesToolsProjects Include="$(DropSamplesDirectory)\OpenIdOfflineProvider\OpenIdOfflineProvider.csproj" /> <DropSpecsFiles Include="@(DropSpecsSourceFiles->'$(DropSpecsDirectory)\%(RecursiveDir)%(FileName)%(Extension)')"/> <AllDropSources Include=" @@ -227,6 +227,9 @@ <ItemGroup> <SampleProjectTargets Include="$(DropSamplesDirectory)\**\*.csproj" /> </ItemGroup> + <FixupShippingToolSamples Projects="@(DropSamplesToolsProjects)" + RemoveImportsStartingWith="%24(ProjectRoot)tools\;..\" + AddReferences="Microsoft.Contracts"/> <ChangeProjectReferenceToAssemblyReference Projects="@(SampleProjectTargets)" ProjectReference="..\..\src\$(ProductName)\$(ProductName).csproj" Reference="..\..\Bin\$(ProductName).dll" /> </Target> diff --git a/doc/Configuration.htm b/doc/Configuration.htm deleted file mode 100644 index 9346c9f..0000000 --- a/doc/Configuration.htm +++ /dev/null @@ -1,167 +0,0 @@ -<!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>Web.config file configuration of DotNetOpenId</title> -<style> -#id_text_to_colorize{width:600px;height:120px} -.linenos {padding-right: 5px;background: #ccc} -.code {padding-left: 5px;} -.highlight { background: #ffffff; } -.highlight .c { color: #408080; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ -.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #808080 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0040D0 } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #7D9029 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #A0A000 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ -</style> -</head> -<body> - - <p>DotNetOpenId can be configured in some aspects inside your web project's - web.config file. To do this, add the below <sectionGroup> within the - <configSections> of your Web.config file:</p> - <div class="highlight"><pre><span class="nt"><configSections></span> - <span class="nt"><sectionGroup</span> <span class="na">name=</span><span - class="s">"dotNetOpenId"</span><span class="nt">></span> - <span class="nt"><section</span> <span class="na">name=</span><span - class="s">"relyingParty"</span> <span class="na">type=</span><span - class="s">"DotNetOpenId.Configuration.RelyingPartySection"</span> <span - class="na">requirePermission=</span><span class="s">"false"</span> <span - class="na">allowLocation=</span><span class="s">"true"</span><span - class="nt">/></span> - <span class="nt"><section</span> <span class="na">name=</span><span - class="s">"provider"</span> <span class="na">type=</span><span class="s">"DotNetOpenId.Configuration.ProviderSection"</span> <span - class="na">requirePermission=</span><span class="s">"false"</span> <span - class="na">allowLocation=</span><span class="s">"true"</span><span - class="nt">/></span> - <span class="nt"><section</span> <span class="na">name=</span><span - class="s">"untrustedWebRequest"</span> <span class="na">type=</span><span - class="s">"DotNetOpenId.Configuration.UntrustedWebRequestSection"</span> <span - class="na">requirePermission=</span><span class="s">"false"</span> <span - class="na">allowLocation=</span><span class="s">"false"</span><span - class="nt">/></span> - <span class="nt"></sectionGroup></span> -<span class="nt"></configSections></span></pre></div> - <p>If you do not already have a configSections element in your Web.config file, add - it at the very top, as the first child of the root <configuration> tag.</p> - <p>Following is an example of every possible configuration setting, where each - demonstrate value happens to be the default that would be used if it wasn't set - in the .config file. Keep in mind that every setting below is optional, so - you need only include those elements that you wish to change in your own copy of - Web.config. The <dotNetOpenId> node below should show up as a peer node to - system.web in your Web.config file.</p> - <div class="highlight"><pre><span class="nt"><dotNetOpenId></span> - <span class="nt"><relyingParty></span> - <span class="nt"><security</span> <span class="na">minimumHashBitLength=</span><span - class="s">"160"</span> <span class="na">maximumHashBitLength=</span><span - class="s">"256"</span> - <span class="na">requireSsl=</span><span class="s">"false"</span> <span - class="na">minimumRequiredOpenIdVersion=</span><span class="s">"V10"</span> <span - class="nt">/></span> - <span class="nt"><store</span> <span class="na">type=</span><span class="s">"SomeSite.CustomRPStore, SomeSite"</span> <span - class="nt">/></span> - <span class="nt"></relyingParty></span> - <span class="nt"><provider></span> - <span class="nt"><security</span> <span class="na">minimumHashBitLength=</span><span - class="s">"160"</span> <span class="na">maximumHashBitLength=</span><span - class="s">"256"</span> <span class="nt">/></span> - <span class="nt"><store</span> <span class="na">type=</span><span class="s">"SomeSite.CustomProviderStore, SomeSite"</span> <span - class="nt">/></span> - <span class="nt"></provider></span> - <span class="nt"><untrustedWebRequest</span> <span class="na">readWriteTimeout=</span><span - class="s">"00:00:00.800"</span> <span class="na">timeout=</span><span - class="s">"00:00:10"</span> <span class="na">maximumBytesToRead=</span><span - class="s">"1048576"</span> <span class="na">maximumRedirections=</span><span - class="s">"10"</span><span class="nt">></span> - <span class="nt"><whitelistHosts></span> - <span class="nt"><add</span> <span class="na">name=</span><span - class="s">"localhost"</span> <span class="nt">/></span> - <span class="nt"><add</span> <span class="na">name=</span><span - class="s">"127.0.0.1"</span> <span class="nt">/></span> - <span class="nt"></whitelistHosts></span> - <span class="nt"><whitelistHostsRegex></span> - <span class="nt"><add</span> <span class="na">name=</span><span - class="s">"^(.*\.)?goodsite.com"</span> <span class="nt">/></span> - <span class="nt"></whitelistHostsRegex></span> - <span class="nt"><blacklistHosts></span> - <span class="nt"><add</span> <span class="na">name=</span><span - class="s">"internalfinancialserver"</span> <span class="nt">/></span> - <span class="nt"><add</span> <span class="na">name=</span><span - class="s">"www.evilsite.com"</span> <span class="nt">/></span> - <span class="nt"></blacklistHosts></span> - <span class="nt"><blacklistHostsRegex></span> - <span class="nt"><add</span> <span class="na">name=</span><span - class="s">"^(.*\.)?evilsite.com"</span> <span class="nt">/></span> - <span class="nt"></blacklistHostsRegex></span> - <span class="nt"></untrustedWebRequest></span> -<span class="nt"></dotNetOpenId></span> -</pre></div> - - <p>All these configuration values are also configurable at runtime using the object - model of the library. Using the Web.config file allows changes to be made - without recompiling the web site. In the case of OpenIdRelyingParty and - OpenIdProvider, it also allows you to setup your configuration just once, in - your .config file, and have it apply to every instance of OpenIdRelyingParty or - OpenIdProvider instead of you having to set up that configuration everywhere you - instantiate these types.</p> - <p>By using the ASP.NET <location> element, you can set some configuration settings - for OpenIdRelyingParty or OpenIdProvider based on which directory or web page in - your project is instantiating them. This would allow you to, for example, - use enhanced SSL security requirements at just the administrator log in screen - while allowing non-SSL OpenIDs for ordinary users.</p> - -</body> -</html> diff --git a/doc/README.html b/doc/README.html index 7877d72..a82d848 100644 --- a/doc/README.html +++ b/doc/README.html @@ -4,8 +4,8 @@ <p>DotNetOpenAuth is a .NET library that enables OpenID, OAuth and InfoCard support to be easily added to your web and/or desktop applications. </p> <p>The project site for this library is hosted at - <a href="http://dotnetopenid.googlecode.com/"> - http://dotnetopenid.googlecode.com/</a>. Please visit that web site for + <a href="http://dotnetopenauth.net/"> + http://dotnetopenauth.net/</a>. Please visit that web site for documentation, support and maintenance releases.</p> <p>As with any library that is used for security-sensitive purposes such as authentication and authorization, you should periodically check the project web diff --git a/doc/WebFarms.htm b/doc/WebFarms.htm index 44447e7..5ae924d 100644 --- a/doc/WebFarms.htm +++ b/doc/WebFarms.htm @@ -7,21 +7,26 @@ <h3> Non-ASP.NET web servers</h3> <p> - DotNetOpenId works without being a part of an ASP.NET web site. The - ASP.NET controls may not be used in this context, but the programmatic access to - the OpenIdProvider and OpenIdRelyingParty classes will work correctly if you - call the method overloads that do not require a current ASP.NET context. - The xml doc comments indicate which methods require an ASP.NET context and which - methods may be used without one.</p> + DotNetOpenAuth works without being a part of an ASP.NET web site. The ASP.NET + controls may not be used in this context, but the programmatic access to the OpenIdProvider + and OpenIdRelyingParty classes will work correctly if you call the method overloads + that do not require a current ASP.NET context. The xml doc comments indicate + which methods require an ASP.NET context and which methods may be used without one.</p> <h3> Proxy servers</h3> <p> - If your web servers need to use proxy servers to make outbound requests, you - will need to configure your .NET AppDomain to default to the proxy server you - require so that DotNetOpenId will use it. + If your web servers need to use proxy servers to make outbound requests, you will + need to configure your .NET AppDomain to default to the proxy server you require + so that DotNetOpenAuth will use it. </p> <p> - To set the default web proxy, set the System.Net.WebRequest.DefaultWebProxy + To set the default web proxy programmatically, set the System.Net.WebRequest.DefaultWebProxy property.</p> + <p> + To use the default proxy using your web.config file, add this section:<br /> + <pre><system.net> + <defaultProxy enabled="true" /> +</system.net></pre> + </p> </body> </html> diff --git a/lib/DotNetOpenAuth.BuildTasks.dll b/lib/DotNetOpenAuth.BuildTasks.dll Binary files differindex d11865f..3db458c 100644 --- a/lib/DotNetOpenAuth.BuildTasks.dll +++ b/lib/DotNetOpenAuth.BuildTasks.dll diff --git a/lib/DotNetOpenAuth.BuildTasks.pdb b/lib/DotNetOpenAuth.BuildTasks.pdb Binary files differindex dda205e..24a5292 100644 --- a/lib/DotNetOpenAuth.BuildTasks.pdb +++ b/lib/DotNetOpenAuth.BuildTasks.pdb diff --git a/lib/DotNetOpenAuth.BuildTasks.targets b/lib/DotNetOpenAuth.BuildTasks.targets index 024b43a..e57ec97 100644 --- a/lib/DotNetOpenAuth.BuildTasks.targets +++ b/lib/DotNetOpenAuth.BuildTasks.targets @@ -1,19 +1,31 @@ <?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> - <PropertyGroup> - <ProjectRoot Condition="'$(ProjectRoot)' == ''">$(MSBuildProjectDirectory)\..\..</ProjectRoot> - </PropertyGroup> - - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="GetBuildVersion" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="SetEnvironmentVariable" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="ChangeProjectReferenceToAssemblyReference" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="CompareFiles" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="ReSignDelaySignedAssemblies" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="SignatureVerification" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="CheckAdminRights" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="CreateWebApplication" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="DeleteWebApplication" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="Trim" /> - <UsingTask AssemblyFile="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.dll" TaskName="FilterItems" /> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> + <ItemGroup> + <VsTemplateParameterReplaceExtensions Include=".cs;.csproj;.sql;.config;.Master;.aspx;.ascx;.vb;.asax;.ashx" /> + <VsTemplateProjectItemTypes Include="Compile;EmbeddedResource;EntityDeploy;Content;None" /> + </ItemGroup> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="GetBuildVersion" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="SetEnvironmentVariable" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="ChangeProjectReferenceToAssemblyReference" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="CompareFiles" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="ReSignDelaySignedAssemblies" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="SignatureVerification" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="CheckAdminRights" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="CreateWebApplication" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="DeleteWebApplication" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="Trim" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="FilterItems" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="JsPack" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="CopyWithTokenSubstitution" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="MergeProjectWithVSTemplate" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="DiscoverProjectTemplates" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="FixupReferenceHintPaths" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="AddProjectItems" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="Purge" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="FixupShippingToolSamples" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="Publicize" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="DowngradeProjects" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="HardLinkCopy" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="PrepareOhlohRelease" /> </Project> diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs index ea7da97..65505b6 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs @@ -1,10 +1,44 @@ namespace DotNetOpenAuth.ApplicationBlock { using System; using System.Collections.Generic; + using System.Diagnostics; using System.IO; + using System.Net; using DotNetOpenAuth.Messaging; - internal static class Util { + public static class Util { + /// <summary> + /// Pseudo-random data generator. + /// </summary> + internal static readonly Random NonCryptoRandomDataGenerator = new Random(); + + /// <summary> + /// Sets the channel's outgoing HTTP requests to use default network credentials. + /// </summary> + /// <param name="channel">The channel to modify.</param> + public static void UseDefaultNetworkCredentialsOnOutgoingHttpRequests(this Channel channel) + { + Debug.Assert(!(channel.WebRequestHandler is WrappingWebRequestHandler), "Wrapping an already wrapped web request handler. This is legal, but highly suspect of a bug as you don't want to wrap the same channel repeatedly to apply the same effect."); + AddOutgoingHttpRequestTransform(channel, http => http.Credentials = CredentialCache.DefaultNetworkCredentials); + } + + /// <summary> + /// Adds some action to any outgoing HTTP request on this channel. + /// </summary> + /// <param name="channel">The channel's whose outgoing HTTP requests should be modified.</param> + /// <param name="action">The action to perform on outgoing HTTP requests.</param> + internal static void AddOutgoingHttpRequestTransform(this Channel channel, Action<HttpWebRequest> action) { + if (channel == null) { + throw new ArgumentNullException("channel"); + } + + if (action == null) { + throw new ArgumentNullException("action"); + } + + channel.WebRequestHandler = new WrappingWebRequestHandler(channel.WebRequestHandler, action); + } + /// <summary> /// Enumerates through the individual set bits in a flag enum. /// </summary> @@ -72,5 +106,154 @@ return totalCopiedBytes; } + + /// <summary> + /// Wraps some instance of a web request handler in order to perform some extra operation on all + /// outgoing HTTP requests. + /// </summary> + private class WrappingWebRequestHandler : IDirectWebRequestHandler + { + /// <summary> + /// The handler being wrapped. + /// </summary> + private readonly IDirectWebRequestHandler wrappedHandler; + + /// <summary> + /// The action to perform on outgoing HTTP requests. + /// </summary> + private readonly Action<HttpWebRequest> action; + + /// <summary> + /// Initializes a new instance of the <see cref="WrappingWebRequestHandler"/> class. + /// </summary> + /// <param name="wrappedHandler">The HTTP handler to wrap.</param> + /// <param name="action">The action to perform on outgoing HTTP requests.</param> + internal WrappingWebRequestHandler(IDirectWebRequestHandler wrappedHandler, Action<HttpWebRequest> action) + { + if (wrappedHandler == null) { + throw new ArgumentNullException("wrappedHandler"); + } + + if (action == null) { + throw new ArgumentNullException("action"); + } + + this.wrappedHandler = wrappedHandler; + this.action = action; + } + + #region Implementation of IDirectWebRequestHandler + + /// <summary> + /// Determines whether this instance can support the specified options. + /// </summary> + /// <param name="options">The set of options that might be given in a subsequent web request.</param> + /// <returns> + /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>. + /// </returns> + public bool CanSupport(DirectWebRequestOptions options) + { + return this.wrappedHandler.CanSupport(options); + } + + /// <summary> + /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param> + /// <returns> + /// The stream the caller should write out the entity data to. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/> + /// and any other appropriate properties <i>before</i> calling this method. + /// Callers <i>must</i> close and dispose of the request stream when they are done + /// writing to it to avoid taking up the connection too long and causing long waits on + /// subsequent requests.</para> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch.</para> + /// </remarks> + public Stream GetRequestStream(HttpWebRequest request) + { + this.action(request); + return wrappedHandler.GetRequestStream(request); + } + + /// <summary> + /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param> + /// <param name="options">The options to apply to this web request.</param> + /// <returns> + /// The stream the caller should write out the entity data to. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/> + /// and any other appropriate properties <i>before</i> calling this method. + /// Callers <i>must</i> close and dispose of the request stream when they are done + /// writing to it to avoid taking up the connection too long and causing long waits on + /// subsequent requests.</para> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch.</para> + /// </remarks> + public Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) + { + this.action(request); + return wrappedHandler.GetRequestStream(request, options); + } + + /// <summary> + /// Processes an <see cref="HttpWebRequest"/> and converts the + /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param> + /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> + /// value, if set, should be Closed before throwing.</para> + /// </remarks> + public IncomingWebResponse GetResponse(HttpWebRequest request) + { + // If the request has an entity, the action would have already been processed in GetRequestStream. + if (request.Method == "GET") + { + this.action(request); + } + + return wrappedHandler.GetResponse(request); + } + + /// <summary> + /// Processes an <see cref="HttpWebRequest"/> and converts the + /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param> + /// <param name="options">The options to apply to this web request.</param> + /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> + /// value, if set, should be Closed before throwing.</para> + /// </remarks> + public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) + { + // If the request has an entity, the action would have already been processed in GetRequestStream. + if (request.Method == "GET") { + this.action(request); + } + + return wrappedHandler.GetResponse(request, options); + } + + #endregion + } } } diff --git a/samples/InfoCardRelyingParty/Site.Master b/samples/InfoCardRelyingParty/Site.Master index 7d3dae7..508f62c 100644 --- a/samples/InfoCardRelyingParty/Site.Master +++ b/samples/InfoCardRelyingParty/Site.Master @@ -19,9 +19,9 @@ <asp:LoginStatus ID="LoginStatus1" runat="server" /> </span> <div> - <a href="http://dotnetopenid.googlecode.com"> + <a href="http://dotnetopenauth.net"> <img runat="server" src="~/images/dotnetopenid_tiny.gif" title="Jump to the project web site." - alt="DotNetOpenId" border='0' /></a> + alt="DotNetOpenAuth" border='0' /></a> </div> <div> <asp:ContentPlaceHolder ID="Main" runat="server" /> diff --git a/samples/OAuthConsumer/Twitter.aspx.cs b/samples/OAuthConsumer/Twitter.aspx.cs index a4fb0cb..9b9eced 100644 --- a/samples/OAuthConsumer/Twitter.aspx.cs +++ b/samples/OAuthConsumer/Twitter.aspx.cs @@ -54,7 +54,7 @@ public partial class Twitter : System.Web.UI.Page { protected void downloadUpdates_Click(object sender, EventArgs e) { var twitter = new WebConsumer(TwitterConsumer.ServiceDescription, this.TokenManager); - XPathDocument updates = new XPathDocument(TwitterConsumer.GetUpdates(twitter, AccessToken).CreateReader()); + XPathDocument updates = new XPathDocument(TwitterConsumer.GetUpdates(twitter, this.AccessToken).CreateReader()); XPathNavigator nav = updates.CreateNavigator(); var parsedUpdates = from status in nav.Select("/statuses/status").OfType<XPathNavigator>() where !status.SelectSingleNode("user/protected").ValueAsBoolean diff --git a/samples/OpenIdOfflineProvider/OpenIdOfflineProvider.csproj b/samples/OpenIdOfflineProvider/OpenIdOfflineProvider.csproj index 1bb2367..be1e9c3 100644 --- a/samples/OpenIdOfflineProvider/OpenIdOfflineProvider.csproj +++ b/samples/OpenIdOfflineProvider/OpenIdOfflineProvider.csproj @@ -15,6 +15,8 @@ <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <WarningLevel>4</WarningLevel> <UICulture>en-US</UICulture> + <OutputPath Condition=" '$(OutputPath)' == '' ">bin\$(Configuration)\</OutputPath> + <TargetFrameworkVersion Condition=" '$(TargetFrameworkVersion)' == '' ">v3.5</TargetFrameworkVersion> <ApplicationIcon>openid.ico</ApplicationIcon> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> @@ -161,11 +163,5 @@ <Resource Include="openid.ico" /> </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. - <Target Name="BeforeBuild"> - </Target> - <Target Name="AfterBuild"> - </Target> - --> -</Project>
\ No newline at end of file + <Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" /> +</Project> diff --git a/samples/OpenIdOfflineProvider/Properties/AssemblyInfo.cs b/samples/OpenIdOfflineProvider/Properties/AssemblyInfo.cs index adaded3..77d7464 100644 --- a/samples/OpenIdOfflineProvider/Properties/AssemblyInfo.cs +++ b/samples/OpenIdOfflineProvider/Properties/AssemblyInfo.cs @@ -10,6 +10,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; +// We DON'T put an AssemblyVersionAttribute in here because it is generated in the build. + // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. @@ -36,16 +38,3 @@ using System.Windows; ResourceDictionaryLocation.SourceAssembly)] // where the generic resource dictionary is located // (used if a resource is not found in the page, // app, or any theme specific resource dictionaries) - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/OpenIdProviderMvc/Views/Shared/Site.Master b/samples/OpenIdProviderMvc/Views/Shared/Site.Master index 073908e..49f6a7f 100644 --- a/samples/OpenIdProviderMvc/Views/Shared/Site.Master +++ b/samples/OpenIdProviderMvc/Views/Shared/Site.Master @@ -2,11 +2,11 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> -<head runat="server"> +<head> <title> <asp:ContentPlaceHolder ID="TitleContent" runat="server" /> </title> - <link href="../../Content/Site.css" rel="stylesheet" type="text/css" /> + <link href='<%= Url.Content("~/Content/Site.css") %>' rel="stylesheet" type="text/css" /> <asp:ContentPlaceHolder ID="HeadContent" runat="server" /> </head> <body> diff --git a/samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx b/samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx index 7aad102..0d73909 100644 --- a/samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx +++ b/samples/OpenIdProviderMvc/Views/Shared/Xrds.aspx @@ -27,5 +27,13 @@ for all XRDS discovery. <Type>http://specs.openid.net/extensions/ui/1.0/icon</Type>--%> <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/Provider"))%></URI> </Service> +<% if (ViewData["OPIdentifier"] == null) { %> + <Service priority="20"> + <Type>http://openid.net/signon/1.0</Type> + <Type>http://openid.net/extensions/sreg/1.1</Type> + <Type>http://axschema.org/contact/email</Type> + <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/Provider"))%></URI> + </Service> +<% } %> </XRD> </xrds:XRDS> diff --git a/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs b/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs index 893830f..6954aa6 100644 --- a/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs +++ b/samples/OpenIdProviderWebForms/ProfileFields.ascx.cs @@ -76,15 +76,15 @@ namespace OpenIdProviderWebForms { this.privacyLink.Visible = false; } - this.dobRequiredLabel.Visible = (requestFields.BirthDate == DemandLevel.Require); - this.countryRequiredLabel.Visible = (requestFields.Country == DemandLevel.Require); - this.emailRequiredLabel.Visible = (requestFields.Email == DemandLevel.Require); - this.fullnameRequiredLabel.Visible = (requestFields.FullName == DemandLevel.Require); - this.genderRequiredLabel.Visible = (requestFields.Gender == DemandLevel.Require); - this.languageRequiredLabel.Visible = (requestFields.Language == DemandLevel.Require); - this.nicknameRequiredLabel.Visible = (requestFields.Nickname == DemandLevel.Require); - this.postcodeRequiredLabel.Visible = (requestFields.PostalCode == DemandLevel.Require); - this.timezoneRequiredLabel.Visible = (requestFields.TimeZone == DemandLevel.Require); + this.dobRequiredLabel.Visible = requestFields.BirthDate == DemandLevel.Require; + this.countryRequiredLabel.Visible = requestFields.Country == DemandLevel.Require; + this.emailRequiredLabel.Visible = requestFields.Email == DemandLevel.Require; + this.fullnameRequiredLabel.Visible = requestFields.FullName == DemandLevel.Require; + this.genderRequiredLabel.Visible = requestFields.Gender == DemandLevel.Require; + this.languageRequiredLabel.Visible = requestFields.Language == DemandLevel.Require; + this.nicknameRequiredLabel.Visible = requestFields.Nickname == DemandLevel.Require; + this.postcodeRequiredLabel.Visible = requestFields.PostalCode == DemandLevel.Require; + this.timezoneRequiredLabel.Visible = requestFields.TimeZone == DemandLevel.Require; this.dateOfBirthRow.Visible = !(requestFields.BirthDate == DemandLevel.NoRequest); this.countryRow.Visible = !(requestFields.Country == DemandLevel.NoRequest); diff --git a/samples/OpenIdProviderWebForms/Site.Master b/samples/OpenIdProviderWebForms/Site.Master index 8780550..4df9e0a 100644 --- a/samples/OpenIdProviderWebForms/Site.Master +++ b/samples/OpenIdProviderWebForms/Site.Master @@ -9,9 +9,9 @@ </head> <body> <form id="form1" runat="server"> - <div><a href="http://dotnetopenid.googlecode.com"> + <div><a href="http://dotnetopenauth.net"> <img runat="server" src="~/images/dotnetopenid_tiny.gif" title="Jump to the project web site." - alt="DotNetOpenId" border='0' /></a> </div> + alt="DotNetOpenAuth" border='0' /></a> </div> <div> <asp:ContentPlaceHolder ID="Main" runat="server" /> </div> diff --git a/samples/OpenIdProviderWebForms/Web.config b/samples/OpenIdProviderWebForms/Web.config index 5db477c..cd070ba 100644 --- a/samples/OpenIdProviderWebForms/Web.config +++ b/samples/OpenIdProviderWebForms/Web.config @@ -43,7 +43,7 @@ </settings> </system.net> - <!-- this is an optional configuration section where aspects of dotnetopenid can be customized --> + <!-- this is an optional configuration section where aspects of DotNetOpenAuth can be customized --> <dotNetOpenAuth> <openid> <provider> @@ -125,7 +125,7 @@ </authorization> </system.web> </location> - <!-- log4net is a 3rd party (free) logger library that dotnetopenid will use if present but does not require. --> + <!-- log4net is a 3rd party (free) logger library that DotNetOpenAuth will use if present but does not require. --> <log4net> <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="Provider.log"/> diff --git a/samples/OpenIdProviderWebForms/server.aspx b/samples/OpenIdProviderWebForms/server.aspx index d3ce78d..a874ccd 100644 --- a/samples/OpenIdProviderWebForms/server.aspx +++ b/samples/OpenIdProviderWebForms/server.aspx @@ -25,7 +25,7 @@ <table> <tr> <td> - <a href="http://dotnetopenid.googlecode.com/">http://dotnetopenid.googlecode.com/</a> + <a href="http://dotnetopenauth.net/">http://dotnetopenauth.net/</a> </td> <td> Home of this library diff --git a/samples/OpenIdRelyingPartyClassicAsp/MembersOnly.asp b/samples/OpenIdRelyingPartyClassicAsp/MembersOnly.asp index 741a3e7..da6c18b 100644 --- a/samples/OpenIdRelyingPartyClassicAsp/MembersOnly.asp +++ b/samples/OpenIdRelyingPartyClassicAsp/MembersOnly.asp @@ -11,7 +11,7 @@ End If </head> <body> <div> - <a href="http://DotNetOpenId.googlecode.com"> + <a href="http://dotnetopenauth.net"> <img runat="server" src="images/DotNetOpenId_tiny.gif" title="Jump to the project web site." alt="DotNetOpenAuth" border='0' /></a> </div> diff --git a/samples/OpenIdRelyingPartyClassicAsp/default.asp b/samples/OpenIdRelyingPartyClassicAsp/default.asp index bdddbcc..f4d1d1d 100644 --- a/samples/OpenIdRelyingPartyClassicAsp/default.asp +++ b/samples/OpenIdRelyingPartyClassicAsp/default.asp @@ -6,7 +6,7 @@ </head> <body> <div> - <a href="http://DotNetOpenId.googlecode.com"> + <a href="http://dotnetopenauth.net"> <img runat="server" src="images/DotNetOpenId_tiny.gif" title="Jump to the project web site." alt="DotNetOpenAuth" border='0' /></a> </div> diff --git a/samples/OpenIdRelyingPartyClassicAsp/login.asp b/samples/OpenIdRelyingPartyClassicAsp/login.asp index d222e57..449af3e 100644 --- a/samples/OpenIdRelyingPartyClassicAsp/login.asp +++ b/samples/OpenIdRelyingPartyClassicAsp/login.asp @@ -6,7 +6,7 @@ </head> <body> <div> - <a href="http://DotNetOpenId.googlecode.com"> + <a href="http://dotnetopenauth.net"> <img runat="server" src="images/DotNetOpenId_tiny.gif" title="Jump to the project web site." alt="DotNetOpenAuth" border='0' /></a> </div> diff --git a/samples/OpenIdRelyingPartyMvc/Controllers/UserController.cs b/samples/OpenIdRelyingPartyMvc/Controllers/UserController.cs index fd22389..b3698bb 100644 --- a/samples/OpenIdRelyingPartyMvc/Controllers/UserController.cs +++ b/samples/OpenIdRelyingPartyMvc/Controllers/UserController.cs @@ -14,7 +14,7 @@ public ActionResult Index() { if (!User.Identity.IsAuthenticated) { - Response.Redirect("/User/Login?ReturnUrl=Index"); + Response.Redirect("~/User/Login?ReturnUrl=Index"); } return View("Index"); @@ -26,7 +26,7 @@ public ActionResult Logout() { FormsAuthentication.SignOut(); - return Redirect("/Home"); + return Redirect("~/Home"); } public ActionResult Login() { diff --git a/samples/OpenIdRelyingPartyMvc/Views/Home/Index.aspx b/samples/OpenIdRelyingPartyMvc/Views/Home/Index.aspx index 8535c7c..be4bd20 100644 --- a/samples/OpenIdRelyingPartyMvc/Views/Home/Index.aspx +++ b/samples/OpenIdRelyingPartyMvc/Views/Home/Index.aspx @@ -2,7 +2,7 @@ <asp:Content ID="indexContent" ContentPlaceHolderID="MainContentPlaceHolder" runat="server"> <h1>OpenID Relying Party </h1> - <h2>Provided by <a href="http://dotnetopenid.googlecode.com">DotNetOpenAuth</a> </h2> + <h2>Provided by <a href="http://dotnetopenauth.net">DotNetOpenAuth</a> </h2> <% if (User.Identity.IsAuthenticated) { %> <p><b>You are already logged in!</b> Try visiting the <%=Html.ActionLink("Members Only", "Index", "User") %> diff --git a/samples/OpenIdRelyingPartyMvc/Views/Shared/Site.Master b/samples/OpenIdRelyingPartyMvc/Views/Shared/Site.Master index d9b759c..35c101d 100644 --- a/samples/OpenIdRelyingPartyMvc/Views/Shared/Site.Master +++ b/samples/OpenIdRelyingPartyMvc/Views/Shared/Site.Master @@ -2,10 +2,10 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> -<head runat="server"> +<head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>DotNetOpenAuth ASP.NET MVC Login sample</title> - <link href="../../Content/Site.css" rel="stylesheet" type="text/css" /> + <link href='<%= Url.Content("~/Content/Site.css") %>' rel="stylesheet" type="text/css" /> <asp:ContentPlaceHolder ID="HeadContentPlaceHolder" runat="server" /> </head> <body> @@ -26,7 +26,7 @@ <div class="leftColumn"> <h2>External OpenID Links</h2> <ul> - <li><a href="http://dotnetopenid.googlecode.com">DotNetOpenAuth</a></li> + <li><a href="http://dotnetopenauth.net">DotNetOpenAuth</a></li> <li><a href="http://openid.net">About OpenID</a></li> </ul> </div> diff --git a/samples/OpenIdRelyingPartyMvc/Views/User/LoginPopup.aspx b/samples/OpenIdRelyingPartyMvc/Views/User/LoginPopup.aspx index e7bc18a..2f4b276 100644 --- a/samples/OpenIdRelyingPartyMvc/Views/User/LoginPopup.aspx +++ b/samples/OpenIdRelyingPartyMvc/Views/User/LoginPopup.aspx @@ -7,10 +7,10 @@ <head> <title>OpenID login demo</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> - <link type="text/css" href="../../Content/theme/ui.all.css" rel="Stylesheet" /> - <link type="text/css" href="../../Content/css/openidlogin.css" rel="stylesheet" /> - <script type="text/javascript" src="../../Content/scripts/jquery-1.3.1.js"></script> - <script type="text/javascript" src="../../Content/scripts/jquery-ui-personalized-1.6rc6.js"></script> + <link type="text/css" href='<%= Url.Content("~/Content/theme/ui.all.css") %>' rel="Stylesheet" /> + <link type="text/css" href='<%= Url.Content("~/Content/css/openidlogin.css") %>' rel="stylesheet" /> + <script type="text/javascript" src='<%= Url.Content("~/Content/scripts/jquery-1.3.1.js") %>'></script> + <script type="text/javascript" src='<%= Url.Content("~/Content/scripts/jquery-ui-personalized-1.6rc6.js") %>'></script> <script> $(function() { $('#openidlogin').dialog({ @@ -171,10 +171,10 @@ <div id="openidlogin" class="ui-widget-content"> <p>Log in with an account you already use:</p> <div class="large buttons"> - <div class="provider" onclick="document.selectProvider(this, 'https://www.google.com/accounts/o8/id')"><div><img src="../../Content/images/google.gif"/></div></div> - <div class="provider" onclick="document.selectProvider(this, 'https://me.yahoo.com/')"><div><img src="../../Content/images/yahoo.gif"/></div></div> - <div class="provider" onclick="document.selectProvider(this, 'http://openid.aol.com/{username}')"><div><img src="../../Content/images/aol.gif"/></div></div> - <div class="provider" onclick="document.selectProvider(this, '')"><div><img src="../../Content/images/openid.gif"/></div></div> + <div class="provider" onclick="document.selectProvider(this, 'https://www.google.com/accounts/o8/id')"><div><img src='<%= Url.Content("~/Content/images/google.gif") %>'/></div></div> + <div class="provider" onclick="document.selectProvider(this, 'https://me.yahoo.com/')"><div><img src='<%= Url.Content("~/Content/images/yahoo.gif") %>'/></div></div> + <div class="provider" onclick="document.selectProvider(this, 'http://openid.aol.com/{username}')"><div><img src='<%= Url.Content("~/Content/images/aol.gif") %>'/></div></div> + <div class="provider" onclick="document.selectProvider(this, '')"><div><img src='<%= Url.Content("~/Content/images/openid.gif") %>'/></div></div> </div> <div class="small buttons"> <div class="provider" onclick="document.selectProvider(this, 'http://www.flickr.com/photos/{username}')"><div><img src="http://flickr.com/favicon.ico"/></div></div> diff --git a/samples/OpenIdRelyingPartyMvc/Web.config b/samples/OpenIdRelyingPartyMvc/Web.config index da36d72..bf37616 100644 --- a/samples/OpenIdRelyingPartyMvc/Web.config +++ b/samples/OpenIdRelyingPartyMvc/Web.config @@ -117,7 +117,7 @@ ASP.NET to identify an incoming user. --> <authentication mode="Forms"> - <forms defaultUrl="/Home" loginUrl="/User/Login" name="OpenIdRelyingPartyMvcSession"/> + <forms defaultUrl="~/Home" loginUrl="~/User/Login" name="OpenIdRelyingPartyMvcSession"/> <!-- named cookie prevents conflicts with other samples --> </authentication> <!-- diff --git a/samples/OpenIdRelyingPartyWebForms/Site.Master b/samples/OpenIdRelyingPartyWebForms/Site.Master index 9630f78..cf8c507 100644 --- a/samples/OpenIdRelyingPartyWebForms/Site.Master +++ b/samples/OpenIdRelyingPartyWebForms/Site.Master @@ -27,9 +27,9 @@ <asp:LoginStatus ID="LoginStatus1" runat="server" OnLoggedOut="LoginStatus1_LoggedOut" /> </span> <div> - <a href="http://dotnetopenid.googlecode.com"> + <a href="http://dotnetopenauth.net"> <img runat="server" src="~/images/dotnetopenid_tiny.gif" title="Jump to the project web site." - alt="DotNetOpenId" border='0' /></a> + alt="DotNetOpenAuth" border='0' /></a> </div> <div> <asp:ContentPlaceHolder ID="Main" runat="server" /> diff --git a/samples/OpenIdRelyingPartyWebForms/Web.config b/samples/OpenIdRelyingPartyWebForms/Web.config index 536294a..7c35f74 100644 --- a/samples/OpenIdRelyingPartyWebForms/Web.config +++ b/samples/OpenIdRelyingPartyWebForms/Web.config @@ -73,7 +73,7 @@ <trust level="Medium" originUrl=".*"/> </system.web> - <!-- log4net is a 3rd party (free) logger library that dotnetopenid will use if present but does not require. --> + <!-- log4net is a 3rd party (free) logger library that DotNetOpenAuth will use if present but does not require. --> <log4net> <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="RelyingParty.log" /> diff --git a/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx.cs b/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx.cs index ffaf6f0..de44e35 100644 --- a/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx.cs +++ b/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx.cs @@ -18,7 +18,7 @@ } protected void OpenIdAjaxTextBox1_LoggedIn(object sender, OpenIdEventArgs e) { - Label label = ((Label)this.commentSubmitted.FindControl("emailLabel")); + Label label = (Label)this.commentSubmitted.FindControl("emailLabel"); label.Text = e.Response.FriendlyIdentifierForDisplay; // We COULD get the sreg extension response here for the email, but since we let the user diff --git a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx index 78179f7..a00eccd 100644 --- a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx +++ b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx @@ -12,5 +12,4 @@ Visible="False" /> <asp:Label ID="loginCanceledLabel" runat="server" EnableViewState="False" Text="Login canceled" Visible="False" /> - <asp:CheckBox ID="noLoginCheckBox" runat="server" Text="Extensions only (no login) -- most OPs don't yet support this" /> </asp:Content>
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.designer.cs b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.designer.cs index 239d7b8..088e305 100644 --- a/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.designer.cs +++ b/samples/OpenIdRelyingPartyWebForms/loginProgrammatic.aspx.designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4918 +// Runtime Version:2.0.50727.4927 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -66,14 +66,5 @@ namespace OpenIdRelyingPartyWebForms { /// To modify move field declaration from designer file to code-behind file. /// </remarks> protected global::System.Web.UI.WebControls.Label loginCanceledLabel; - - /// <summary> - /// noLoginCheckBox control. - /// </summary> - /// <remarks> - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// </remarks> - protected global::System.Web.UI.WebControls.CheckBox noLoginCheckBox; } } diff --git a/samples/README.html b/samples/README.html index 287942a..10e0f8c 100644 --- a/samples/README.html +++ b/samples/README.html @@ -94,22 +94,22 @@ <h3>Interesting classes and methods</h3> <h4>Relying party</h4> <ul> - <li>DotNetOpenId.RelyingParty.<b>OpenIdRelyingParty</b> - programmatic access to everything + <li>DotNetOpenAuth.OpenId.RelyingParty.<b>OpenIdRelyingParty</b> - programmatic access to everything a relying party web site needs.</li> - <li>DotNetOpenId.RelyingParty.<b>OpenIdTextBox</b> - An ASP.NET control that is a bare-bones + <li>DotNetOpenAuth.OpenId.RelyingParty.<b>OpenIdTextBox</b> - An ASP.NET control that is a bare-bones text input box with a LogOn method that automatically does all the OpenId stuff for you.</li> - <li>DotNetOpenId.RelyingParty.<b>OpenIdLogin</b> - Like the OpenIdTextBox, but has a + <li>DotNetOpenAuth.OpenId.RelyingParty.<b>OpenIdLogin</b> - Like the OpenIdTextBox, but has a Login button and some other end user-friendly UI built-in. Drop this onto your web form and you're all done!</li> </ul> <h4>Provider</h4> <ul> - <li>DotNetOpenId.Provider.<b>OpenIdProvider</b> - programmatic access to everything + <li>DotNetOpenAuth.OpenId.Provider.<b>OpenIdProvider</b> - programmatic access to everything a provider web site needs.</li> - <li>DotNetOpenId.Provider.<b>ProviderEndpoint</b> - An ASP.NET control that you can + <li>DotNetOpenAuth.OpenId.Provider.<b>ProviderEndpoint</b> - An ASP.NET control that you can drop in and have an instant provider endpoint on your page.</li> - <li>DotNetOpenId.Provider.<b>IdentityEndpoint</b> - An ASP.NET control that you can + <li>DotNetOpenAuth.OpenId.Provider.<b>IdentityEndpoint</b> - An ASP.NET control that you can drop onto the page for your own or your customers' individual identity pages for discovery by Relying Parties.</li> </ul> diff --git a/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs b/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs new file mode 100644 index 0000000..30fa284 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------- +// <copyright file="AddProjectItems.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.BuildEngine; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + using System.Collections; + + public class AddProjectItems : Task { + /// <summary> + /// Gets or sets the projects to add items to. + /// </summary> + /// <value>The projects.</value> + [Required] + public ITaskItem[] Projects { get; set; } + + /// <summary> + /// Gets or sets the items to add to each project. + /// </summary> + /// <value>The items.</value> + /// <remarks> + /// Use the metadata "ItemType" on each item to specify the item type to use for the new + /// project item. If the metadata is absent, "None" is used as the item type. + /// </remarks> + [Required] + public ITaskItem[] Items { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + foreach (var projectTaskItem in this.Projects) { + var project = new Project(); + project.Load(projectTaskItem.ItemSpec); + + foreach (var projectItem in this.Items) { + string itemType = projectItem.GetMetadata("ItemType"); + if (string.IsNullOrEmpty(itemType)) { + itemType = "None"; + } + 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); + } + } + + project.Save(projectTaskItem.ItemSpec); + } + + return !this.Log.HasLoggedErrors; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs new file mode 100644 index 0000000..3ad5eb0 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs @@ -0,0 +1,47 @@ +using System; +using System.Linq; +using System.IO; +using System.Xml; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Build.BuildEngine; + +namespace DotNetOpenAuth.BuildTasks { + /// <summary> + /// Replaces Reference items HintPaths in a set of projects. + /// </summary> + public class ChangeAssemblyReference : Task { + /// <summary> + /// The projects to alter. + /// </summary> + [Required] + public ITaskItem[] Projects { get; set; } + /// <summary> + /// The project reference to remove. + /// </summary> + [Required] + public string OldReference { get; set; } + /// <summary> + /// The assembly reference to add. + /// </summary> + [Required] + public string NewReference { get; set; } + + const string msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; + public override bool Execute() { + foreach (var project in Projects) { + Project doc = new Project(); + doc.Load(project.ItemSpec); + + var reference = doc.GetEvaluatedItemsByName("Reference").OfType<BuildItem>(). + Where(item => string.Equals(item.GetMetadata("HintPath"), OldReference, StringComparison.OrdinalIgnoreCase)).Single(); + reference.SetMetadata("HintPath", NewReference); + + doc.Save(project.ItemSpec); + } + + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs new file mode 100644 index 0000000..4b103b4 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using System.IO; +using System.Xml; + +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 { + /// <summary> + /// The projects to alter. + /// </summary> + [Required] + public ITaskItem[] Projects { get; set; } + /// <summary> + /// The project reference to remove. + /// </summary> + [Required] + public string ProjectReference { get; set; } + /// <summary> + /// The assembly reference to add. + /// </summary> + [Required] + public string Reference { 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); + + Project doc = new Project(); + doc.Load(project.ItemSpec, ProjectLoadSettings.IgnoreMissingImports); + + 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); + + doc.Save(project.ItemSpec); + } + + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs b/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs new file mode 100644 index 0000000..9cfd35d --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------- +// <copyright file="CheckAdminRights.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System.Security.Principal; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + public class CheckAdminRights : Task { + /// <summary> + /// Gets or sets a value indicating whether this process has elevated permissions. + /// </summary> + [Output] + public bool IsElevated { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + WindowsIdentity id = WindowsIdentity.GetCurrent(); + WindowsPrincipal p = new WindowsPrincipal(id); + this.IsElevated = p.IsInRole(WindowsBuiltInRole.Administrator); + + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs b/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs new file mode 100644 index 0000000..51fcee4 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; +using System.IO; + +namespace DotNetOpenAuth.BuildTasks { + public class CompareFiles : Task { + /// <summary> + /// One set of items to compare. + /// </summary> + [Required] + public ITaskItem[] OriginalItems { get; set; } + + /// <summary> + /// The other set of items to compare. + /// </summary> + [Required] + public ITaskItem[] NewItems { get; set; } + + /// <summary> + /// Gets whether the items lists contain items that are identical going down the list. + /// </summary> + [Output] + public bool AreSame { get; private set; } + + /// <summary> + /// Same as <see cref="AreSame"/>, but opposite. + /// </summary> + [Output] + public bool AreChanged { get { return !AreSame; } } + + public override bool Execute() { + AreSame = AreFilesIdentical(); + return true; + } + + private bool AreFilesIdentical() { + if (OriginalItems.Length != NewItems.Length) { + return false; + } + + for (int i = 0; i < OriginalItems.Length; i++) { + if (!IsContentOfFilesTheSame(OriginalItems[i].ItemSpec, NewItems[i].ItemSpec)) { + return false; + } + } + + return true; + } + + private bool IsContentOfFilesTheSame(string file1, string file2) { + // If exactly one file is missing, that's different. + if (File.Exists(file1) ^ File.Exists(file2)) return false; + // If both are missing, that's the same. + if (!File.Exists(file1)) return true; + // If both are present, we need to do a content comparison. + using (FileStream fileStream1 = File.OpenRead(file1)) { + using (FileStream fileStream2 = File.OpenRead(file2)) { + if (fileStream1.Length != fileStream2.Length) return false; + byte[] buffer1 = new byte[4096]; + byte[] buffer2 = new byte[buffer1.Length]; + int bytesRead; + do { + bytesRead = fileStream1.Read(buffer1, 0, buffer1.Length); + if (fileStream2.Read(buffer2, 0, buffer2.Length) != bytesRead) { + // We should never get here since we compared file lengths, but + // this is a sanity check. + return false; + } + for (int i = 0; i < bytesRead; i++) { + if (buffer1[i] != buffer2[i]) { + return false; + } + } + } while (bytesRead == buffer1.Length); + } + } + + return true; + } + + /// <summary> + /// Tests whether a file is up to date with respect to another, + /// based on existence, last write time and file size. + /// </summary> + /// <param name="sourcePath">The source path.</param> + /// <param name="destPath">The dest path.</param> + /// <returns><c>true</c> if the files are the same; <c>false</c> if the files are different</returns> + internal static bool FastFileEqualityCheck(string sourcePath, string destPath) { + FileInfo sourceInfo = new FileInfo(sourcePath); + FileInfo destInfo = new FileInfo(destPath); + + if (sourceInfo.Exists ^ destInfo.Exists) { + // Either the source file or the destination file is missing. + return false; + } + + if (!sourceInfo.Exists) { + // Neither file exists. + return true; + } + + // We'll say the files are the same if their modification date and length are the same. + return + sourceInfo.LastWriteTimeUtc == destInfo.LastWriteTimeUtc && + sourceInfo.Length == destInfo.Length; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs b/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs new file mode 100644 index 0000000..e17d8f2 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------- +// <copyright file="CopyWithTokenSubstitution.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.Framework; + using Microsoft.Build.Utilities; + + /// <summary> + /// Copies files and performs a search and replace for given tokens in their contents during the process. + /// </summary> + public class CopyWithTokenSubstitution : Task { + /// <summary> + /// Gets or sets the files to copy. + /// </summary> + /// <value>The files to copy.</value> + [Required] + public ITaskItem[] SourceFiles { get; set; } + + /// <summary> + /// Gets or sets a list of files to copy the source files to. + /// </summary> + /// <value>The list of files to copy the source files to.</value> + [Required] + public ITaskItem[] DestinationFiles { get; set; } + + /// <summary> + /// Gets or sets the destination files actually copied to. + /// </summary> + /// <remarks> + /// In the case of error partway through, or files not copied due to already being up to date, + /// this can be a subset of the <see cref="DestinationFiles"/> array. + /// </remarks> + [Output] + public ITaskItem[] CopiedFiles { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + /// <returns><c>true</c> if the operation was successful.</returns> + public override bool Execute() { + if (this.SourceFiles.Length != this.DestinationFiles.Length) { + Log.LogError("{0} inputs and {1} outputs given.", this.SourceFiles.Length, this.DestinationFiles.Length); + return false; + } + + var copiedFiles = new List<ITaskItem>(this.DestinationFiles.Length); + + 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")); + + // 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); + + 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 (StreamReader sr = File.OpenText(sourcePath)) { + if (!Directory.Exists(Path.GetDirectoryName(destPath))) { + Directory.CreateDirectory(Path.GetDirectoryName(destPath)); + } + 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); + } + } + } + + copiedFiles.Add(this.DestinationFiles[i]); + } + + this.CopiedFiles = copiedFiles.ToArray(); + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/CreateWebApplication.cs b/src/DotNetOpenAuth.BuildTasks/CreateWebApplication.cs new file mode 100644 index 0000000..4980898 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/CreateWebApplication.cs @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------- +// <copyright file="CreateWebApplication.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Linq; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + using Microsoft.Web.Administration; + + /// <summary> + /// Creates or updates web applications within an existing web site in IIS. + /// </summary> + public class CreateWebApplication : Task { + /// <summary> + /// Gets or sets the name of the application pool that should host the web application. + /// </summary> + /// <value>The name of an existing application pool.</value> + public string ApplicationPoolName { get; set; } + + /// <summary> + /// Gets or sets the name of the web site under which to create the web application. + /// </summary> + /// <value>The name of the existing web site.</value> + [Required] + public string WebSiteName { get; set; } + + /// <summary> + /// Gets or sets the virtual paths within the web site that will access these applications. + /// </summary> + /// <value>The virtual path, which must start with '/'.</value> + [Required] + public ITaskItem[] VirtualPaths { get; set; } + + /// <summary> + /// Gets or sets the full file system paths to the web applications. + /// </summary> + /// <value>The physical path.</value> + [Required] + public ITaskItem[] PhysicalPaths { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + /// <returns>A value indicating whether the task completed successfully.</returns> + public override bool Execute() { + var serverManager = new ServerManager(); + + if (this.PhysicalPaths.Length != this.VirtualPaths.Length) { + Log.LogError(TaskStrings.MismatchingArrayLengths, "PhysicalPath", "VirtualPath"); + return false; + } + + if (this.VirtualPaths.Length == 0) { + // Nothing to do. + return true; + } + + // Find the root web site that this web application will be created under. + var site = serverManager.Sites.FirstOrDefault(s => string.Equals(s.Name, this.WebSiteName, StringComparison.OrdinalIgnoreCase)); + if (site == null) { + Log.LogError(TaskStrings.NoMatchingWebSiteFound, this.WebSiteName); + return false; + } + + Log.LogMessage(MessageImportance.Normal, "Creating web applications under web site: {0}", site.Name); + + for (int i = 0; i < this.PhysicalPaths.Length; i++) { + string physicalPath = this.PhysicalPaths[i].ItemSpec; + string virtualPath = this.VirtualPaths[i].ItemSpec; + + Log.LogMessage(MessageImportance.Normal, "\t{0} -> {1}", virtualPath, physicalPath); + + var app = site.Applications.FirstOrDefault(a => string.Equals(a.Path, virtualPath, StringComparison.OrdinalIgnoreCase)); + if (app == null) { + app = site.Applications.Add(virtualPath, physicalPath); + } else { + // Ensure physical path is set correctly. + var appRoot = app.VirtualDirectories.First(vd => vd.Path == "/"); + appRoot.PhysicalPath = physicalPath; + } + + if (!string.IsNullOrEmpty(this.ApplicationPoolName)) { + app.ApplicationPoolName = this.ApplicationPoolName; + } + } + + serverManager.CommitChanges(); + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/DeleteWebApplication.cs b/src/DotNetOpenAuth.BuildTasks/DeleteWebApplication.cs new file mode 100644 index 0000000..930a8c4 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/DeleteWebApplication.cs @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------- +// <copyright file="DeleteWebApplication.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Linq; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + using Microsoft.Web.Administration; + + /// <summary> + /// Deletes a web application from IIS. + /// </summary> + public class DeleteWebApplication : Task { + /// <summary> + /// Gets or sets the name of the web site under which to create the web application. + /// </summary> + /// <value>The name of the existing web site.</value> + [Required] + public string WebSiteName { get; set; } + + /// <summary> + /// Gets or sets the virtual paths within the web site that will access these applications. + /// </summary> + /// <value>The virtual path, which must start with '/'.</value> + [Required] + public ITaskItem[] VirtualPaths { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + /// <returns>A value indicating whether the task completed successfully.</returns> + public override bool Execute() { + var serverManager = new ServerManager(); + + // Find the root web site that this web application will be created under. + var site = serverManager.Sites.FirstOrDefault(s => string.Equals(s.Name, this.WebSiteName, StringComparison.OrdinalIgnoreCase)); + if (site == null) { + Log.LogMessage(MessageImportance.Low, TaskStrings.NoMatchingWebSiteFound, this.WebSiteName); + return true; + } + + if (this.VirtualPaths.Length == 0) { + // Nothing to do. + return true; + } + + foreach (ITaskItem path in this.VirtualPaths) { + var app = site.Applications.FirstOrDefault(a => string.Equals(a.Path, path.ItemSpec, StringComparison.OrdinalIgnoreCase)); + if (app != null) { + site.Applications.Remove(app); + Log.LogMessage(MessageImportance.Normal, TaskStrings.DeletedWebApplication, app.Path); + } else { + Log.LogMessage(MessageImportance.Low, TaskStrings.WebApplicationNotFoundSoNotDeleted, path.ItemSpec); + } + } + + serverManager.CommitChanges(); + + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs b/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs new file mode 100644 index 0000000..0162c16 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------- +// <copyright file="DiscoverProjectTemplates.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 System.Xml.Linq; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + public class DiscoverProjectTemplates : Task { + public ITaskItem[] TopLevelTemplates { get; set; } + + [Output] + public ITaskItem[] ProjectTemplates { get; set; } + + [Output] + public ITaskItem[] ProjectTemplateContents { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + List<ITaskItem> projectTemplates = new List<ITaskItem>(); + List<ITaskItem> projectTemplateContents = new List<ITaskItem>(); + foreach (ITaskItem topLevelTemplate in this.TopLevelTemplates) { + var vsTemplate = XElement.Load(topLevelTemplate.ItemSpec); + var templateContent = vsTemplate.Element(XName.Get("TemplateContent", MergeProjectWithVSTemplate.VSTemplateNamespace)); + var projectCollection = templateContent.Element(XName.Get("ProjectCollection", MergeProjectWithVSTemplate.VSTemplateNamespace)); + var links = projectCollection.Elements(XName.Get("ProjectTemplateLink", MergeProjectWithVSTemplate.VSTemplateNamespace)); + var subTemplates = links.Select( + link => (ITaskItem)new TaskItem( + link.Value, + new Dictionary<string, string> { + { "TopLevelTemplate", topLevelTemplate.ItemSpec }, + { "TopLevelTemplateFileName", Path.GetFileNameWithoutExtension(topLevelTemplate.ItemSpec) }, + })); + projectTemplates.AddRange(subTemplates); + + foreach (var link in links.Select(link => link.Value)) { + string[] files = Directory.GetFiles(Path.Combine(Path.GetDirectoryName(topLevelTemplate.ItemSpec), Path.GetDirectoryName(link)), "*.*", SearchOption.AllDirectories); + projectTemplateContents.AddRange(files.Select(file => (ITaskItem)new TaskItem( + file, + new Dictionary<string, string> { + { "TopLevelTemplate", topLevelTemplate.ItemSpec }, + { "TopLevelTemplateFileName", Path.GetFileNameWithoutExtension(topLevelTemplate.ItemSpec) }, + }))); + } + } + + this.ProjectTemplates = projectTemplates.ToArray(); + this.ProjectTemplateContents = projectTemplateContents.ToArray(); + + return !this.Log.HasLoggedErrors; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj new file mode 100644 index 0000000..038e7f8 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>9.0.30729</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>DotNetOpenAuth.BuildTasks</RootNamespace> + <AssemblyName>DotNetOpenAuth.BuildTasks</AssemblyName> + <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <CodeContractsAssemblyMode>1</CodeContractsAssemblyMode> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>..\..\lib\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking> + <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface> + <CodeContractsRuntimeThrowOnFailure>False</CodeContractsRuntimeThrowOnFailure> + <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires> + <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis> + <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations> + <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations> + <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations> + <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> + <CodeContractsRunInBackground>True</CodeContractsRunInBackground> + <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies> + <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine> + <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs> + <CodeContractsCustomRewriterAssembly> + </CodeContractsCustomRewriterAssembly> + <CodeContractsCustomRewriterClass> + </CodeContractsCustomRewriterClass> + <CodeContractsLibPaths> + </CodeContractsLibPaths> + <CodeContractsPlatformPath> + </CodeContractsPlatformPath> + <CodeContractsExtraAnalysisOptions> + </CodeContractsExtraAnalysisOptions> + <CodeContractsBaseLineFile> + </CodeContractsBaseLineFile> + <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel> + <CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly> + <CodeContractsContainerAnalysis>False</CodeContractsContainerAnalysis> + <CodeContractsExtraRewriteOptions> + </CodeContractsExtraRewriteOptions> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>..\..\lib\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking> + <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface> + <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure> + <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires> + <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis> + <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations> + <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations> + <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations> + <CodeContractsContainerAnalysis>False</CodeContractsContainerAnalysis> + <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> + <CodeContractsRunInBackground>True</CodeContractsRunInBackground> + <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies> + <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine> + <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs> + <CodeContractsCustomRewriterAssembly> + </CodeContractsCustomRewriterAssembly> + <CodeContractsCustomRewriterClass> + </CodeContractsCustomRewriterClass> + <CodeContractsLibPaths> + </CodeContractsLibPaths> + <CodeContractsExtraRewriteOptions> + </CodeContractsExtraRewriteOptions> + <CodeContractsExtraAnalysisOptions> + </CodeContractsExtraAnalysisOptions> + <CodeContractsBaseLineFile> + </CodeContractsBaseLineFile> + <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel> + <CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.Build.Engine" /> + <Reference Include="Microsoft.Build.Framework" /> + <Reference Include="Microsoft.Build.Utilities.v3.5"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + <Reference Include="Microsoft.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL"> + <Private>False</Private> + </Reference> + <Reference Include="Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>$(SystemRoot)\System32\inetsrv\Microsoft.Web.Administration.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + <Reference Include="System.Xml.Linq"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="AddProjectItems.cs" /> + <Compile Include="ChangeProjectReferenceToAssemblyReference.cs" /> + <Compile Include="CompareFiles.cs" /> + <Compile Include="ChangeAssemblyReference.cs" /> + <Compile Include="CopyWithTokenSubstitution.cs" /> + <Compile Include="CreateWebApplication.cs" /> + <Compile Include="DeleteWebApplication.cs" /> + <Compile Include="DiscoverProjectTemplates.cs" /> + <Compile Include="ECMAScriptPacker.cs" /> + <Compile Include="FilterItems.cs" /> + <Compile Include="FixupReferenceHintPaths.cs" /> + <Compile Include="FixupShippingToolSamples.cs" /> + <Compile Include="MergeProjectWithVSTemplate.cs" /> + <Compile Include="GetBuildVersion.cs" /> + <Compile Include="CheckAdminRights.cs" /> + <Compile Include="JsPack.cs" /> + <Compile Include="ParseMaster.cs" /> + <Compile Include="Purge.cs" /> + <Compile Include="ReSignDelaySignedAssemblies.cs" /> + <Compile Include="SetEnvironmentVariable.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="SignatureVerification.cs" /> + <Compile Include="SnToolTask.cs" /> + <Compile Include="TaskStrings.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>TaskStrings.resx</DependentUpon> + </Compile> + <Compile Include="Trim.cs" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="TaskStrings.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>TaskStrings.Designer.cs</LastGenOutput> + </EmbeddedResource> + </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. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln new file mode 100644 index 0000000..fca41e8 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln @@ -0,0 +1,28 @@ + +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 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ABBE14A3-0404-4123-9093-E598C3DD3E9B}" + ProjectSection(SolutionItems) = preProject + ..\..\build.proj = ..\..\build.proj + ..\..\lib\DotNetOpenAuth.BuildTasks.targets = ..\..\lib\DotNetOpenAuth.BuildTasks.targets + ..\..\tools\DotNetOpenAuth.Common.Settings.targets = ..\..\tools\DotNetOpenAuth.Common.Settings.targets + ..\..\tools\DotNetOpenAuth.Versioning.targets = ..\..\tools\DotNetOpenAuth.Versioning.targets + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/DotNetOpenAuth.BuildTasks/ECMAScriptPacker.cs b/src/DotNetOpenAuth.BuildTasks/ECMAScriptPacker.cs new file mode 100644 index 0000000..d63d5b4 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/ECMAScriptPacker.cs @@ -0,0 +1,486 @@ +using System; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections; +using System.Collections.Specialized; +using System.Web; +using System.IO; + +/* + packer, version 2.0 (beta) (2005/02/01) + Copyright 2004-2005, Dean Edwards + Web: http://dean.edwards.name/ + + This software is licensed under the CC-GNU LGPL + Web: http://creativecommons.org/licenses/LGPL/2.1/ + + Ported to C# by Jesse Hansen, twindagger2k@msn.com + modified slightly by Andrew Arnott +*/ + +// http://dean.edwards.name/packer/ + +namespace Dean.Edwards +{ + /// <summary> + /// Packs a javascript file into a smaller area, removing unnecessary characters from the output. + /// </summary> + internal class ECMAScriptPacker + { + /// <summary> + /// The encoding level to use. See http://dean.edwards.name/packer/usage/ for more info. + /// </summary> + public enum PackerEncoding { None = 0, Numeric = 10, Mid = 36, Normal = 62, HighAscii = 95 }; + + private PackerEncoding encoding = PackerEncoding.Normal; + private bool fastDecode = true; + private bool specialChars = false; + private bool enabled = true; + + string IGNORE = "$1"; + + /// <summary> + /// The encoding level for this instance + /// </summary> + public PackerEncoding Encoding + { + get { return encoding; } + set { encoding = value; } + } + + /// <summary> + /// Adds a subroutine to the output to speed up decoding + /// </summary> + public bool FastDecode + { + get { return fastDecode; } + set { fastDecode = value; } + } + + /// <summary> + /// Replaces special characters + /// </summary> + public bool SpecialChars + { + get { return specialChars; } + set { specialChars = value; } + } + + /// <summary> + /// Packer enabled + /// </summary> + public bool Enabled + { + get { return enabled; } + set { enabled = value; } + } + + public ECMAScriptPacker() + { + Encoding = PackerEncoding.Normal; + FastDecode = true; + SpecialChars = false; + } + + /// <summary> + /// Constructor + /// </summary> + /// <param name="encoding">The encoding level for this instance</param> + /// <param name="fastDecode">Adds a subroutine to the output to speed up decoding</param> + /// <param name="specialChars">Replaces special characters</param> + public ECMAScriptPacker(PackerEncoding encoding, bool fastDecode, bool specialChars) + { + Encoding = encoding; + FastDecode = fastDecode; + SpecialChars = specialChars; + } + + /// <summary> + /// Packs the script + /// </summary> + /// <param name="script">the script to pack</param> + /// <returns>the packed script</returns> + public string Pack(string script) + { + if (enabled) + { + script += "\n"; + script = basicCompression(script); + if (SpecialChars) + script = encodeSpecialChars(script); + if (Encoding != PackerEncoding.None) + script = encodeKeywords(script); + } + return script; + } + + //zero encoding - just removal of whitespace and comments + private string basicCompression(string script) + { + ParseMaster parser = new ParseMaster(); + // make safe + parser.EscapeChar = '\\'; + // protect strings + parser.Add("'[^'\\n\\r]*'", IGNORE); + parser.Add("\"[^\"\\n\\r]*\"", IGNORE); + // remove comments + parser.Add("\\/\\/[^\\n\\r]*[\\n\\r]"); + parser.Add("\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/"); + // protect regular expressions + parser.Add("\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)", "$2"); + parser.Add("[^\\w\\$\\/'\"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?", IGNORE); + // remove: ;;; doSomething(); + if (specialChars) + parser.Add(";;[^\\n\\r]+[\\n\\r]"); + // remove redundant semi-colons + parser.Add(";+\\s*([};])", "$2"); + // remove white-space + parser.Add("(\\b|\\$)\\s+(\\b|\\$)", "$2 $3"); + parser.Add("([+\\-])\\s+([+\\-])", "$2 $3"); + parser.Add("\\s+"); + // done + return parser.Exec(script); + } + + WordList encodingLookup; + private string encodeSpecialChars(string script) + { + ParseMaster parser = new ParseMaster(); + // replace: $name -> n, $$name -> na + parser.Add("((\\$+)([a-zA-Z\\$_]+))(\\d*)", + new ParseMaster.MatchGroupEvaluator(encodeLocalVars)); + + // replace: _name -> _0, double-underscore (__name) is ignored + Regex regex = new Regex("\\b_[A-Za-z\\d]\\w*"); + + // build the word list + encodingLookup = analyze(script, regex, new EncodeMethod(encodePrivate)); + + parser.Add("\\b_[A-Za-z\\d]\\w*", new ParseMaster.MatchGroupEvaluator(encodeWithLookup)); + + script = parser.Exec(script); + return script; + } + + private string encodeKeywords(string script) + { + // escape high-ascii values already in the script (i.e. in strings) + if (Encoding == PackerEncoding.HighAscii) script = escape95(script); + // create the parser + ParseMaster parser = new ParseMaster(); + EncodeMethod encode = getEncoder(Encoding); + + // for high-ascii, don't encode single character low-ascii + Regex regex = new Regex( + (Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+" + ); + // build the word list + encodingLookup = analyze(script, regex, encode); + + // encode + parser.Add((Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+", + new ParseMaster.MatchGroupEvaluator(encodeWithLookup)); + + // if encoded, wrap the script in a decoding function + return (script == string.Empty) ? "" : bootStrap(parser.Exec(script), encodingLookup); + } + + private string bootStrap(string packed, WordList keywords) + { + // packed: the packed script + packed = "'" + escape(packed) + "'"; + + // ascii: base for encoding + int ascii = Math.Min(keywords.Sorted.Count, (int) Encoding); + if (ascii == 0) + ascii = 1; + + // count: number of words contained in the script + int count = keywords.Sorted.Count; + + // keywords: list of words contained in the script + foreach (object key in keywords.Protected.Keys) + { + keywords.Sorted[(int) key] = ""; + } + // convert from a string to an array + StringBuilder sbKeywords = new StringBuilder("'"); + foreach (string word in keywords.Sorted) + sbKeywords.Append(word + "|"); + sbKeywords.Remove(sbKeywords.Length-1, 1); + string keywordsout = sbKeywords.ToString() + "'.split('|')"; + + string encode; + string inline = "c"; + + switch (Encoding) + { + case PackerEncoding.Mid: + encode = "function(c){return c.toString(36)}"; + inline += ".toString(a)"; + break; + case PackerEncoding.Normal: + encode = "function(c){return(c<a?\"\":e(parseInt(c/a)))+" + + "((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}"; + inline += ".toString(a)"; + break; + case PackerEncoding.HighAscii: + encode = "function(c){return(c<a?\"\":e(c/a))+" + + "String.fromCharCode(c%a+161)}"; + inline += ".toString(a)"; + break; + default: + encode = "function(c){return c}"; + break; + } + + // decode: code snippet to speed up decoding + string decode = ""; + if (fastDecode) + { + decode = "if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\\\w+'};c=1;}"; + if (Encoding == PackerEncoding.HighAscii) + decode = decode.Replace("\\\\w", "[\\xa1-\\xff]"); + else if (Encoding == PackerEncoding.Numeric) + decode = decode.Replace("e(c)", inline); + if (count == 0) + decode = decode.Replace("c=1", "c=0"); + } + + // boot function + string unpack = "function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p;}"; + Regex r; + if (fastDecode) + { + //insert the decoder + r = new Regex("\\{"); + unpack = r.Replace(unpack, "{" + decode + ";", 1); + } + + if (Encoding == PackerEncoding.HighAscii) + { + // get rid of the word-boundries for regexp matches + r = new Regex("'\\\\\\\\b'\\s*\\+|\\+\\s*'\\\\\\\\b'"); + unpack = r.Replace(unpack, ""); + } + if (Encoding == PackerEncoding.HighAscii || ascii > (int) PackerEncoding.Normal || fastDecode) + { + // insert the encode function + r = new Regex("\\{"); + unpack = r.Replace(unpack, "{e=" + encode + ";", 1); + } + else + { + r = new Regex("e\\(c\\)"); + unpack = r.Replace(unpack, inline); + } + // no need to pack the boot function since i've already done it + string _params = "" + packed + "," + ascii + "," + count + "," + keywordsout; + if (fastDecode) + { + //insert placeholders for the decoder + _params += ",0,{}"; + } + // the whole thing + return "eval(" + unpack + "(" + _params + "))\n"; + } + + private string escape(string input) + { + Regex r = new Regex("([\\\\'])"); + return r.Replace(input, "\\$1"); + } + + private EncodeMethod getEncoder(PackerEncoding encoding) + { + switch (encoding) + { + case PackerEncoding.Mid: + return new EncodeMethod(encode36); + case PackerEncoding.Normal: + return new EncodeMethod(encode62); + case PackerEncoding.HighAscii: + return new EncodeMethod(encode95); + default: + return new EncodeMethod(encode10); + } + } + + private string encode10(int code) + { + return code.ToString(); + } + + //lookups seemed like the easiest way to do this since + // I don't know of an equivalent to .toString(36) + private static string lookup36 = "0123456789abcdefghijklmnopqrstuvwxyz"; + + private string encode36(int code) + { + string encoded = ""; + int i = 0; + do + { + int digit = (code / (int) Math.Pow(36, i)) % 36; + encoded = lookup36[digit] + encoded; + code -= digit * (int) Math.Pow(36, i++); + } while (code > 0); + return encoded; + } + + private static string lookup62 = lookup36 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + private string encode62(int code) + { + string encoded = ""; + int i = 0; + do + { + int digit = (code / (int) Math.Pow(62, i)) % 62; + encoded = lookup62[digit] + encoded; + code -= digit * (int) Math.Pow(62, i++); + } while (code > 0); + return encoded; + } + + private static string lookup95 = "¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"; + + private string encode95(int code) + { + string encoded = ""; + int i = 0; + do + { + int digit = (code / (int) Math.Pow(95, i)) % 95; + encoded = lookup95[digit] + encoded; + code -= digit * (int) Math.Pow(95, i++); + } while (code > 0); + return encoded; + } + + private string escape95(string input) + { + Regex r = new Regex("[\xa1-\xff]"); + return r.Replace(input, new MatchEvaluator(escape95Eval)); + } + + private string escape95Eval(Match match) + { + return "\\x" + ((int) match.Value[0]).ToString("x"); //return hexadecimal value + } + + private string encodeLocalVars(Match match, int offset) + { + int length = match.Groups[offset + 2].Length; + int start = length - Math.Max(length - match.Groups[offset + 3].Length, 0); + return match.Groups[offset + 1].Value.Substring(start, length) + + match.Groups[offset + 4].Value; + } + + private string encodeWithLookup(Match match, int offset) + { + return (string) encodingLookup.Encoded[match.Groups[offset].Value]; + } + + private delegate string EncodeMethod(int code); + + private string encodePrivate(int code) + { + return "_" + code; + } + + private WordList analyze(string input, Regex regex, EncodeMethod encodeMethod) + { + // analyse + // retreive all words in the script + MatchCollection all = regex.Matches(input); + WordList rtrn; + rtrn.Sorted = new StringCollection(); // list of words sorted by frequency + rtrn.Protected = new HybridDictionary(); // dictionary of word->encoding + rtrn.Encoded = new HybridDictionary(); // instances of "protected" words + if (all.Count > 0) + { + StringCollection unsorted = new StringCollection(); // same list, not sorted + HybridDictionary Protected = new HybridDictionary(); // "protected" words (dictionary of word->"word") + HybridDictionary values = new HybridDictionary(); // dictionary of charCode->encoding (eg. 256->ff) + HybridDictionary count = new HybridDictionary(); // word->count + int i = all.Count, j = 0; + string word; + // count the occurrences - used for sorting later + do + { + word = "$" + all[--i].Value; + if (count[word] == null) + { + count[word] = 0; + unsorted.Add(word); + // make a dictionary of all of the protected words in this script + // these are words that might be mistaken for encoding + Protected["$" + (values[j] = encodeMethod(j))] = j++; + } + // increment the word counter + count[word] = (int) count[word] + 1; + } while (i > 0); + /* prepare to sort the word list, first we must protect + words that are also used as codes. we assign them a code + equivalent to the word itself. + e.g. if "do" falls within our encoding range + then we store keywords["do"] = "do"; + this avoids problems when decoding */ + i = unsorted.Count; + string[] sortedarr = new string[unsorted.Count]; + do + { + word = unsorted[--i]; + if (Protected[word] != null) + { + sortedarr[(int) Protected[word]] = word.Substring(1); + rtrn.Protected[(int) Protected[word]] = true; + count[word] = 0; + } + } while (i > 0); + string[] unsortedarr = new string[unsorted.Count]; + unsorted.CopyTo(unsortedarr, 0); + // sort the words by frequency + Array.Sort(unsortedarr, (IComparer) new CountComparer(count)); + j = 0; + /*because there are "protected" words in the list + we must add the sorted words around them */ + do + { + if (sortedarr[i] == null) + sortedarr[i] = unsortedarr[j++].Substring(1); + rtrn.Encoded[sortedarr[i]] = values[i]; + } while (++i < unsortedarr.Length); + rtrn.Sorted.AddRange(sortedarr); + } + return rtrn; + } + + private struct WordList + { + public StringCollection Sorted; + public HybridDictionary Encoded; + public HybridDictionary Protected; + } + + private class CountComparer : IComparer + { + HybridDictionary count; + + public CountComparer(HybridDictionary count) + { + this.count = count; + } + + #region IComparer Members + + public int Compare(object x, object y) + { + return (int) count[y] - (int) count[x]; + } + + #endregion + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/FilterItems.cs b/src/DotNetOpenAuth.BuildTasks/FilterItems.cs new file mode 100644 index 0000000..97631c6 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/FilterItems.cs @@ -0,0 +1,24 @@ +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using Microsoft.Build.Utilities; + using Microsoft.Build.Framework; + + public class FilterItems : Task { + [Required] + public ITaskItem[] InputItems { get; set; } + + [Required] + public ITaskItem[] StartsWithAny { get; set; } + + [Output] + public ITaskItem[] FilteredItems { get; set; } + + public override bool Execute() { + FilteredItems = InputItems.Where(item => StartsWithAny.Any(filter => item.ItemSpec.StartsWith(filter.ItemSpec, StringComparison.OrdinalIgnoreCase))).ToArray(); + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs b/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs new file mode 100644 index 0000000..13a4b8f --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------- +// <copyright file="FixupReferenceHintPaths.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.Reflection; + using System.Text; + using Microsoft.Build.BuildEngine; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + public class FixupReferenceHintPaths : Task { + /// <summary> + /// Gets or sets the projects to fixup references for. + /// </summary> + [Required] + public ITaskItem[] Projects { get; set; } + + /// <summary> + /// Gets or sets the set of full paths to assemblies that may be found in any of the <see cref="Projects"/>. + /// </summary> + [Required] + public ITaskItem[] References { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + if (this.References.Length == 0 || this.Projects.Length == 0) { + this.Log.LogMessage(MessageImportance.Low, "Skipping reference hintpath fixup because no projects or no references were supplied."); + return !this.Log.HasLoggedErrors; + } + + // 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); + } + + foreach (var projectTaskItem in this.Projects) { + var project = new Project(); + Uri projectUri = new Uri(projectTaskItem.GetMetadata("FullPath")); + project.Load(projectTaskItem.ItemSpec); + + foreach (BuildItem referenceItem in project.GetEvaluatedItemsByName("Reference")) { + var referenceAssemblyName = new AssemblyName(referenceItem.Include); + var matchingReference = availableReferences.FirstOrDefault(r => string.Equals(r.Name, referenceAssemblyName.Name, StringComparison.OrdinalIgnoreCase)); + if (matchingReference != null) { + var originalSuppliedReferenceItem = this.References[Array.IndexOf(availableReferences, matchingReference)]; + string hintPath = originalSuppliedReferenceItem.GetMetadata("HintPath"); + if (string.IsNullOrEmpty(hintPath)) { + hintPath = projectUri.MakeRelativeUri(new Uri(matchingReference.CodeBase)).OriginalString.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + } + this.Log.LogMessage("Fixing up HintPath to \"{0}\" in project \"{1}\".", referenceAssemblyName.Name, projectTaskItem.ItemSpec); + referenceItem.SetMetadata("HintPath", hintPath); + } + } + + project.Save(projectTaskItem.ItemSpec); + } + + return !this.Log.HasLoggedErrors; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs b/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs new file mode 100644 index 0000000..6c71740 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------- +// <copyright file="FixupShippingToolSamples.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.Collections; + 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> + /// Removes imports that only apply when a shipping tool sample builds as part of + /// the entire project, but not when it's part of a source code sample. + /// </summary> + public class FixupShippingToolSamples : Task { + [Required] + public ITaskItem[] Projects { get; set; } + + public string[] RemoveImportsStartingWith { get; set; } + + public ITaskItem[] AddReferences { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + /// <returns></returns> + public override bool Execute() { + foreach (ITaskItem projectTaskItem in this.Projects) { + this.Log.LogMessage("Fixing up the {0} sample for shipping as source code.", Path.GetFileNameWithoutExtension(projectTaskItem.ItemSpec)); + + var project = new Project(); + Uri projectUri = new Uri(projectTaskItem.GetMetadata("FullPath")); + project.Load(projectTaskItem.ItemSpec, ProjectLoadSettings.IgnoreMissingImports); + + 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); + } + + return !this.Log.HasLoggedErrors; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs new file mode 100644 index 0000000..2068a6b --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace DotNetOpenAuth.BuildTasks { + public class GetBuildVersion : Task { + + /// <summary> + /// Gets the version string to use in the compiled assemblies. + /// </summary> + [Output] + public string Version { get; private set; } + + /// <summary> + /// Gets the Git revision control commit id for HEAD (the current source code version). + /// </summary> + [Output] + public string GitCommitId { get; private set; } + + /// <summary> + /// The file that contains the version base (Major.Minor.Build) to use. + /// </summary> + [Required] + public string VersionFile { get; set; } + + /// <summary> + /// Gets or sets the parent directory of the .git directory. + /// </summary> + public string GitRepoRoot { get; set; } + + public override bool Execute() { + try { + Version typedVersion = ReadVersionFromFile(); + typedVersion = new Version(typedVersion.Major, typedVersion.Minor, typedVersion.Build, CalculateJDate(DateTime.Now)); + Version = typedVersion.ToString(); + + this.GitCommitId = GetGitHeadCommitId(); + } catch (ArgumentOutOfRangeException ex) { + Log.LogErrorFromException(ex); + return false; + } + + return true; + } + + private string GetGitHeadCommitId() { + if (string.IsNullOrEmpty(this.GitRepoRoot)) { + return string.Empty; + } + + string commitId = string.Empty; + + // First try asking Git for the HEAD commit id + try { + string cmdPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe"); + var psi = new ProcessStartInfo(cmdPath, "/c git rev-parse HEAD") { + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true, + RedirectStandardOutput = true, + UseShellExecute = false + }; + var git = Process.Start(psi); + commitId = git.StandardOutput.ReadLine(); + git.WaitForExit(); + if (git.ExitCode != 0) { + commitId = string.Empty; + } + if (commitId != null) { + commitId = commitId.Trim(); + if (commitId.Length == 40) { + return commitId; + } + } + } catch (InvalidOperationException) { + } catch (Win32Exception) { + } + + // Failing being able to use the git command to figure out the HEAD commit ID, try the filesystem directly. + try { + string headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim(); + if (headContent.StartsWith("ref:", StringComparison.Ordinal)) { + string refName = headContent.Substring(5).Trim(); + string refPath = Path.Combine(this.GitRepoRoot, ".git/" + refName); + if (File.Exists(refPath)) { + commitId = File.ReadAllText(refPath).Trim(); + } else { + string packedRefPath = Path.Combine(this.GitRepoRoot, ".git/packed-refs"); + string matchingLine = File.ReadAllLines(packedRefPath).FirstOrDefault(line => line.EndsWith(refName)); + if (matchingLine != null) { + commitId = matchingLine.Substring(0, matchingLine.IndexOf(' ')); + } + } + } else { + commitId = headContent; + } + } catch (FileNotFoundException) { + } catch (DirectoryNotFoundException) { + } + + commitId = commitId ?? String.Empty; // doubly-be sure it's not null, since in some error cases it can be. + return commitId.Trim(); + } + + private Version ReadVersionFromFile() { + string[] lines = File.ReadAllLines(VersionFile); + string versionLine = lines[0]; + return new Version(versionLine); + } + + private int CalculateJDate(DateTime date) { + int yearLastDigit = date.Year - 2000; // can actually be two digits in or after 2010 + DateTime firstOfYear = new DateTime(date.Year, 1, 1); + int dayOfYear = (date - firstOfYear).Days + 1; + int jdate = yearLastDigit * 1000 + dayOfYear; + return jdate; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/JsPack.cs b/src/DotNetOpenAuth.BuildTasks/JsPack.cs new file mode 100644 index 0000000..fa8c7f0 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/JsPack.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------- +// <copyright file="JsPack.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.Framework; + using Microsoft.Build.Utilities; + + /// <summary> + /// Compresses .js files. + /// </summary> + public class JsPack : Task { + /// <summary> + /// The Javascript packer to use. + /// </summary> + private Dean.Edwards.ECMAScriptPacker packer = new Dean.Edwards.ECMAScriptPacker(); + + /// <summary> + /// Gets or sets the set of javascript files to compress. + /// </summary> + /// <value>The inputs.</value> + [Required] + public ITaskItem[] Inputs { get; set; } + + /// <summary> + /// Gets or sets the paths where the packed javascript files should be saved. + /// </summary> + /// <value>The outputs.</value> + [Required] + public ITaskItem[] Outputs { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + /// <returns>A value indicating whether the packing was successful.</returns> + public override bool Execute() { + if (this.Inputs.Length != this.Outputs.Length) { + Log.LogError("{0} inputs and {1} outputs given.", this.Inputs.Length, this.Outputs.Length); + return false; + } + + for (int i = 0; i < this.Inputs.Length; i++) { + if (!File.Exists(this.Outputs[i].ItemSpec) || File.GetLastWriteTime(this.Outputs[i].ItemSpec) < File.GetLastWriteTime(this.Inputs[i].ItemSpec)) { + Log.LogMessage(MessageImportance.Normal, TaskStrings.PackingJsFile, this.Inputs[i].ItemSpec, this.Outputs[i].ItemSpec); + string input = File.ReadAllText(this.Inputs[i].ItemSpec); + string output = this.packer.Pack(input); + if (!Directory.Exists(Path.GetDirectoryName(this.Outputs[i].ItemSpec))) { + Directory.CreateDirectory(Path.GetDirectoryName(this.Outputs[i].ItemSpec)); + } + + // Minification removes all comments, including copyright notices + // that must remain. So if there's metadata on this item with + // a copyright notice on it, stick it on the front of the file. + string copyright = this.Inputs[i].GetMetadata("Copyright"); + if (!string.IsNullOrEmpty(copyright)) { + output = "/*" + copyright + "*/" + output; + } + + File.WriteAllText(this.Outputs[i].ItemSpec, output, Encoding.UTF8); + } else { + Log.LogMessage(MessageImportance.Low, TaskStrings.SkipPackingJsFile, this.Inputs[i].ItemSpec); + } + } + + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs new file mode 100644 index 0000000..1a8a17d --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs @@ -0,0 +1,103 @@ +//----------------------------------------------------------------------- +// <copyright file="MergeProjectWithVSTemplate.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.Xml.Linq; + using System.IO; + using Microsoft.Build.BuildEngine; + using System.Diagnostics.Contracts; + + public class MergeProjectWithVSTemplate : Task { + internal const string VSTemplateNamespace = "http://schemas.microsoft.com/developer/vstemplate/2005"; + + [Required] + public string[] ProjectItemTypes { get; set; } + + [Required] + public string[] ReplaceParametersExtensions { get; set; } + + [Required] + public ITaskItem[] Templates { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + foreach(ITaskItem sourceTemplateTaskItem in this.Templates) { + var template = XElement.Load(sourceTemplateTaskItem.ItemSpec); + var templateContentElement = template.Element(XName.Get("TemplateContent", VSTemplateNamespace)); + var projectElement = templateContentElement.Element(XName.Get("Project", VSTemplateNamespace)); + if (projectElement == null) { + Log.LogMessage("Skipping merge of \"{0}\" with a project because no project was referenced from the template.", sourceTemplateTaskItem.ItemSpec); + continue; + } + + var projectPath = Path.Combine(Path.GetDirectoryName(sourceTemplateTaskItem.ItemSpec), projectElement.Attribute("File").Value); + Log.LogMessage("Merging project \"{0}\" with \"{1}\".", projectPath, sourceTemplateTaskItem.ItemSpec); + var sourceProject = new Project(); + sourceProject.Load(projectPath); + + // 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)); + var itemName = XName.Get("ProjectItem", VSTemplateNamespace); + var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.Include), StringComparison.OrdinalIgnoreCase)); + if (projectItem == null) { + projectItem = new XElement(itemName, Path.GetFileName(item.Include)); + parentNode.Add(projectItem); + } + if (replaceParameters) { + projectItem.SetAttributeValue("ReplaceParameters", "true"); + } + } + } + + template.Save(sourceTemplateTaskItem.ItemSpec); + } + + return !Log.HasLoggedErrors; + } + + private static XElement FindOrCreateParent(string directoryName, XElement projectElement) { + Contract.Requires<ArgumentNullException>(projectElement != null); + + if (string.IsNullOrEmpty(directoryName)) { + return projectElement; + } + + string[] segments = directoryName.Split(Path.DirectorySeparatorChar); + XElement parent = projectElement; + for (int i = 0; i < segments.Length; i++) { + var candidateName = XName.Get("Folder", VSTemplateNamespace); + var candidate = parent.Elements(XName.Get("Folder", VSTemplateNamespace)).FirstOrDefault(n => n.Attribute("Name").Value == segments[i]); + if (candidate == null) { + candidate = new XElement( + candidateName, + new XAttribute("Name", segments[i])); + parent.Add(candidate); + } + + parent = candidate; + } + + return parent; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/ParseMaster.cs b/src/DotNetOpenAuth.BuildTasks/ParseMaster.cs new file mode 100644 index 0000000..7edba29 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/ParseMaster.cs @@ -0,0 +1,250 @@ +using System; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections; +using System.Collections.Specialized; + +/* + ParseMaster, version 1.0 (pre-release) (2005/02/01) x4 + Copyright 2005, Dean Edwards + Web: http://dean.edwards.name/ + + This software is licensed under the CC-GNU LGPL + Web: http://creativecommons.org/licenses/LGPL/2.1/ + + Ported to C# by Jesse Hansen, twindagger2k@msn.com +*/ + +namespace Dean.Edwards +{ + /// <summary> + /// a multi-pattern parser + /// </summary> + internal class ParseMaster + { + // used to determine nesting levels + Regex GROUPS = new Regex("\\("), + SUB_REPLACE = new Regex("\\$"), + INDEXED = new Regex("^\\$\\d+$"), + ESCAPE = new Regex("\\\\."), + QUOTE = new Regex("'"), + DELETED = new Regex("\\x01[^\\x01]*\\x01"); + + /// <summary> + /// Delegate to call when a regular expression is found. + /// Use match.Groups[offset + <group number>].Value to get + /// the correct subexpression + /// </summary> + public delegate string MatchGroupEvaluator(Match match, int offset); + + private string DELETE(Match match, int offset) + { + return "\x01" + match.Groups[offset].Value + "\x01"; + } + + private bool ignoreCase = false; + private char escapeChar = '\0'; + + /// <summary> + /// Ignore Case? + /// </summary> + public bool IgnoreCase + { + get { return ignoreCase; } + set { ignoreCase = value; } + } + + /// <summary> + /// Escape Character to use + /// </summary> + public char EscapeChar + { + get { return escapeChar; } + set { escapeChar = value; } + } + + /// <summary> + /// Add an expression to be deleted + /// </summary> + /// <param name="expression">Regular Expression String</param> + public void Add(string expression) + { + Add(expression, string.Empty); + } + + /// <summary> + /// Add an expression to be replaced with the replacement string + /// </summary> + /// <param name="expression">Regular Expression String</param> + /// <param name="replacement">Replacement String. Use $1, $2, etc. for groups</param> + public void Add(string expression, string replacement) + { + if (replacement == string.Empty) + add(expression, new MatchGroupEvaluator(DELETE)); + + add(expression, replacement); + } + + /// <summary> + /// Add an expression to be replaced using a callback function + /// </summary> + /// <param name="expression">Regular expression string</param> + /// <param name="replacement">Callback function</param> + public void Add(string expression, MatchGroupEvaluator replacement) + { + add(expression, replacement); + } + + /// <summary> + /// Executes the parser + /// </summary> + /// <param name="input">input string</param> + /// <returns>parsed string</returns> + public string Exec(string input) + { + return DELETED.Replace(unescape(getPatterns().Replace(escape(input), new MatchEvaluator(replacement))), string.Empty); + //long way for debugging + /*input = escape(input); + Regex patterns = getPatterns(); + input = patterns.Replace(input, new MatchEvaluator(replacement)); + input = DELETED.Replace(input, string.Empty); + return input;*/ + } + + ArrayList patterns = new ArrayList(); + private void add(string expression, object replacement) + { + Pattern pattern = new Pattern(); + pattern.expression = expression; + pattern.replacement = replacement; + //count the number of sub-expressions + // - add 1 because each group is itself a sub-expression + pattern.length = GROUPS.Matches(internalEscape(expression)).Count + 1; + + //does the pattern deal with sup-expressions? + if (replacement is string && SUB_REPLACE.IsMatch((string) replacement)) + { + string sreplacement = (string) replacement; + // a simple lookup (e.g. $2) + if (INDEXED.IsMatch(sreplacement)) + { + pattern.replacement = int.Parse(sreplacement.Substring(1)) - 1; + } + } + + patterns.Add(pattern); + } + + /// <summary> + /// builds the patterns into a single regular expression + /// </summary> + /// <returns></returns> + private Regex getPatterns() + { + StringBuilder rtrn = new StringBuilder(string.Empty); + foreach (object pattern in patterns) + { + rtrn.Append(((Pattern) pattern).ToString() + "|"); + } + rtrn.Remove(rtrn.Length - 1, 1); + return new Regex(rtrn.ToString(), ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None ); + } + + /// <summary> + /// Global replacement function. Called once for each match found + /// </summary> + /// <param name="match">Match found</param> + private string replacement(Match match) + { + int i = 1, j = 0; + Pattern pattern; + //loop through the patterns + while (!((pattern = (Pattern) patterns[j++]) == null)) + { + //do we have a result? + if (match.Groups[i].Value != string.Empty) + { + object replacement = pattern.replacement; + if (replacement is MatchGroupEvaluator) + { + return ((MatchGroupEvaluator) replacement)(match, i); + } + else if (replacement is int) + { + return match.Groups[(int) replacement + i].Value; + } + else + { + //string, send to interpreter + return replacementString(match, i, (string) replacement, pattern.length); + } + } + else //skip over references to sub-expressions + i += pattern.length; + } + return match.Value; //should never be hit, but you never know + } + + /// <summary> + /// Replacement function for complicated lookups (e.g. Hello $3 $2) + /// </summary> + private string replacementString(Match match, int offset, string replacement, int length) + { + while (length > 0) + { + replacement = replacement.Replace("$" + length--, match.Groups[offset + length].Value); + } + return replacement; + } + + private StringCollection escaped = new StringCollection(); + + //encode escaped characters + private string escape(string str) + { + if (escapeChar == '\0') + return str; + Regex escaping = new Regex("\\\\(.)"); + return escaping.Replace(str, new MatchEvaluator(escapeMatch)); + } + + private string escapeMatch(Match match) + { + escaped.Add(match.Groups[1].Value); + return "\\"; + } + + //decode escaped characters + private int unescapeIndex = 0; + private string unescape(string str) + { + if (escapeChar == '\0') + return str; + Regex unescaping = new Regex("\\" + escapeChar); + return unescaping.Replace(str, new MatchEvaluator(unescapeMatch)); + } + + private string unescapeMatch(Match match) + { + return "\\" + escaped[unescapeIndex++]; + } + + private string internalEscape(string str) + { + return ESCAPE.Replace(str, ""); + } + + //subclass for each pattern + private class Pattern + { + public string expression; + public object replacement; + public int length; + + public override string ToString() + { + return "(" + expression + ")"; + } + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6fdcc21 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CustomMsBuildTasks")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CustomMsBuildTasks")] +[assembly: AssemblyCopyright("Copyright © 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("952e3aaa-5dc6-4b71-8c9c-6b485263be19")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/DotNetOpenAuth.BuildTasks/Purge.cs b/src/DotNetOpenAuth.BuildTasks/Purge.cs new file mode 100644 index 0000000..e19e485 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/Purge.cs @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------- +// <copyright file="Purge.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.Utilities; + using Microsoft.Build.Framework; + using System.IO; + using System.Text.RegularExpressions; + + /// <summary> + /// Purges directory trees of all directories and files that are not on a whitelist. + /// </summary> + /// <remarks> + /// This task performs a function similar to robocopy's /MIR switch, except that + /// this task does not require that an entire directory tree be used as the source + /// in order to purge old files from the destination. + /// </remarks> + public class Purge : Task { + /// <summary> + /// Initializes a new instance of the <see cref="Purge"/> class. + /// </summary> + public Purge() { + this.PurgeEmptyDirectories = true; + } + + /// <summary> + /// Gets or sets the root directories to purge. + /// </summary> + /// <value>The directories.</value> + [Required] + public string[] Directories { get; set; } + + /// <summary> + /// Gets or sets the files that should be NOT be purged. + /// </summary> + public ITaskItem[] IntendedFiles { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether empty directories will be deleted. + /// </summary> + /// <value> + /// The default value is <c>true</c>. + /// </value> + public bool PurgeEmptyDirectories { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + public override bool Execute() { + HashSet<string> intendedFiles = new HashSet<string>(this.IntendedFiles.Select(file => file.GetMetadata("FullPath")), StringComparer.OrdinalIgnoreCase); + + foreach (string directory in this.Directories.Select(dir => NormalizePath(dir)).Where(dir => Directory.Exists(dir))) { + foreach (string existingFile in Directory.GetFiles(directory, "*", SearchOption.AllDirectories)) { + if (!intendedFiles.Contains(existingFile)) { + this.Log.LogWarning("Purging file \"{0}\".", existingFile); + File.Delete(existingFile); + } + } + + if (this.PurgeEmptyDirectories) { + foreach (string subdirectory in Directory.GetDirectories(directory, "*", SearchOption.AllDirectories)) { + // We have to check for the existance of the directory because it MAY be + // a descendent of a directory we already deleted in this loop. + if (Directory.Exists(subdirectory)) { + if (Directory.GetDirectories(subdirectory).Length == 0 && Directory.GetFiles(subdirectory).Length == 0) { + this.Log.LogWarning("Purging empty directory \"{0}\".", subdirectory); + Directory.Delete(subdirectory); + } + } + } + } + } + + return !this.Log.HasLoggedErrors; + } + + private static string NormalizePath(string path) { + return Regex.Replace(path, @"\\+", @"\"); + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs new file mode 100644 index 0000000..a0ba386 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------- +// <copyright file="ReSignDelaySignedAssemblies.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + public class ReSignDelaySignedAssemblies : SnToolTask { + /// <summary> + /// Gets or sets the key file to use for signing. + /// </summary> + public ITaskItem KeyFile { get; set; } + + /// <summary> + /// Gets or sets the key container. + /// </summary> + public ITaskItem KeyContainer { get; set; } + + /// <summary> + /// Gets or sets the assemblies to re-sign. + /// </summary> + public ITaskItem[] Assemblies { get; set; } + + /// <summary> + /// Generates the command line commands. + /// </summary> + protected override string GenerateCommandLineCommands() { + ////if (this.Assemblies.Length != 1) { + //// throw new NotSupportedException("Exactly 1 assembly for signing is supported."); + ////} + CommandLineBuilder args = new CommandLineBuilder(); + args.AppendSwitch("-q"); + + if (this.KeyFile != null) { + args.AppendSwitch("-R"); + } else if (this.KeyContainer != null) { + args.AppendSwitch("-Rc"); + } else { + throw new InvalidOperationException("Either KeyFile or KeyContainer must be set."); + } + + args.AppendFileNameIfNotNull(this.Assemblies[0]); + if (this.KeyFile != null) { + args.AppendFileNameIfNotNull(this.KeyFile); + } else { + args.AppendFileNameIfNotNull(this.KeyContainer); + } + + return args.ToString(); + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs b/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs new file mode 100644 index 0000000..2de5976 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; + +namespace DotNetOpenAuth.BuildTasks { + public class SetEnvironmentVariable : Task { + public SetEnvironmentVariable() { + Scope = EnvironmentVariableTarget.Process; + } + + /// <summary> + /// The name of the environment variable to set or clear. + /// </summary> + [Required] + public string Name { get; set; } + /// <summary> + /// The value of the environment variable, or the empty string to clear it. + /// </summary> + [Required] + public string Value { get; set; } + /// <summary> + /// The target environment for the variable. Machine, User, or Process. + /// </summary> + public EnvironmentVariableTarget Scope { get; set; } + + public override bool Execute() { + Environment.SetEnvironmentVariable(Name, Value, Scope); + return true; + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs b/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs new file mode 100644 index 0000000..2e69926 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------- +// <copyright file="SignatureVerification.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using Microsoft.Build.Utilities; + + public class SignatureVerification : SnToolTask { + /// <summary> + /// Gets or sets a value indicating whether to register the given assembly and public key token + /// for skip verification or clear any pre-existing skip verification entry. + /// </summary> + public bool SkipVerification { get; set; } + + /// <summary> + /// Gets or sets the name of the assembly. + /// </summary> + /// <value>The name of the assembly.</value> + public string AssemblyName { get; set; } + + /// <summary> + /// Gets or sets the public key token. + /// </summary> + /// <value>The public key token.</value> + public string PublicKeyToken { get; set; } + + /// <summary> + /// Generates the command line commands. + /// </summary> + protected override string GenerateCommandLineCommands() { + CommandLineBuilder builder = new CommandLineBuilder(); + builder.AppendSwitch("-q"); + if (this.SkipVerification) { + builder.AppendSwitch("-Vr"); + } else { + builder.AppendSwitch("-Vu"); + } + + builder.AppendFileNameIfNotNull(this.AssemblyName + "," + this.PublicKeyToken); + return builder.ToString(); + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs b/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs new file mode 100644 index 0000000..29896fe --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------- +// <copyright file="SnToolTask.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using System; + using System.IO; + using Microsoft.Build.Utilities; + + public abstract class SnToolTask : ToolTask { + /// <summary> + /// Gets the name of the tool. + /// </summary> + /// <value>The name of the tool.</value> + protected override string ToolName { + get { return "sn.exe"; } + } + + /// <summary> + /// Generates the full path to tool. + /// </summary> + protected override string GenerateFullPathToTool() { + string[] versions = new[] { "v6.0A", "v6.1", "v7.0a" }; + string fullPath = null; + foreach (string version in versions) { + fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Microsoft SDKs\Windows\" + version + @"\bin\" + this.ToolName); + if (File.Exists(fullPath)) { + return fullPath; + } + } + + throw new FileNotFoundException("Unable to find sn.exe tool.", fullPath); + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs b/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs new file mode 100644 index 0000000..17647fd --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.4927 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace DotNetOpenAuth.BuildTasks { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // 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.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class TaskStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TaskStrings() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOpenAuth.BuildTasks.TaskStrings", typeof(TaskStrings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// <summary> + /// Looks up a localized string similar to Web application '{0}' deleted.. + /// </summary> + internal static string DeletedWebApplication { + get { + return ResourceManager.GetString("DeletedWebApplication", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The {0} and {1} properties must be set to arrays of equal length.. + /// </summary> + internal static string MismatchingArrayLengths { + get { + return ResourceManager.GetString("MismatchingArrayLengths", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to No web site with the name '{0}' found.. + /// </summary> + internal static string NoMatchingWebSiteFound { + get { + return ResourceManager.GetString("NoMatchingWebSiteFound", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Packing javascript resource "{0}" into "{1}".. + /// </summary> + internal static string PackingJsFile { + get { + return ResourceManager.GetString("PackingJsFile", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Skip packing "{0}" because its packed version is up to date.. + /// </summary> + internal static string SkipPackingJsFile { + get { + return ResourceManager.GetString("SkipPackingJsFile", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Web application '{0}' was not found, so it was not deleted.. + /// </summary> + internal static string WebApplicationNotFoundSoNotDeleted { + get { + return ResourceManager.GetString("WebApplicationNotFoundSoNotDeleted", resourceCulture); + } + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/TaskStrings.resx b/src/DotNetOpenAuth.BuildTasks/TaskStrings.resx new file mode 100644 index 0000000..50e1592 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/TaskStrings.resx @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="DeletedWebApplication" xml:space="preserve"> + <value>Web application '{0}' deleted.</value> + </data> + <data name="MismatchingArrayLengths" xml:space="preserve"> + <value>The {0} and {1} properties must be set to arrays of equal length.</value> + </data> + <data name="NoMatchingWebSiteFound" xml:space="preserve"> + <value>No web site with the name '{0}' found.</value> + </data> + <data name="PackingJsFile" xml:space="preserve"> + <value>Packing javascript resource "{0}" into "{1}".</value> + </data> + <data name="SkipPackingJsFile" xml:space="preserve"> + <value>Skip packing "{0}" because its packed version is up to date.</value> + </data> + <data name="WebApplicationNotFoundSoNotDeleted" xml:space="preserve"> + <value>Web application '{0}' was not found, so it was not deleted.</value> + </data> +</root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.BuildTasks/Trim.cs b/src/DotNetOpenAuth.BuildTasks/Trim.cs new file mode 100644 index 0000000..972b87d --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/Trim.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------- +// <copyright file="Trim.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.BuildTasks { + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// <summary> + /// Trims item identities or metadata. + /// </summary> + public class Trim : Task { + /// <summary> + /// Gets or sets the name of the metadata to trim. Leave empty or null to operate on itemspec. + /// </summary> + /// <value>The name of the metadata.</value> + public string MetadataName { get; set; } + + /// <summary> + /// Gets or sets the characters that should be trimmed off if found at the start of items' ItemSpecs. + /// </summary> + public string StartCharacters { get; set; } + + /// <summary> + /// Gets or sets the characters that should be trimmed off if found at the end of items' ItemSpecs. + /// </summary> + public string EndCharacters { get; set; } + + /// <summary> + /// Gets or sets the substring that should be trimmed along with everything that appears after it. + /// </summary> + public string AllAfter { get; set; } + + /// <summary> + /// Gets or sets the items with ItemSpec's to be trimmed. + /// </summary> + [Required] + public ITaskItem[] Inputs { get; set; } + + /// <summary> + /// Gets or sets the items with trimmed ItemSpec strings. + /// </summary> + [Output] + public ITaskItem[] Outputs { get; set; } + + /// <summary> + /// Executes this instance. + /// </summary> + /// <returns>A value indicating whether the task completed successfully.</returns> + public override bool Execute() { + this.Outputs = new ITaskItem[this.Inputs.Length]; + for (int i = 0; i < this.Inputs.Length; i++) { + this.Outputs[i] = new TaskItem(this.Inputs[i]); + string value = string.IsNullOrEmpty(this.MetadataName) ? this.Outputs[i].ItemSpec : this.Outputs[i].GetMetadata(this.MetadataName); + if (!string.IsNullOrEmpty(this.StartCharacters)) { + value = value.TrimStart(this.StartCharacters.ToCharArray()); + } + if (!string.IsNullOrEmpty(this.EndCharacters)) { + value = value.TrimEnd(this.EndCharacters.ToCharArray()); + } + if (!string.IsNullOrEmpty(this.AllAfter)) { + int index = value.IndexOf(this.AllAfter); + if (index >= 0) { + value = value.Substring(0, index); + } + } + if (string.IsNullOrEmpty(this.MetadataName)) { + this.Outputs[i].ItemSpec = value; + } else { + this.Outputs[i].SetMetadata(this.MetadataName, value); + } + } + + return true; + } + } +} diff --git a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs index 05ac306..fd77746 100644 --- a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs @@ -5,6 +5,8 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test.Messaging { + using System; + using System.Collections.Specialized; using System.Web; using DotNetOpenAuth.Messaging; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -92,5 +94,59 @@ namespace DotNetOpenAuth.Test.Messaging { HttpRequestInfo info = new HttpRequestInfo(); Assert.IsNull(info.QueryString["hi"]); } + + /// <summary> + /// Verifies SSL forwarders are correctly handled when they supply X_FORWARDED_PROTO and HOST + /// </summary> + [TestMethod] + public void GetPublicFacingUrlSSLForwarder1() { + HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); + var serverVariables = new NameValueCollection(); + serverVariables["HTTP_X_FORWARDED_PROTO"] = "https"; + serverVariables["HTTP_HOST"] = "somehost"; + Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables); + Uri expected = new Uri("https://somehost/a.aspx?a=b"); + Assert.AreEqual(expected, actual); + } + + /// <summary> + /// Verifies SSL forwarders are correctly handled when they supply X_FORWARDED_PROTO and HOST:port + /// </summary> + [TestMethod] + public void GetPublicFacingUrlSSLForwarder2() { + HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); + var serverVariables = new NameValueCollection(); + serverVariables["HTTP_X_FORWARDED_PROTO"] = "https"; + serverVariables["HTTP_HOST"] = "somehost:999"; + Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables); + Uri expected = new Uri("https://somehost:999/a.aspx?a=b"); + Assert.AreEqual(expected, actual); + } + + /// <summary> + /// Verifies SSL forwarders are correctly handled when they supply just HOST + /// </summary> + [TestMethod] + public void GetPublicFacingUrlSSLForwarder3() { + HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); + var serverVariables = new NameValueCollection(); + serverVariables["HTTP_HOST"] = "somehost"; + Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables); + Uri expected = new Uri("http://somehost/a.aspx?a=b"); + Assert.AreEqual(expected, actual); + } + + /// <summary> + /// Verifies SSL forwarders are correctly handled when they supply just HOST:port + /// </summary> + [TestMethod] + public void GetPublicFacingUrlSSLForwarder4() { + HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b"); + var serverVariables = new NameValueCollection(); + serverVariables["HTTP_HOST"] = "somehost:79"; + Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables); + Uri expected = new Uri("http://somehost:79/a.aspx?a=b"); + Assert.AreEqual(expected, actual); + } } } diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs index 24171e1..b9e7436 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs @@ -300,7 +300,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { [TestMethod] public void CopyTo() { ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); - IDictionary<string, string> targetAsDictionary = ((IDictionary<string, string>)target); + IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target; KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[target.Count + 1]; int arrayIndex = 1; target.CopyTo(array, arrayIndex); @@ -317,7 +317,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { [TestMethod] public void ContainsKeyValuePair() { ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message); - IDictionary<string, string> targetAsDictionary = ((IDictionary<string, string>)target); + IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target; Assert.IsFalse(target.Contains(new KeyValuePair<string, string>("age", "1"))); Assert.IsTrue(target.Contains(new KeyValuePair<string, string>("age", "0"))); @@ -333,7 +333,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { [TestMethod] public void ClearValues() { MessageDictionary target = this.MessageDescriptions.GetAccessor(this.message); - IDictionary<string, string> targetAsDictionary = ((IDictionary<string, string>)target); + IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target; this.message.Name = "Andrew"; this.message.Age = 15; targetAsDictionary["extra"] = "value"; diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs index 19e6a82..9deaecd 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs @@ -28,7 +28,12 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { [TestMethod] 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] diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs index 48547b7..aae119d 100644 --- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs +++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Test.Mocks { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; + using DotNetOpenAuth.Test.OAuth; internal class InMemoryTokenManager : IConsumerTokenManager, IServiceProviderTokenManager { private KeyedCollectionDelegate<string, ConsumerInfo> consumers = new KeyedCollectionDelegate<string, ConsumerInfo>(c => c.Key); diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs index fcdb5e8..6477510 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using DotNetOpenAuth.Test.Mocks; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index 856f164..e215bc1 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -356,6 +356,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { { "Name", "Andrew" }, { "Location", "http://hostb/pathB" }, { "Timestamp", XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc) }, + { "realm" , "someValue" }, }; IProtocolMessage requestMessage = this.channel.ReadFromRequest(CreateHttpRequestInfo(scheme, fields)); Assert.IsNotNull(requestMessage); @@ -364,6 +365,12 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.AreEqual(15, testMessage.Age); Assert.AreEqual("Andrew", testMessage.Name); Assert.AreEqual("http://hostb/pathB", testMessage.Location.AbsoluteUri); + if (scheme == HttpDeliveryMethods.AuthorizationHeaderRequest) { + // The realm value should be ignored in the authorization header + Assert.IsFalse(((IMessage)testMessage).ExtraData.ContainsKey("realm")); + } else { + Assert.AreEqual("someValue", ((IMessage)testMessage).ExtraData["realm"]); + } } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs index 627db8f..80a1c01 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs index 6e566c8..49549f5 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth; diff --git a/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs b/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs index 625f416..89105ef 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { /// <summary> /// Information necessary to initialize a <see cref="Consumer"/>, /// and to tell a <see cref="ServiceProvider"/> about it. diff --git a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs index ce548a9..c3ef6c2 100644 --- a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs +++ b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; diff --git a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs index ce8070b..e60a9e2 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { using DotNetOpenAuth.OAuth; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs index 760a9e9..3430103 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs index 1051092..fa05e94 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs @@ -34,8 +34,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { var request = new StoreRequest(); var newAttribute = new AttributeValues( IncrementingAttribute, - "val" + (incrementingAttributeValue++).ToString(), - "val" + (incrementingAttributeValue++).ToString()); + "val" + (this.incrementingAttributeValue++).ToString(), + "val" + (this.incrementingAttributeValue++).ToString()); request.Attributes.Add(newAttribute); var successResponse = new StoreResponse(); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs index 9f849ea..1fb3160 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs @@ -42,6 +42,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.IsNull(sreg); // Make sure we're still able to send an sreg response. + // (not really a valid scenario, since OPs don't have public access + // to directly create a response without a request. var sregResponse = new ClaimsResponse(); this.request.AddResponseExtension(sregResponse); ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request); @@ -49,12 +51,18 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreSame(sregResponse, extensions.Single()); } + [TestMethod] + public void NegativeResponse() { + this.request.IsAuthenticated = false; + ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request); + } + /// <summary> /// Verifies sreg coming in is seen as sreg. /// </summary> [TestMethod] public void UnifyExtensionsAsSregWithSreg() { - var sregInjected = new ClaimsRequest { + var sregInjected = new ClaimsRequest(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns) { Nickname = DemandLevel.Request, }; this.extensions.Add(sregInjected); @@ -63,7 +71,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual(DemandLevel.Request, sreg.Nickname); Assert.AreEqual(DemandLevel.NoRequest, sreg.FullName); - var sregResponse = new ClaimsResponse(); + var sregResponse = sreg.CreateResponse(); this.request.AddResponseExtension(sregResponse); ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request); var extensions = this.GetResponseExtensions(); @@ -91,7 +99,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { /// </summary> [TestMethod] public void UnifyExtensionsAsSregWithBothSregAndAX() { - var sregInjected = new ClaimsRequest { + var sregInjected = new ClaimsRequest(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns) { Nickname = DemandLevel.Request, }; this.extensions.Add(sregInjected); @@ -103,9 +111,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual(DemandLevel.Request, sreg.Nickname); Assert.AreEqual(DemandLevel.NoRequest, sreg.Email); - var sregResponseInjected = new ClaimsResponse { - Nickname = "andy", - }; + var sregResponseInjected = sreg.CreateResponse(); + sregResponseInjected.Nickname = "andy"; this.request.AddResponseExtension(sregResponseInjected); var axResponseInjected = new FetchResponse(); axResponseInjected.Attributes.Add(WellKnownAttributes.Contact.Email, "a@b.com"); @@ -134,9 +141,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { Assert.AreEqual(DemandLevel.Require, sreg.FullName); Assert.AreEqual(DemandLevel.NoRequest, sreg.Language); - var sregResponse = new ClaimsResponse { - Nickname = "andy", - }; + var sregResponse = sreg.CreateResponse(); + sregResponse.Nickname = "andy"; this.request.AddResponseExtension(sregResponse); ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request); var extensions = this.GetResponseExtensions(); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs index ba5e335..7edec09 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.OpenId { +namespace DotNetOpenAuth.Test.OpenId.Extensions { using System.Linq; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Extensions; diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs index 5fe05c1..655e616 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.OpenId { +namespace DotNetOpenAuth.Test.OpenId.Extensions { using System.Collections.Generic; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs index 10497b2..0ddc76b 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs @@ -112,11 +112,11 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { var rp = CreateRelyingParty(); // First verify that delegating identifiers work - Assert.IsTrue(AuthenticationRequest.Create(id, rp, realm, returnTo, false).Any(), "The delegating identifier should have not generated any results."); + Assert.IsTrue(AuthenticationRequest.Create(id, rp, this.realm, this.returnTo, false).Any(), "The delegating identifier should have not generated any results."); // Now disable them and try again. rp.SecuritySettings.RejectDelegatingIdentifiers = true; - Assert.IsFalse(AuthenticationRequest.Create(id, rp, realm, returnTo, false).Any(), "The delegating identifier should have not generated any results."); + Assert.IsFalse(AuthenticationRequest.Create(id, rp, this.realm, this.returnTo, false).Any(), "The delegating identifier should have not generated any results."); } /// <summary> diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd index a637d1f..d2b5f14 100644 --- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd +++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd @@ -4,28 +4,68 @@ elementFormDefault="qualified" attributeFormDefault="unqualified"> <xs:element name="dotNetOpenAuth"> + <xs:annotation> + <xs:documentation> + Customizations and configuration of DotNetOpenAuth behavior. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="messaging"> + <xs:annotation> + <xs:documentation> + Options for general messaging protocols, such as whitelist/blacklist hosts and maximum message age. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="untrustedWebRequest"> + <xs:annotation> + <xs:documentation> + Restrictions and settings to apply to outgoing HTTP requests to hosts that are not + trusted by this web site. Useful for OpenID-supporting hosts because HTTP connections + are initiated based on user input to arbitrary servers. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="whitelistHosts"> + <xs:annotation> + <xs:documentation> + A set of host names (including domain names) to allow outgoing connections to + that would otherwise not be allowed based on security restrictions. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="add"> <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The host name to trust. For example: "localhost" or "www.mypartners.com". + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="remove"> <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The host name to NOT trust. For example: "localhost" or "www.mypartners.com". + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="clear"> + <xs:annotation> + <xs:documentation> + Clears all hosts from the whitelist. + </xs:documentation> + </xs:annotation> <xs:complexType> <!--tag is empty--> </xs:complexType> @@ -55,19 +95,42 @@ </xs:complexType> </xs:element> <xs:element name="blacklistHosts"> + <xs:annotation> + <xs:documentation> + A set of host names (including domain names) to disallow outgoing connections to + that would otherwise be allowed based on security restrictions. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="add"> <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The host name known to add to the blacklist. For example: "localhost" or "www.mypartners.com". + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="remove"> <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The host name known to remove to the blacklist. For example: "localhost" or "www.mypartners.com". + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="clear"> + <xs:annotation> + <xs:documentation> + Clears all hosts from the blacklist. + </xs:documentation> + </xs:annotation> <xs:complexType> <!--tag is empty--> </xs:complexType> @@ -97,27 +160,92 @@ </xs:complexType> </xs:element> </xs:choice> - <xs:attribute name="timeout" type="xs:string" /> - <xs:attribute name="readWriteTimeout" type="xs:string" /> - <xs:attribute name="maximumBytesToRead" type="xs:int" /> - <xs:attribute name="maximumRedirections" type="xs:int" /> + <xs:attribute name="timeout" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum time to allow for an outgoing HTTP request to complete before giving up. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="readWriteTimeout" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum time to allow for an outgoing HTTP request to either send or receive data before giving up. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="maximumBytesToRead" type="xs:int"> + <xs:annotation> + <xs:documentation> + The maximum bytes to read from an untrusted server during an outgoing HTTP request before cutting off the response. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="maximumRedirections" type="xs:int"> + <xs:annotation> + <xs:documentation> + The maximum redirection instructions to follow before giving up. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> </xs:choice> - <xs:attribute name="lifetime" type="xs:string" /> - <xs:attribute name="clockSkew" type="xs:string" /> + <xs:attribute name="lifetime" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum time allowed between a message being sent to when it is received before + it is considered expired. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="clockSkew" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum time to consider a safe difference in server clocks. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="openid"> + <xs:annotation> + <xs:documentation> + Configuration for OpenID authentication (relying parties and providers). + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="relyingParty"> + <xs:annotation> + <xs:documentation> + Configuration specific for OpenID relying parties. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="security"> + <xs:annotation> + <xs:documentation> + Security settings that apply to OpenID relying parties. + </xs:documentation> + </xs:annotation> <xs:complexType> - <xs:attribute name="requireSsl" type="xs:boolean" default="false" /> + <xs:attribute name="requireSsl" type="xs:boolean" default="false"> + <xs:annotation> + <xs:documentation> + Restricts OpenID logins to identifiers that use HTTPS throughout the discovery process, + and only uses HTTPS OpenID Provider endpoints. + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attribute name="minimumRequiredOpenIdVersion"> + <xs:annotation> + <xs:documentation> + Optionally restricts interoperability with remote parties that + implement older versions of OpenID. + </xs:documentation> + </xs:annotation> <xs:simpleType> <xs:restriction base="xs:NMTOKEN"> <xs:enumeration value="V10" /> @@ -126,28 +254,103 @@ </xs:restriction> </xs:simpleType> </xs:attribute> - <xs:attribute name="minimumHashBitLength" type="xs:int" /> - <xs:attribute name="maximumHashBitLength" type="xs:int" /> - <xs:attribute name="privateSecretMaximumAge" type="xs:string" /> - <xs:attribute name="requireDirectedIdentity" type="xs:boolean" /> - <xs:attribute name="requireAssociation" type="xs:boolean" /> - <xs:attribute name="rejectUnsolicitedAssertions" type="xs:boolean" /> - <xs:attribute name="rejectDelegatingIdentifiers" type="xs:boolean" /> - <xs:attribute name="ignoreUnsignedExtensions" type="xs:boolean" /> + <xs:attribute name="minimumHashBitLength" type="xs:int"> + <xs:annotation> + <xs:documentation> + Shared associations with OpenID Providers will only be formed or used if they + are willing to form associations equal to or greater than a given level of protection. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="maximumHashBitLength" type="xs:int"> + <xs:annotation> + <xs:documentation> + Shared associaitons with OpenID Providers will only be formed or used if they + are willing to form associations equal to or less than a given level of protection. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="privateSecretMaximumAge" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum age of a secret used for private signing before it is renewed. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="requireDirectedIdentity" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Requires that OpenID identifiers upon which authentication requests are created + are to be OP Identifiers. Claimed Identifiers are not allowed. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="requireAssociation" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Requires that the relying party can form a shared association with an + OpenID Provider before creating an authentication request for it. + Note that this does not require that the Provider actually use a + shared association in its response. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="rejectUnsolicitedAssertions" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Requires that users begin their login experience at the relying party + rather than at a Provider or using other forms of unsolicited assertions. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="rejectDelegatingIdentifiers" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Requires that the claimed identifiers used to log into the relying party + be the same ones that are originally issued by the Provider. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="ignoreUnsignedExtensions" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Makes it impossible for the relying party to read authentication response + extensions that are not signed by the Provider. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="behaviors"> + <xs:annotation> + <xs:documentation> + Manipulates the set of custom behaviors that are automatically applied + to incoming and outgoing OpenID messages. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="add"> <xs:complexType> - <xs:attribute name="type" type="xs:string" use="optional" /> + <xs:attribute name="type" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the type that implements the IRelyingPartyBehavior interface. + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attribute name="xaml" type="xs:string" use="optional" /> </xs:complexType> </xs:element> <xs:element name="remove"> <xs:complexType> - <xs:attribute name="type" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the type that implements the IRelyingPartyBehavior interface. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="clear"> @@ -159,31 +362,76 @@ </xs:complexType> </xs:element> <xs:element name="store"> + <xs:annotation> + <xs:documentation> + A custom implementation of IRelyingPartyApplicationStore to use by default for new + instances of OpenIdRelyingParty. + </xs:documentation> + </xs:annotation> <xs:complexType> - <xs:attribute name="type" type="xs:string"/> + <xs:attribute name="type" type="xs:string"> + <xs:annotation> + <xs:documentation> + A fully-qualified type name of the custom implementation of IRelyingPartyApplicationStore. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> <xs:element name="provider"> + <xs:annotation> + <xs:documentation> + Configuration specific for OpenID providers. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="security"> + <xs:annotation> + <xs:documentation> + Security settings that apply to OpenID providers. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="associations"> + <xs:annotation> + <xs:documentation> + Sets maximum ages for shared associations of various strengths. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="add"> <xs:complexType> - <xs:attribute name="type" type="xs:string" use="required" /> - <xs:attribute name="lifetime" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The OpenID association type (i.e. HMAC-SHA1 or HMAC-SHA256) + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="lifetime" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The lifetime a shared association of this type will be used for. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="remove"> <xs:complexType> - <xs:attribute name="type" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The OpenID association type (i.e. HMAC-SHA1 or HMAC-SHA256) + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="clear"> @@ -195,27 +443,92 @@ </xs:complexType> </xs:element> </xs:choice> - <xs:attribute name="requireSsl" type="xs:boolean" default="false" /> - <xs:attribute name="protectDownlevelReplayAttacks" type="xs:boolean" /> + <xs:attribute name="requireSsl" type="xs:boolean" default="false"> + <xs:annotation> + <xs:documentation> + Requires that relying parties' realm URLs be protected by HTTPS, + ensuring that the RP discovery step is not vulnerable to DNS poisoning attacks. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="protectDownlevelReplayAttacks" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Provides automatic security protections to OpenID 1.x relying parties + so security is comparable to OpenID 2.0 relying parties. + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attribute name="unsolicitedAssertionVerification"> + <xs:annotation> + <xs:documentation> + The level of verification done on a claimed identifier before an unsolicited + assertion for that identifier is issued by this Provider. + </xs:documentation> + </xs:annotation> <xs:simpleType> <xs:restriction base="xs:NMTOKEN"> - <xs:enumeration value="RequireSuccess" /> - <xs:enumeration value="LogWarningOnFailure" /> - <xs:enumeration value="NeverVerify" /> + <xs:enumeration value="RequireSuccess"> + <xs:annotation> + <xs:documentation> + The claimed identifier being asserted must delegate to this Provider + and this must be verifiable by the Provider to send the assertion. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="LogWarningOnFailure"> + <xs:annotation> + <xs:documentation> + The claimed identifier being asserted is checked for delegation to this Provider + and an warning is logged, but the assertion is allowed to go through. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="NeverVerify"> + <xs:annotation> + <xs:documentation> + The claimed identifier being asserted is not checked to see that this Provider + has authority to assert its identity. + </xs:documentation> + </xs:annotation> + </xs:enumeration> </xs:restriction> </xs:simpleType> </xs:attribute> - <xs:attribute name="minimumHashBitLength" type="xs:int" /> - <xs:attribute name="maximumHashBitLength" type="xs:int" /> + <xs:attribute name="minimumHashBitLength" type="xs:int"> + <xs:annotation> + <xs:documentation> + The minimum shared association strength to form with relying parties. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="maximumHashBitLength" type="xs:int"> + <xs:annotation> + <xs:documentation> + The maximum shared association strength to form with relying parties. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="behaviors"> + <xs:annotation> + <xs:documentation> + Manipulates the set of custom behaviors that are automatically applied + to incoming and outgoing OpenID messages. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="add"> <xs:complexType> - <xs:attribute name="type" type="xs:string" use="optional" /> + <xs:attribute name="type" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the type that implements the IRelyingPartyBehavior interface. + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attribute name="xaml" type="xs:string" use="optional" /> </xs:complexType> </xs:element> @@ -233,25 +546,54 @@ </xs:complexType> </xs:element> <xs:element name="store"> + <xs:annotation> + <xs:documentation> + A custom implementation of IProviderApplicationStore to use by default for new + instances of OpenIdRelyingParty. + </xs:documentation> + </xs:annotation> <xs:complexType> - <xs:attribute name="type" type="xs:string"/> + <xs:attribute name="type" type="xs:string"> + <xs:annotation> + <xs:documentation> + A fully-qualified type name of the custom implementation of IProviderApplicationStore. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> <xs:element name="extensionFactories"> + <xs:annotation> + <xs:documentation> + Adjusts the list of known OpenID extensions via the registration of extension factories. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="add"> <xs:complexType> - <xs:attribute name="type" type="xs:string" use="optional" /> + <xs:attribute name="type" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the type that implements IOpenIdExtensionFactory. + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attribute name="xaml" type="xs:string" use="optional" /> </xs:complexType> </xs:element> <xs:element name="remove"> <xs:complexType> - <xs:attribute name="type" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the type that implements IOpenIdExtensionFactory. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="clear"> @@ -263,22 +605,69 @@ </xs:complexType> </xs:element> <xs:element name="xriResolver"> + <xs:annotation> + <xs:documentation> + Controls XRI resolution to XRDS documents. + </xs:documentation> + </xs:annotation> <xs:complexType> - <xs:attribute name="enabled" type="xs:boolean" /> - <xs:attribute name="proxy" type="xs:string" /> + <xs:attribute name="enabled" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Controls whether XRI identifiers are allowed at all. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="proxy" type="xs:string"> + <xs:annotation> + <xs:documentation> + The XRI proxy resolver to use for obtaining XRDS documents from an XRI. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> </xs:choice> - <xs:attribute name="maxAuthenticationTime" type="xs:string" /> + <xs:attribute name="maxAuthenticationTime" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum time a user can take at the Provider while logging in before a relying party considers + the authentication lost. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="cacheDiscovery" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Whether the results of identifier discovery should be cached for a short time to improve performance + on subsequent requests, at the potential risk of reading stale data. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="oauth"> + <xs:annotation> + <xs:documentation> + Settings for OAuth consumers and service providers. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="consumer"> + <xs:annotation> + <xs:documentation> + Settings applicable to OAuth Consumers. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="security"> + <xs:annotation> + <xs:documentation> + Security settings applicable to OAuth Consumers. + </xs:documentation> + </xs:annotation> <xs:complexType> </xs:complexType> @@ -287,24 +676,70 @@ </xs:complexType> </xs:element> <xs:element name="serviceProvider"> + <xs:annotation> + <xs:documentation> + Settings applicable to OAuth Service Providers. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="security"> + <xs:annotation> + <xs:documentation> + Security settings applicable to OAuth Service Providers. + </xs:documentation> + </xs:annotation> <xs:complexType> <xs:attribute name="minimumRequiredOAuthVersion" default="V10"> + <xs:annotation> + <xs:documentation> + Optionally restricts interoperability with OAuth consumers that implement + older versions of OAuth. + </xs:documentation> + </xs:annotation> <xs:simpleType> <xs:restriction base="xs:NMTOKEN"> - <xs:enumeration value="V10" /> - <xs:enumeration value="V10a" /> + <xs:enumeration value="V10"> + <xs:annotation> + <xs:documentation> + The initial version of OAuth, now known to be vulnerable to certain social engineering attacks. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="V10a"> + <xs:annotation> + <xs:documentation> + The OAuth version that protects against social engineering attacks by introducing + the oauth_verifier parameter. + </xs:documentation> + </xs:annotation> + </xs:enumeration> </xs:restriction> </xs:simpleType> </xs:attribute> - <xs:attribute name="maxAuthorizationTime" type="xs:string" default="0:05" /> + <xs:attribute name="maxAuthorizationTime" type="xs:string" default="0:05"> + <xs:annotation> + <xs:documentation> + The maximum time allowed for users to authorize a consumer before request tokens expire. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="store"> + <xs:annotation> + <xs:documentation> + Sets the custom type that implements the INonceStore interface to use for nonce checking. + </xs:documentation> + </xs:annotation> <xs:complexType> - <xs:attribute name="type" type="xs:string"/> + <xs:attribute name="type" type="xs:string"> + <xs:annotation> + <xs:documentation> + A fully-qualified type name of the custom implementation of INonceStore. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> </xs:choice> diff --git a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs index 58a8276..385185a 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs @@ -104,10 +104,10 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> - /// Gets or sets the registered OpenID extensions. + /// Gets or sets the registered OpenID extension factories. /// </summary> [ConfigurationProperty(ExtensionFactoriesElementName, IsDefaultCollection = false)] - [ConfigurationCollection(typeof(TypeConfigurationCollection<IOpenIdMessageExtension>))] + [ConfigurationCollection(typeof(TypeConfigurationCollection<IOpenIdExtensionFactory>))] internal TypeConfigurationCollection<IOpenIdExtensionFactory> ExtensionFactories { get { return (TypeConfigurationCollection<IOpenIdExtensionFactory>)this[ExtensionFactoriesElementName] ?? new TypeConfigurationCollection<IOpenIdExtensionFactory>(); } set { this[ExtensionFactoriesElementName] = value; } diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs index d0e0d05..56daae9 100644 --- a/src/DotNetOpenAuth/GlobalSuppressions.cs +++ b/src/DotNetOpenAuth/GlobalSuppressions.cs @@ -48,3 +48,4 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "icam", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "idmanagement", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "no-pii", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly")] diff --git a/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs b/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs index 1511e2d..b91b82f 100644 --- a/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs +++ b/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.InfoCard { /// </summary> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] [ContractInvariantMethod] - protected void ObjectInvariant() { + private void ObjectInvariant() { Contract.Invariant(this.Token != null); } #endif diff --git a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs index f3722d7..d78329f 100644 --- a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs +++ b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs @@ -83,7 +83,7 @@ namespace DotNetOpenAuth.InfoCard { /// </summary> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] [ContractInvariantMethod] - protected void ObjectInvariant() { + private void ObjectInvariant() { Contract.Invariant(this.TokenXml != null); Contract.Invariant(this.DecryptingTokens != null); } diff --git a/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs b/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs index 1038ad7..15330e9 100644 --- a/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs +++ b/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs @@ -150,7 +150,7 @@ namespace DotNetOpenAuth.InfoCard { /// </summary> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] [ContractInvariantMethod] - protected void ObjectInvariant() { + private void ObjectInvariant() { Contract.Invariant(this.Tokens != null); } #endif diff --git a/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs b/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs index 1132ac0..fa72c98 100644 --- a/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs +++ b/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs @@ -40,7 +40,7 @@ namespace DotNetOpenAuth.InfoCard { /// </summary> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] [ContractInvariantMethod] - protected void ObjectInvariant() { + private void ObjectInvariant() { Contract.Invariant(this.TokenXml != null); Contract.Invariant(this.Exception != null); } diff --git a/src/DotNetOpenAuth/Loggers/ILog.cs b/src/DotNetOpenAuth/Loggers/ILog.cs index 4ddbd49..8094296 100644 --- a/src/DotNetOpenAuth/Loggers/ILog.cs +++ b/src/DotNetOpenAuth/Loggers/ILog.cs @@ -21,7 +21,7 @@ // This interface is designed to look like log4net's ILog interface. // We have this as a facade in front of it to avoid crashing if the // hosting web site chooses not to deploy log4net.dll along with -// dotnetopenid.dll. +// DotNetOpenAuth.dll. namespace DotNetOpenAuth.Loggers { diff --git a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs index 22c947a..0418d21 100644 --- a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs +++ b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Messaging { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Linq; /// <summary> @@ -153,6 +154,7 @@ namespace DotNetOpenAuth.Messaging { /// <exception cref="T:System.NotSupportedException"> /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. /// </exception> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void Add(KeyValuePair<TKey, TValue> item) { throw new NotSupportedException(); } diff --git a/src/DotNetOpenAuth/Messaging/EmptyList.cs b/src/DotNetOpenAuth/Messaging/EmptyList.cs index b418623..afe2c89 100644 --- a/src/DotNetOpenAuth/Messaging/EmptyList.cs +++ b/src/DotNetOpenAuth/Messaging/EmptyList.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Messaging { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; /// <summary> /// An empty, read-only list. @@ -98,6 +99,7 @@ namespace DotNetOpenAuth.Messaging { /// <exception cref="T:System.NotSupportedException"> /// The <see cref="T:System.Collections.Generic.IList`1"/> is read-only. /// </exception> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void RemoveAt(int index) { throw new ArgumentOutOfRangeException("index"); } @@ -113,6 +115,7 @@ namespace DotNetOpenAuth.Messaging { /// <exception cref="T:System.NotSupportedException"> /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. /// </exception> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void Add(T item) { throw new NotSupportedException(); } diff --git a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs index d343410..b64491b 100644 --- a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs +++ b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs @@ -136,7 +136,7 @@ namespace DotNetOpenAuth.Messaging { private int cachePosition = -1; /// <summary> - /// Initializes a new instance of the <see cref="EnumerableCache<T>.EnumeratorCache"/> class. + /// Initializes a new instance of the <see cref="EnumeratorCache"/> class. /// </summary> /// <param name="parent">The parent cached enumerable whose GetEnumerator method is calling this constructor.</param> internal EnumeratorCache(EnumerableCache<T> parent) { diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs index 5acd589..09edc01 100644 --- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs +++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs @@ -294,28 +294,24 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Gets the query or form data from the original request (before any URL rewriting has occurred.) - /// </summary> - /// <returns>A set of name=value pairs.</returns> - [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call")] - internal NameValueCollection GetQueryOrFormFromContext() { - NameValueCollection query; - if (this.HttpMethod == "GET") { - query = this.QueryStringBeforeRewriting; - } else { - query = this.Form; - } - return query; - } - - /// <summary> /// Gets the public facing URL for the given incoming HTTP request. /// </summary> /// <param name="request">The request.</param> - /// <returns>The URI that the outside world used to create this request.</returns> - private static Uri GetPublicFacingUrl(HttpRequest request) { + /// <param name="serverVariables">The server variables to consider part of the request.</param> + /// <returns> + /// The URI that the outside world used to create this request. + /// </returns> + /// <remarks> + /// Although the <paramref name="serverVariables"/> value can be obtained from + /// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them + /// in so we can simulate injected values from our unit tests since the actual property + /// is a read-only kind of <see cref="NameValueCollection"/>. + /// </remarks> + internal static Uri GetPublicFacingUrl(HttpRequest request, NameValueCollection serverVariables) { Contract.Requires(request != null); + Contract.Requires(serverVariables != null); ErrorUtilities.VerifyArgumentNotNull(request, "request"); + ErrorUtilities.VerifyArgumentNotNull(serverVariables, "serverVariables"); // Due to URL rewriting, cloud computing (i.e. Azure) // and web farms, etc., we have to be VERY careful about what @@ -325,15 +321,14 @@ namespace DotNetOpenAuth.Messaging { // So we use a variable that (at least from what I can tell) gives us // the public URL: #if !Mono // In ASP.NET MVC, Mono adds UrlRouting.axd to the URL here, which breaks OpenID return_to verification. - if (request.ServerVariables["HTTP_HOST"] != null) { + if (serverVariables["HTTP_HOST"] != null) { ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols."); + string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme; + Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]); UriBuilder publicRequestUri = new UriBuilder(request.Url); - Uri hostAndPort = new Uri(request.Url.Scheme + Uri.SchemeDelimiter + request.ServerVariables["HTTP_HOST"]); + publicRequestUri.Scheme = scheme; publicRequestUri.Host = hostAndPort.Host; publicRequestUri.Port = hostAndPort.Port; - if (request.ServerVariables["HTTP_X_FORWARDED_PROTO"] != null) { - publicRequestUri.Scheme = request.ServerVariables["HTTP_X_FORWARDED_PROTO"]; - } return publicRequestUri.Uri; } else { #endif @@ -352,6 +347,33 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Gets the query or form data from the original request (before any URL rewriting has occurred.) + /// </summary> + /// <returns>A set of name=value pairs.</returns> + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call")] + internal NameValueCollection GetQueryOrFormFromContext() { + NameValueCollection query; + if (this.HttpMethod == "GET") { + query = this.QueryStringBeforeRewriting; + } else { + query = this.Form; + } + return query; + } + + /// <summary> + /// Gets the public facing URL for the given incoming HTTP request. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>The URI that the outside world used to create this request.</returns> + private static Uri GetPublicFacingUrl(HttpRequest request) { + Contract.Requires(request != null); + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + + return GetPublicFacingUrl(request, request.ServerVariables); + } + + /// <summary> /// Makes up a reasonable guess at the raw URL from the possibly rewritten URL. /// </summary> /// <param name="url">A full URL.</param> diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs index 380e2d5..0f88247 100644 --- a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs @@ -78,7 +78,7 @@ namespace DotNetOpenAuth.Messaging { /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a /// <see cref="ProtocolException"/> to abstract away the transport and provide /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> - /// value, if set, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> IncomingWebResponse GetResponse(HttpWebRequest request); @@ -94,7 +94,7 @@ namespace DotNetOpenAuth.Messaging { /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a /// <see cref="ProtocolException"/> to abstract away the transport and provide /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> - /// value, if set, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options); } diff --git a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs index 1aafc11..63b639b 100644 --- a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs +++ b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs @@ -126,7 +126,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] [ContractInvariantMethod] - protected void ObjectInvariant() { + private void ObjectInvariant() { Contract.Invariant(this.messageType != null); } #endif diff --git a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs index 147cd66..c013fae 100644 --- a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs +++ b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs @@ -120,7 +120,7 @@ namespace DotNetOpenAuth.Messaging { /// Automatically sends the appropriate response to the user agent /// and ends execution on the current page or handler. /// </summary> - /// <exception cref="ThreadAbortException">Thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception> + /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception> /// <remarks> /// Requires a current HttpContext. /// </remarks> @@ -137,7 +137,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="context">The context of the HTTP request whose response should be set. /// Typically this is <see cref="HttpContext.Current"/>.</param> - /// <exception cref="ThreadAbortException">Thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception> + /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception> public virtual void Send(HttpContext context) { Contract.Requires(context != null); ErrorUtilities.VerifyArgumentNotNull(context, "context"); diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs index cadce44..8979693 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs @@ -136,7 +136,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <param name="keys">The names of all parameters included in a message.</param> /// <exception cref="ProtocolException">Thrown when required parts of a message are not in <paramref name="keys"/></exception> private void EnsureRequiredMessagePartsArePresent(IEnumerable<string> keys) { - var missingKeys = (from part in Mapping.Values + var missingKeys = (from part in this.Mapping.Values where part.IsRequired && !keys.Contains(part.Name) select part.Name).ToArray(); if (missingKeys.Length > 0) { @@ -155,7 +155,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <param name="partValues">A dictionary of key/value pairs that make up the serialized message.</param> private void EnsureRequiredProtocolMessagePartsAreNotEmpty(IDictionary<string, string> partValues) { string value; - var emptyValuedKeys = (from part in Mapping.Values + var emptyValuedKeys = (from part in this.Mapping.Values where !part.AllowEmpty && partValues.TryGetValue(part.Name, out value) && value != null && value.Length == 0 select part.Name).ToArray(); if (emptyValuedKeys.Length > 0) { diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs index 0b5b6d0..db5903e 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { using System.Collections; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; /// <summary> @@ -270,6 +271,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// Sets a named value in the message. /// </summary> /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void Add(KeyValuePair<string, string> item) { this.Add(item.Key, item.Value); } diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index f4bc3fe..26c0526 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -114,9 +114,25 @@ namespace DotNetOpenAuth.Messaging.Reflection { 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, + str => str != null ? underlyingMapping.StringToValue(str) : null); + } else { + this.converter = new ValueMapping( + obj => obj != null ? obj.ToString() : null, + str => str != null ? Convert.ChangeType(str, underlyingType, CultureInfo.InvariantCulture) : null); + } + } else { + this.converter = new ValueMapping( + obj => obj != null ? obj.ToString() : null, + str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null); + } } } else { this.converter = new ValueMapping(GetEncoder(attribute.Encoder)); @@ -239,7 +255,7 @@ 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> diff --git a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs index cc991cd..0ed0b5c 100644 --- a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs @@ -95,7 +95,7 @@ namespace DotNetOpenAuth.Messaging { /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a /// <see cref="ProtocolException"/> to abstract away the transport and provide /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> - /// value, if set, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> public IncomingWebResponse GetResponse(HttpWebRequest request) { return this.GetResponse(request, DirectWebRequestOptions.None); @@ -115,7 +115,7 @@ namespace DotNetOpenAuth.Messaging { /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a /// <see cref="ProtocolException"/> to abstract away the transport and provide /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> - /// value, if set, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); diff --git a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs index 1656155..f68ac73 100644 --- a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs @@ -230,7 +230,7 @@ namespace DotNetOpenAuth.Messaging { /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a /// <see cref="ProtocolException"/> to abstract away the transport and provide /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> - /// value, if set, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")] public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) { @@ -299,7 +299,7 @@ namespace DotNetOpenAuth.Messaging { /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a /// <see cref="ProtocolException"/> to abstract away the transport and provide /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> - /// value, if set, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) { return this.GetResponse(request, DirectWebRequestOptions.None); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index d325825..a4be672 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -139,6 +139,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { } } } + + fields.Remove("realm"); // ignore the realm parameter, since we don't use it, and it must be omitted from signature base string. } // Scrape the entity diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs index 55b40ac..bc78c63 100644 --- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs +++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs @@ -208,7 +208,7 @@ namespace DotNetOpenAuth.OAuth { // Fine-tune our understanding of the SP's supported OAuth version if it's wrong. if (this.ServiceProvider.Version != requestTokenResponse.Version) { - Logger.OAuth.WarnFormat("Expected OAuth service provider at endpoint {0} to use OAuth {1} but {2} was detected. Adjusting service description to new version.", this.ServiceProvider.RequestTokenEndpoint, this.ServiceProvider.Version, requestTokenResponse.Version); + Logger.OAuth.WarnFormat("Expected OAuth service provider at endpoint {0} to use OAuth {1} but {2} was detected. Adjusting service description to new version.", this.ServiceProvider.RequestTokenEndpoint.Location, this.ServiceProvider.Version, requestTokenResponse.Version); this.ServiceProvider.ProtocolVersion = Protocol.Lookup(requestTokenResponse.Version).ProtocolVersion; } diff --git a/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs b/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs index 13633b4..7a20fd5 100644 --- a/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs +++ b/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs @@ -20,12 +20,22 @@ namespace DotNetOpenAuth.OpenId { /// </remarks> internal class AssociationMemoryStore<TKey> : IAssociationStore<TKey> { /// <summary> + /// How many association store requests should occur between each spring cleaning. + /// </summary> + private const int PeriodicCleaningFrequency = 10; + + /// <summary> /// For Relying Parties, this maps OP Endpoints to a set of associations with that endpoint. /// For Providers, this keeps smart and dumb associations in two distinct pools. /// </summary> private Dictionary<TKey, Associations> serverAssocsTable = new Dictionary<TKey, Associations>(); /// <summary> + /// A counter to track how close we are to an expired association cleaning run. + /// </summary> + private int periodicCleaning; + + /// <summary> /// Stores a given association for later recall. /// </summary> /// <param name="distinguishingFactor">The distinguishing factor, either an OP Endpoint or smart/dumb mode.</param> @@ -38,6 +48,13 @@ namespace DotNetOpenAuth.OpenId { Associations server_assocs = this.serverAssocsTable[distinguishingFactor]; server_assocs.Set(association); + + unchecked { + this.periodicCleaning++; + } + if (this.periodicCleaning % PeriodicCleaningFrequency == 0) { + this.ClearExpiredAssociations(); + } } } @@ -88,17 +105,6 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Clears all expired associations from the store. - /// </summary> - public void ClearExpiredAssociations() { - lock (this) { - foreach (Associations assocs in this.serverAssocsTable.Values) { - assocs.ClearExpired(); - } - } - } - - /// <summary> /// Gets the server associations for a given OP Endpoint or dumb/smart mode. /// </summary> /// <param name="distinguishingFactor">The distinguishing factor, either an OP Endpoint (for relying parties) or smart/dumb (for providers).</param> @@ -112,5 +118,16 @@ namespace DotNetOpenAuth.OpenId { return this.serverAssocsTable[distinguishingFactor]; } } + + /// <summary> + /// Clears all expired associations from the store. + /// </summary> + private void ClearExpiredAssociations() { + lock (this) { + foreach (Associations assocs in this.serverAssocsTable.Values) { + assocs.ClearExpired(); + } + } + } } } diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs index d7dca9a..01b74a1 100644 --- a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs +++ b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs @@ -23,7 +23,7 @@ namespace DotNetOpenAuth.OpenId.Behaviors { /// to the originally requested extension and format. /// </summary> [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sreg", Justification = "Abbreviation")] - public class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior { + public sealed class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior { /// <summary> /// Initializes static members of the <see cref="AXFetchAsSregTransform"/> class. /// </summary> @@ -65,7 +65,10 @@ namespace DotNetOpenAuth.OpenId.Behaviors { /// without malfunctioning. /// </remarks> void IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(RelyingParty.IAuthenticationRequest request) { - request.SpreadSregToAX(AXFormats); + // Don't create AX extensions for OpenID 1.x messages, since AX requires OpenID 2.0. + if (request.Provider.Version.Major >= 2) { + request.SpreadSregToAX(AXFormats); + } } /// <summary> @@ -112,12 +115,7 @@ namespace DotNetOpenAuth.OpenId.Behaviors { bool IProviderBehavior.OnIncomingRequest(IRequest request) { var extensionRequest = request as Provider.HostProcessedRequest; if (extensionRequest != null) { - if (extensionRequest.GetExtension<ClaimsRequest>() == null) { - ClaimsRequest sreg = extensionRequest.UnifyExtensionsAsSreg(); - if (sreg != null) { - ((IProtocolMessageWithExtensions)extensionRequest.RequestMessage).Extensions.Add(sreg); - } - } + extensionRequest.UnifyExtensionsAsSreg(); } return false; diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs index 9c4c46d..6dbc455 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs @@ -209,7 +209,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } /// <summary> - /// A special DotNetOpenId-only nonce used by the RP when talking to 1.0 OPs in order + /// A special DotNetOpenAuth-only nonce used by the RP when talking to 1.0 OPs in order /// to protect against replay attacks. /// </summary> private class CustomNonce { diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs index e508233..42393f5 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs @@ -14,6 +14,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// the Attribute Exchange extension. /// </summary> [Serializable] + [DebuggerDisplay("{TypeUri} (required: {IsRequired}) ({Count})")] public class AttributeRequest { /// <summary> /// Backing field for the <see cref="Count"/> property. diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs index e87e188..9047b68 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { using System; using System.Collections.Generic; + using System.Diagnostics; using DotNetOpenAuth.Messaging; /// <summary> @@ -15,6 +16,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// a fetch request, or by a relying party as part of a store request. /// </summary> [Serializable] + [DebuggerDisplay("{TypeUri}")] public class AttributeValues { /// <summary> /// Initializes a new instance of the <see cref="AttributeValues"/> class. diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs index a69e226..124a18c 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { using System; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using DotNetOpenAuth.Messaging; @@ -67,7 +68,10 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// <value>A collection where the keys are the attribute type URIs, and the value /// is all the attribute request details.</value> public KeyedCollection<string, AttributeRequest> Attributes { - get { return this.attributes; } + get { + Contract.Ensures(Contract.Result<KeyedCollection<string, AttributeRequest>>() != null); + return this.attributes; + } } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs index 758b20c..14b1caa 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { using System; using System.Collections.ObjectModel; + using System.Diagnostics.Contracts; using System.Linq; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; @@ -52,7 +53,10 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// Gets a sequence of the attributes whose values are provided by the OpenID Provider. /// </summary> public KeyedCollection<string, AttributeValues> Attributes { - get { return this.attributesProvided; } + get { + Contract.Ensures(Contract.Result<KeyedCollection<string, AttributeValues>>() != null); + return this.attributesProvided; + } } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs index 36358a7..8cfe0b7 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs @@ -132,7 +132,8 @@ namespace DotNetOpenAuth.OpenId.Extensions { /// <summary> /// Looks for Simple Registration and Attribute Exchange (all known formats) - /// request extensions and returns them as a Simple Registration extension. + /// request extensions and returns them as a Simple Registration extension, + /// and adds the new extension to the original request message if it was absent. /// </summary> /// <param name="request">The authentication request.</param> /// <returns> @@ -143,7 +144,7 @@ namespace DotNetOpenAuth.OpenId.Extensions { Contract.Requires(request != null); ErrorUtilities.VerifyArgumentNotNull(request, "request"); - var req = (Provider.AuthenticationRequest)request; + var req = (Provider.HostProcessedRequest)request; var sreg = req.GetExtension<ClaimsRequest>(); if (sreg != null) { return sreg; @@ -151,7 +152,7 @@ namespace DotNetOpenAuth.OpenId.Extensions { var ax = req.GetExtension<FetchRequest>(); if (ax != null) { - sreg = new ClaimsRequest(); + sreg = new ClaimsRequest(SimpleRegistration.Constants.sreg_ns); sreg.Synthesized = true; ((IProtocolMessageWithExtensions)req.RequestMessage).Extensions.Add(sreg); sreg.BirthDate = GetDemandLevelFor(ax, WellKnownAttributes.BirthDate.WholeBirthDate); @@ -179,9 +180,9 @@ namespace DotNetOpenAuth.OpenId.Extensions { /// </remarks> internal static void ConvertSregToMatchRequest(this Provider.IHostProcessedRequest request) { var req = (Provider.HostProcessedRequest)request; - var response = (IProtocolMessageWithExtensions)req.Response; + var response = req.Response as IProtocolMessageWithExtensions; // negative responses don't support extensions. var sregRequest = request.GetExtension<ClaimsRequest>(); - if (sregRequest != null) { + if (sregRequest != null && response != null) { if (sregRequest.Synthesized) { var axRequest = request.GetExtension<FetchRequest>(); ErrorUtilities.VerifyInternal(axRequest != null, "How do we have a synthesized Sreg request without an AX request?"); diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs index 0a3147a..3031aad 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs @@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { /// <remarks> /// <para>One using this enum should review the following publication for details /// before asserting or interpreting what these levels signify, notwithstanding - /// the brief summaries attached to each level in DotNetOpenId documentation. + /// the brief summaries attached to each level in DotNetOpenAuth documentation. /// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf</para> /// <para> /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels. diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs index bee675d..ae483a6 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs @@ -31,6 +31,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI { /// <see cref="IProviderEndpoint.IsExtensionSupported"/> method on the <see cref="DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationRequest.Provider"/> /// object.</para> /// </remarks> + [Serializable] public sealed class UIRequest : IOpenIdMessageExtension, IMessageWithEvents { /// <summary> /// The factory method that may be used in deserialization of this message. @@ -71,6 +72,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI { /// <remarks> /// The user's preferred languages as a [BCP 47] language priority list, represented as a comma-separated list of BCP 47 basic language ranges in descending priority order. For instance, the value "fr-CA,fr-FR,en-CA" represents the preference for French spoken in Canada, French spoken in France, followed by English spoken in Canada. /// </remarks> + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design.")] [MessagePart("lang", AllowEmpty = false)] public CultureInfo[] LanguagePreference { get; set; } diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs index 4c31100..36a874d 100644 --- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs +++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs @@ -136,7 +136,7 @@ namespace DotNetOpenAuth.OpenId { /// Creates a new association of a given type. /// </summary> /// <param name="protocol">The protocol.</param> - /// <param name="associationType">Type of the association.</param> + /// <param name="associationType">Type of the association (i.e. HMAC-SHA1 or HMAC-SHA256)</param> /// <param name="associationUse">A value indicating whether the new association will be used privately by the Provider for "dumb mode" authentication /// or shared with the Relying Party for "smart mode" authentication.</param> /// <param name="securitySettings">The security settings of the Provider.</param> diff --git a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs index 2376b0d..a3c5305 100644 --- a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs +++ b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs @@ -33,6 +33,12 @@ namespace DotNetOpenAuth.OpenId { /// <see cref="System.Uri"/> for consumers (to distinguish associations across servers) or /// <see cref="AssociationRelyingPartyType"/> for providers (to distinguish dumb and smart client associations). /// </typeparam> + /// <remarks> + /// Expired associations should be periodically cleared out of an association store. + /// This should be done frequently enough to avoid a memory leak, but sparingly enough + /// to not be a performance drain. Because this balance can vary by host, it is the + /// responsibility of the host to initiate this cleaning. + /// </remarks> public interface IAssociationStore<TKey> { /// <summary> /// Saves an <see cref="Association"/> for later recall. @@ -80,16 +86,5 @@ namespace DotNetOpenAuth.OpenId { /// before this call. /// </remarks> bool RemoveAssociation(TKey distinguishingFactor, string handle); - - /// <summary> - /// Clears all expired associations from the store. - /// </summary> - /// <remarks> - /// If another algorithm is in place to periodically clear out expired associations, - /// this method call may be ignored. - /// This should be done frequently enough to avoid a memory leak, but sparingly enough - /// to not be a performance drain. - /// </remarks> - void ClearExpiredAssociations(); } } diff --git a/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs index 399a84f..e5e89bd 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs @@ -220,7 +220,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </summary> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] [ContractInvariantMethod] - protected void ObjectInvariant() { + private void ObjectInvariant() { Contract.Invariant(this.Hasher != null); Contract.Invariant(this.Encoder != null); Contract.Invariant(this.BaseIdentifier != null); diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs index 49d18e0..a87b0f6 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Provider { using System; using System.Collections.Generic; using System.ComponentModel; + using System.Diagnostics.Contracts; using System.Text; using System.Web; using System.Web.UI; @@ -42,7 +43,12 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <summary> /// Backing field for the <see cref="Provider"/> property. /// </summary> - private static OpenIdProvider provider = CreateProvider(); + private static OpenIdProvider provider; + + /// <summary> + /// The lock that must be obtained when initializing the provider field. + /// </summary> + private static object providerInitializerLock = new object(); /// <summary> /// Fired when an incoming OpenID request is an authentication challenge @@ -63,6 +69,15 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <value>The default value is an <see cref="OpenIdProvider"/> instance initialized according to the web.config file.</value> public static OpenIdProvider Provider { get { + Contract.Ensures(Contract.Result<OpenIdProvider>() != null); + if (provider == null) { + lock (providerInitializerLock) { + if (provider == null) { + provider = CreateProvider(); + } + } + } + return provider; } @@ -82,8 +97,14 @@ namespace DotNetOpenAuth.OpenId.Provider { /// before responding to the relying party's authentication request. /// </remarks> public static IAuthenticationRequest PendingAuthenticationRequest { - get { return HttpContext.Current.Session[PendingRequestKey] as IAuthenticationRequest; } - set { HttpContext.Current.Session[PendingRequestKey] = value; } + get { + Contract.Ensures(Contract.Result<IAuthenticationRequest>() == null || PendingRequest != null); + return HttpContext.Current.Session[PendingRequestKey] as IAuthenticationRequest; + } + + set { + HttpContext.Current.Session[PendingRequestKey] = value; + } } /// <summary> @@ -96,8 +117,14 @@ namespace DotNetOpenAuth.OpenId.Provider { /// before responding to the relying party's request. /// </remarks> public static IAnonymousRequest PendingAnonymousRequest { - get { return HttpContext.Current.Session[PendingRequestKey] as IAnonymousRequest; } - set { HttpContext.Current.Session[PendingRequestKey] = value; } + get { + Contract.Ensures(Contract.Result<IAnonymousRequest>() == null || PendingRequest != null); + return HttpContext.Current.Session[PendingRequestKey] as IAnonymousRequest; + } + + set { + HttpContext.Current.Session[PendingRequestKey] = value; + } } /// <summary> @@ -158,7 +185,7 @@ namespace DotNetOpenAuth.OpenId.Provider { // Then try the configuration file specified one. Finally, use the default // in-memory one that's built into OpenIdProvider. // determine what incoming message was received - IRequest request = provider.GetRequest(); + IRequest request = Provider.GetRequest(); if (request != null) { PendingRequest = null; @@ -178,7 +205,7 @@ namespace DotNetOpenAuth.OpenId.Provider { } } if (request.IsResponseReady) { - provider.SendResponse(request); + Provider.SendResponse(request); Page.Response.End(); PendingAuthenticationRequest = null; } @@ -217,6 +244,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </summary> /// <returns>The new instance of OpenIdProvider.</returns> private static OpenIdProvider CreateProvider() { + Contract.Ensures(Contract.Result<OpenIdProvider>() != null); return new OpenIdProvider(DotNetOpenAuthSection.Configuration.OpenId.Provider.ApplicationStore.CreateInstance(OpenIdProvider.HttpApplicationStore)); } } diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs index 9590033..d5fa4a9 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Provider { using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; + using System.Diagnostics.CodeAnalysis; using System.Linq; using DotNetOpenAuth.Messaging; @@ -51,6 +52,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// The behavior a Provider takes when verifying that it is authoritative for an /// identifier it is about to send an unsolicited assertion for. /// </summary> + [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "By design")] public enum UnsolicitedAssertionVerificationLevel { /// <summary> /// Always verify that the Provider is authoritative for an identifier before diff --git a/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs index 7085e72..4fa2d64 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs @@ -100,19 +100,6 @@ namespace DotNetOpenAuth.OpenId.Provider { return this.associationStore.RemoveAssociation(distinguishingFactor, handle); } - /// <summary> - /// Clears all expired associations from the store. - /// </summary> - /// <remarks> - /// If another algorithm is in place to periodically clear out expired associations, - /// this method call may be ignored. - /// This should be done frequently enough to avoid a memory leak, but sparingly enough - /// to not be a performance drain. - /// </remarks> - public void ClearExpiredAssociations() { - this.associationStore.ClearExpiredAssociations(); - } - #endregion #region INonceStore Members @@ -122,7 +109,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </summary> /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param> /// <param name="nonce">A series of random characters.</param> - /// <param name="timestamp">The timestamp that together with the nonce string make it unique. + /// <param name="timestampUtc">The timestamp that together with the nonce string make it unique. /// The timestamp may also be used by the data store to clear out old nonces.</param> /// <returns> /// True if the nonce+timestamp (combination) was not previously in the database. @@ -135,8 +122,8 @@ namespace DotNetOpenAuth.OpenId.Provider { /// is retrieved or set using the /// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property. /// </remarks> - public bool StoreNonce(string context, string nonce, DateTime timestamp) { - return this.nonceStore.StoreNonce(context, nonce, timestamp); + public bool StoreNonce(string context, string nonce, DateTime timestampUtc) { + return this.nonceStore.StoreNonce(context, nonce, timestampUtc); } #endregion diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs index 85c0096..9af947a 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Collections.Generic; using System.Linq; using System.Net; + using System.Security; using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.ChannelElements; @@ -147,10 +148,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return null; } - var associateRequest = AssociateRequest.Create(this.securitySettings, provider); - - const int RenegotiateRetries = 1; - return this.CreateNewAssociation(provider, associateRequest, RenegotiateRetries); + try { + var associateRequest = AssociateRequest.Create(this.securitySettings, provider); + + const int RenegotiateRetries = 1; + return this.CreateNewAssociation(provider, associateRequest, RenegotiateRetries); + } catch (VerificationException ex) { + // See Trac ticket #163. In partial trust host environments, the + // Diffie-Hellman implementation we're using for HTTP OP endpoints + // 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); + return null; + } } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs index cea7d21..30ecfc9 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs @@ -4,6 +4,8 @@ // </copyright> //----------------------------------------------------------------------- +using System.Threading; + namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Collections.Generic; @@ -247,6 +249,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// This method requires an ASP.NET HttpContext. /// </remarks> + /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception> public void RedirectToProvider() { this.RedirectingResponse.Send(); } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs index 6a4413f..5d7df71 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs @@ -5,7 +5,6 @@ //----------------------------------------------------------------------- [assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedScriptResourceName, "text/javascript")] -[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedDotNetOpenIdLogoResourceName, "image/gif")] [assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName, "image/gif")] [assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName, "image/png")] [assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName, "image/png")] @@ -45,11 +44,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { internal const string EmbeddedScriptResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdAjaxTextBox.js"; /// <summary> - /// The name of the manifest stream containing the dotnetopenid_16x16.gif file. - /// </summary> - internal const string EmbeddedDotNetOpenIdLogoResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.dotnetopenid_16x16.gif"; - - /// <summary> /// The name of the manifest stream containing the spinner.gif file. /// </summary> internal const string EmbeddedSpinnerResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.spinner.gif"; @@ -1191,9 +1185,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } startupScript.AppendFormat( CultureInfo.InvariantCulture, - "initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, function({18}, {19}, {20}) {{{21}}});{22}", + "initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, function({17}, {18}, {19}) {{{20}}});{21}", MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), OpenIdTextBox.EmbeddedLogoResourceName)), - MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedDotNetOpenIdLogoResourceName)), MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedSpinnerResourceName)), MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginSuccessResourceName)), MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginFailureResourceName)), diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js index 1078003..ec600b7 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js @@ -38,7 +38,7 @@ Array.prototype.remove = function(element) { } }; -function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url, success_icon_url, failure_icon_url, +function initAjaxOpenId(box, openid_logo_url, spinner_url, success_icon_url, failure_icon_url, throttle, timeout, assertionReceivedCode, loginButtonText, loginButtonToolTip, retryButtonText, retryButtonToolTip, busyToolTip, identifierRequiredMessage, loginInProgressMessage, @@ -221,8 +221,6 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url box.dnoi_internal.success_icon = box.dnoi_internal.constructIcon(success_icon_url, authenticatedAsToolTip, true); //box.dnoi_internal.failure_icon = box.dnoi_internal.constructIcon(failure_icon_url, authenticationFailedToolTip, true); - // Disable the display of the DotNetOpenId logo - //box.dnoi_internal.dnoi_logo = box.dnoi_internal.constructIcon(dotnetopenid_logo_url); box.dnoi_internal.dnoi_logo = box.dnoi_internal.openid_logo; box.dnoi_internal.setVisualCue = function(state, authenticatedBy, authenticatedAs) { diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index 1fe6521..ff17410 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -100,7 +100,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // replay attacks. But only 2.0+ Providers can be expected to provide // replay protection. if (nonceStore == null) { - this.SecuritySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; + 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; + } } this.channel = new OpenIdChannel(associationStore, nonceStore, this.SecuritySettings); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs index 8499178..fdb6931 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs @@ -84,19 +84,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return this.associationStore.RemoveAssociation(distinguishingFactor, handle); } - /// <summary> - /// Clears all expired associations from the store. - /// </summary> - /// <remarks> - /// If another algorithm is in place to periodically clear out expired associations, - /// this method call may be ignored. - /// This should be done frequently enough to avoid a memory leak, but sparingly enough - /// to not be a performance drain. - /// </remarks> - public void ClearExpiredAssociations() { - this.associationStore.ClearExpiredAssociations(); - } - #endregion #region INonceStore Members @@ -106,7 +93,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param> /// <param name="nonce">A series of random characters.</param> - /// <param name="timestamp">The timestamp that together with the nonce string make it unique. + /// <param name="timestampUtc">The timestamp that together with the nonce string make it unique. /// The timestamp may also be used by the data store to clear out old nonces.</param> /// <returns> /// True if the nonce+timestamp (combination) was not previously in the database. @@ -119,8 +106,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// is retrieved or set using the /// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property. /// </remarks> - public bool StoreNonce(string context, string nonce, DateTime timestamp) { - return this.nonceStore.StoreNonce(context, nonce, timestamp); + public bool StoreNonce(string context, string nonce, DateTime timestampUtc) { + return this.nonceStore.StoreNonce(context, nonce, timestampUtc); } #endregion diff --git a/src/DotNetOpenAuth/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth/Properties/AssemblyInfo.cs index 69d4dc4..0ffd104 100644 --- a/src/DotNetOpenAuth/Properties/AssemblyInfo.cs +++ b/src/DotNetOpenAuth/Properties/AssemblyInfo.cs @@ -4,18 +4,6 @@ // </copyright> //----------------------------------------------------------------------- -// Uncomment this line to build a partially trusted assembly. -// This has some security bonuses in that if there was a way to -// hijack this assembly to do something it is not designed to do, -// it will fail before doing much damage. -// But a partially trusted assembly's events, handled by the hosting -// web site, will also be under the partial trust restriction. -// Also note that http://support.microsoft.com/kb/839300 states a -// strong-name signed assembly must use AllowPartiallyTrustedCallers -// to be called from a web page, but defining PARTIAL_TRUST below also -// accomplishes this. -////#define PARTIAL_TRUST - // We DON'T put an AssemblyVersionAttribute in here because it is generated in the build. using System; @@ -69,33 +57,3 @@ using System.Web.UI; #else [assembly: InternalsVisibleTo("DotNetOpenAuth.Test")] #endif - -// Specify what permissions are required and optional for the assembly. -// In order for CAS to remove unnecessary privileges from this assembly (which is desirable -// for security), we need at least one RequestMinimum and at least one RequestOptional. -// These permissions were determined using PermCalc.exe - -// We need to be allowed to execute code. Besides, it gives a good baseline RequestMinimum permission. -[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)] - -// Allows the consumer to call out to the web server. This is unnecessary in provider-only scenarios. -// Note: we don't use a single demand for https?://.* because the regex pattern must exactly -// match the one used by hosting providers. Listing them individually seems to be more common. -[assembly: WebPermission(SecurityAction.RequestMinimum, ConnectPattern = @"http://.*")] -[assembly: WebPermission(SecurityAction.RequestMinimum, ConnectPattern = @"https://.*")] - -#if PARTIAL_TRUST -// Allows hosting this assembly in an ASP.NET setting. Not all applications -// will host this using ASP.NET, so this is optional. Besides, we need at least -// one optional permission to activate CAS permission shrinking. -[assembly: AspNetHostingPermission(SecurityAction.RequestOptional, Level = AspNetHostingPermissionLevel.Medium)] - -// The following are only required for diagnostic logging (Trace.Write, Debug.Assert, etc.). -#if TRACE || DEBUG -[assembly: KeyContainerPermission(SecurityAction.RequestOptional, Unrestricted = true)] -[assembly: ReflectionPermission(SecurityAction.RequestOptional, MemberAccess = true)] -[assembly: RegistryPermission(SecurityAction.RequestOptional, Unrestricted = true)] -[assembly: SecurityPermission(SecurityAction.RequestOptional, ControlEvidence = true, UnmanagedCode = true, ControlThread = true)] -[assembly: FileIOPermission(SecurityAction.RequestOptional, AllFiles = FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read)] -#endif -#endif diff --git a/tools/DotNetOpenAuth.Versioning.targets b/tools/DotNetOpenAuth.Versioning.targets index f4b12b0..9ba295d 100644 --- a/tools/DotNetOpenAuth.Versioning.targets +++ b/tools/DotNetOpenAuth.Versioning.targets @@ -5,16 +5,26 @@ <PropertyGroup> <ProjectRoot Condition="'$(ProjectRoot)' == ''">$(MSBuildProjectDirectory)\..\..</ProjectRoot> <VersionCsFile>$(ProjectRoot)\obj\$(Configuration)\$(AssemblyName).Version.cs</VersionCsFile> + <NoWarn>$(NoWarn);1607</NoWarn> </PropertyGroup> <Import Project="$(ProjectRoot)\lib\DotNetOpenAuth.BuildTasks.targets" /> <UsingTask AssemblyFile="$(ProjectRoot)\lib\MSBuild.Community.Tasks.dll" TaskName="AssemblyInfo"/> <Target Name="GetBuildVersion"> - <GetBuildVersion VersionFile="$(ProjectRoot)\src\version.txt" Condition=" '$(BuildVersion)' == '' "> + <GetBuildVersion Condition=" '$(BuildVersion)' == '' " + VersionFile="$(ProjectRoot)\src\version.txt" + GitRepoRoot="$(ProjectRoot)\"> <Output TaskParameter="Version" PropertyName="BuildVersion" /> + <Output TaskParameter="GitCommitId" PropertyName="AssemblyInformationalVersion" /> </GetBuildVersion> - <Message Text="Building version $(BuildVersion)"/> + <PropertyGroup> + <!-- In TeamCity, the build agent doesn't get the .git directory, but the commit id is available by other means. --> + <AssemblyInformationalVersion Condition=" '$(AssemblyInformationalVersion)' == '' ">$(BUILD_VCS_NUMBER)</AssemblyInformationalVersion> + </PropertyGroup> + <Warning Condition=" '$(AssemblyInformationalVersion)' == '' " Text="Unable to determine the git HEAD commit ID to use for informational version number." /> + <Message Condition=" '$(AssemblyInformationalVersion)' != '' " Text="Building version $(BuildVersion) from commit $(AssemblyInformationalVersion)"/> + <Message Condition=" '$(AssemblyInformationalVersion)' == '' " Text="Building version $(BuildVersion)"/> </Target> <Target Name="BeforeBuild" DependsOnTargets="GetBuildVersion"> @@ -22,7 +32,9 @@ <NewVersionCsFile>$(VersionCsFile).new</NewVersionCsFile> </PropertyGroup> <MakeDir Directories="$(ProjectRoot)\obj\$(Configuration)"/> - <AssemblyInfo OutputFile="$(NewVersionCsFile)" CodeLanguage="C#" AssemblyVersion="$(BuildVersion)" /> + <AssemblyInfo OutputFile="$(NewVersionCsFile)" CodeLanguage="C#" + AssemblyVersion="$(BuildVersion)" + AssemblyInformationalVersion="$(AssemblyInformationalVersion)" /> <!-- Avoid applying the newly generated AssemblyInfo.cs file to the build unless it has changed in order to allow for incremental building. --> <CompareFiles OriginalItems="$(VersionCsFile)" NewItems="$(NewVersionCsFile)"> diff --git a/tools/Publish.targets b/tools/Publish.targets index 036e751..a4d2bfa 100644 --- a/tools/Publish.targets +++ b/tools/Publish.targets @@ -67,6 +67,11 @@ <Copy SourceFiles="@(PublishableWebSampleSources)" DestinationFiles="@(PublishableWebSampleTargets)" SkipUnchangedFiles="true" /> </Target> + <Target Name="UnpublishSamples" + DependsOnTargets="DeleteSampleSitesOnIis" + Condition=" '$(SampleWebRoot)' != '' "> + </Target> + <Target Name="PrepareForPublishDocumentation" DependsOnTargets="Documentation"> <ItemGroup> <DocSources Include="$(ProjectRoot)\doc\api\**\*" /> @@ -84,6 +89,11 @@ <Copy SourceFiles="@(DocSources)" DestinationFiles="@(DocTargets)" SkipUnchangedFiles="true" /> </Target> + <Target Name="UnpublishDocumentation" + DependsOnTargets="DeleteDocumentationSiteOnIis" + Condition=" '$(DocWebRoot)' != '' "> + </Target> + <Target Name="CreateSampleSitesOnIis" DependsOnTargets="PrepareForIIS;PrepareForPublishSamples"> <Error Text="The PublishSamplesWebSiteName property must be set." Condition=" '$(PublishSamplesWebSiteName)' == '' "/> <Error Text="The SampleWebRoot property must be set." Condition=" '$(SampleWebRoot)' == '' " /> diff --git a/tools/sandcastle.targets b/tools/sandcastle.targets index 8103a21..d76698e 100644 --- a/tools/sandcastle.targets +++ b/tools/sandcastle.targets @@ -40,10 +40,31 @@ </ItemGroup> <Target Name="CleanDocumentation"> - <Delete Files="$(ReflectionFile);$(ManifestFile);$(ReflectionBaseFile);$(ChmFile)" TreatErrorsAsWarnings="true"/> - <RemoveDir Directories="$(DocOutputApiPath);$(DocIntermediatePath);$(ChmDir)" ContinueOnError="true"/> - <RemoveDir Directories="$(FxReflectionIntermediatePath)" ContinueOnError="true" /> + <ItemGroup> + <_DirtyFiles Include=" + $(DocOutputApiPath)\**; + $(ReflectionFile); + $(ManifestFile); + $(ReflectionBaseFile); + $(ChmFile) + " + Exclude=" + $(DocOutputApiPath)\Web.config; + $(DocOutputApiPath)\Default.aspx; + " /> + <_DirtyDirectories Include=" + $(DocIntermediatePath); + $(ChmDir); + $(FxReflectionIntermediatePath) + " /> + </ItemGroup> + <Delete Files="@(_DirtyFiles)" TreatErrorsAsWarnings="true"/> + <RemoveDir Directories="@(_DirtyDirectories)" ContinueOnError="true"/> <!--<RemoveDir Directories="$(FxReflectionOutputPath)" ContinueOnError="true" />--> + <ItemGroup> + <_DirtyFiles Remove="@(_DirtyFiles)" /> + <_DirtyDirectories Remove="@(_DirtyDirectories)" /> + </ItemGroup> </Target> <Target Name="CreateIntermediatePath"> |